1 use clippy_utils
::msrvs
::{self, Msrv}
;
2 use clippy_utils
::{diagnostics::span_lint_and_then, source}
;
3 use if_chain
::if_chain
;
4 use rustc_ast
::Mutability
;
5 use rustc_hir
::{Expr, ExprKind, Node}
;
6 use rustc_lint
::LateContext
;
7 use rustc_middle
::ty
::{self, layout::LayoutOf, Ty, TypeAndMut}
;
9 use super::CAST_SLICE_DIFFERENT_SIZES
;
11 pub(super) fn check
<'tcx
>(cx
: &LateContext
<'tcx
>, expr
: &Expr
<'tcx
>, msrv
: &Msrv
) {
12 // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist
13 if !msrv
.meets(msrvs
::PTR_SLICE_RAW_PARTS
) {
17 // if this cast is the child of another cast expression then don't emit something for it, the full
18 // chain will be analyzed
19 if is_child_of_cast(cx
, expr
) {
23 if let Some(CastChainInfo
{
27 }) = expr_cast_chain_tys(cx
, expr
)
29 if let (Ok(from_layout
), Ok(to_layout
)) = (cx
.layout_of(start_ty
.ty
), cx
.layout_of(end_ty
.ty
)) {
30 let from_size
= from_layout
.size
.bytes();
31 let to_size
= to_layout
.size
.bytes();
32 if from_size
!= to_size
&& from_size
!= 0 && to_size
!= 0 {
35 CAST_SLICE_DIFFERENT_SIZES
,
38 "casting between raw pointers to `[{}]` (element size {from_size}) and `[{}]` (element size {to_size}) does not adjust the count",
39 start_ty
.ty
, end_ty
.ty
,
42 let ptr_snippet
= source
::snippet(cx
, left_cast
.span
, "..");
44 let (mutbl_fn_str
, mutbl_ptr_str
) = match end_ty
.mutbl
{
45 Mutability
::Mut
=> ("_mut", "mut"),
46 Mutability
::Not
=> ("", "const"),
49 "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {}, ..)",
50 // get just the ty from the TypeAndMut so that the printed type isn't something like `mut
51 // T`, extract just the `T`
57 &format
!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"),
59 rustc_errors
::Applicability
::HasPlaceholders
,
68 fn is_child_of_cast(cx
: &LateContext
<'_
>, expr
: &Expr
<'_
>) -> bool
{
69 let map
= cx
.tcx
.hir();
71 if let Some(parent_id
) = map
.find_parent_node(expr
.hir_id
);
72 if let Some(parent
) = map
.find(parent_id
);
74 let expr
= match parent
{
75 Node
::Block(block
) => {
76 if let Some(parent_expr
) = block
.expr
{
82 Node
::Expr(expr
) => expr
,
86 matches
!(expr
.kind
, ExprKind
::Cast(..))
93 /// Returns the type T of the pointed to *const [T] or *mut [T] and the mutability of the slice if
94 /// the type is one of those slices
95 fn get_raw_slice_ty_mut(ty
: Ty
<'_
>) -> Option
<TypeAndMut
<'_
>> {
97 ty
::RawPtr(TypeAndMut { ty: slice_ty, mutbl }
) => match slice_ty
.kind() {
98 ty
::Slice(ty
) => Some(TypeAndMut { ty: *ty, mutbl: *mutbl }
),
105 struct CastChainInfo
<'tcx
> {
106 /// The left most part of the cast chain, or in other words, the first cast in the chain
107 /// Used for diagnostics
108 left_cast
: &'tcx Expr
<'tcx
>,
109 /// The starting type of the cast chain
110 start_ty
: TypeAndMut
<'tcx
>,
111 /// The final type of the cast chain
112 end_ty
: TypeAndMut
<'tcx
>,
115 /// Returns a `CastChainInfo` with the left-most cast in the chain and the original ptr T and final
116 /// ptr U if the expression is composed of casts.
117 /// Returns None if the expr is not a Cast
118 fn expr_cast_chain_tys
<'tcx
>(cx
: &LateContext
<'tcx
>, expr
: &Expr
<'tcx
>) -> Option
<CastChainInfo
<'tcx
>> {
119 if let ExprKind
::Cast(cast_expr
, _cast_to_hir_ty
) = expr
.peel_blocks().kind
{
120 let cast_to
= cx
.typeck_results().expr_ty(expr
);
121 let to_slice_ty
= get_raw_slice_ty_mut(cast_to
)?
;
123 // If the expression that makes up the source of this cast is itself a cast, recursively
124 // call `expr_cast_chain_tys` and update the end type with the final target type.
125 // Otherwise, this cast is not immediately nested, just construct the info for this cast
126 if let Some(prev_info
) = expr_cast_chain_tys(cx
, cast_expr
) {
132 let cast_from
= cx
.typeck_results().expr_ty(cast_expr
);
133 let from_slice_ty
= get_raw_slice_ty_mut(cast_from
)?
;
135 left_cast
: cast_expr
,
136 start_ty
: from_slice_ty
,