]> git.lizzy.rs Git - rust.git/blob - clippy_lints/src/casts/cast_slice_different_sizes.rs
Auto merge of #8422 - buttercrab:only_used_in_recursion, r=llogiq
[rust.git] / clippy_lints / src / casts / cast_slice_different_sizes.rs
1 use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source::snippet_opt};
2 use if_chain::if_chain;
3 use rustc_ast::Mutability;
4 use rustc_hir::{Expr, ExprKind, Node};
5 use rustc_lint::LateContext;
6 use rustc_middle::ty::{self, layout::LayoutOf, Ty, TypeAndMut};
7 use rustc_semver::RustcVersion;
8
9 use super::CAST_SLICE_DIFFERENT_SIZES;
10
11 fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
12     let map = cx.tcx.hir();
13     if_chain! {
14         if let Some(parent_id) = map.find_parent_node(expr.hir_id);
15         if let Some(parent) = map.find(parent_id);
16         then {
17             let expr = match parent {
18                 Node::Block(block) => {
19                     if let Some(parent_expr) = block.expr {
20                         parent_expr
21                     } else {
22                         return false;
23                     }
24                 },
25                 Node::Expr(expr) => expr,
26                 _ => return false,
27             };
28
29             matches!(expr.kind, ExprKind::Cast(..))
30         } else {
31             false
32         }
33     }
34 }
35
36 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Option<RustcVersion>) {
37     // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist
38     if !meets_msrv(msrv.as_ref(), &msrvs::PTR_SLICE_RAW_PARTS) {
39         return;
40     }
41
42     // if this cast is the child of another cast expression then don't emit something for it, the full
43     // chain will be analyzed
44     if is_child_of_cast(cx, expr) {
45         return;
46     }
47
48     if let Some((from_slice_ty, to_slice_ty)) = expr_cast_chain_tys(cx, expr) {
49         if let (Ok(from_layout), Ok(to_layout)) = (cx.layout_of(from_slice_ty.ty), cx.layout_of(to_slice_ty.ty)) {
50             let from_size = from_layout.size.bytes();
51             let to_size = to_layout.size.bytes();
52             if from_size != to_size && from_size != 0 && to_size != 0 {
53                 span_lint_and_then(
54                     cx,
55                     CAST_SLICE_DIFFERENT_SIZES,
56                     expr.span,
57                     &format!(
58                         "casting between raw pointers to `[{}]` (element size {}) and `[{}]` (element size {}) does not adjust the count",
59                         from_slice_ty, from_size, to_slice_ty, to_size,
60                     ),
61                     |diag| {
62                         let cast_expr = match expr.kind {
63                             ExprKind::Cast(cast_expr, ..) => cast_expr,
64                             _ => unreachable!("expr should be a cast as checked by expr_cast_chain_tys"),
65                         };
66                         let ptr_snippet = snippet_opt(cx, cast_expr.span).unwrap();
67
68                         let (mutbl_fn_str, mutbl_ptr_str) = match to_slice_ty.mutbl {
69                             Mutability::Mut => ("_mut", "mut"),
70                             Mutability::Not => ("", "const"),
71                         };
72                         let sugg = format!(
73                             "core::ptr::slice_from_raw_parts{mutbl_fn_str}({ptr_snippet} as *{mutbl_ptr_str} {to_slice_ty}, ..)"
74                         );
75
76                         diag.span_suggestion(
77                             expr.span,
78                             &format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"),
79                             sugg,
80                             rustc_errors::Applicability::HasPlaceholders,
81                         );
82                     },
83                 );
84             }
85         }
86     }
87 }
88
89 /// Returns the type T of the pointed to *const [T] or *mut [T] and the mutability of the slice if
90 /// the type is one of those slices
91 fn get_raw_slice_ty_mut(ty: Ty<'_>) -> Option<TypeAndMut<'_>> {
92     match ty.kind() {
93         ty::RawPtr(TypeAndMut { ty: slice_ty, mutbl }) => match slice_ty.kind() {
94             ty::Slice(ty) => Some(TypeAndMut { ty: *ty, mutbl: *mutbl }),
95             _ => None,
96         },
97         _ => None,
98     }
99 }
100
101 /// Returns the pair (original ptr T, final ptr U) if the expression is composed of casts
102 /// Returns None if the expr is not a Cast
103 fn expr_cast_chain_tys<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<(TypeAndMut<'tcx>, TypeAndMut<'tcx>)> {
104     if let ExprKind::Cast(cast_expr, _cast_to_hir_ty) = expr.peel_blocks().kind {
105         let cast_to = cx.typeck_results().expr_ty(expr);
106         let to_slice_ty = get_raw_slice_ty_mut(cast_to)?;
107         if let Some((inner_from_ty, _inner_to_ty)) = expr_cast_chain_tys(cx, cast_expr) {
108             Some((inner_from_ty, to_slice_ty))
109         } else {
110             let cast_from = cx.typeck_results().expr_ty(cast_expr);
111             let from_slice_ty = get_raw_slice_ty_mut(cast_from)?;
112             Some((from_slice_ty, to_slice_ty))
113         }
114     } else {
115         None
116     }
117 }