]> git.lizzy.rs Git - rust.git/blob - src/tools/clippy/clippy_lints/src/casts/cast_slice_different_sizes.rs
Rollup merge of #102748 - cuviper:i586-gnu-uncompress, r=pietroalbini
[rust.git] / src / tools / clippy / clippy_lints / src / casts / cast_slice_different_sizes.rs
1 use clippy_utils::{diagnostics::span_lint_and_then, meets_msrv, msrvs, source};
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 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, msrv: Option<RustcVersion>) {
12     // suggestion is invalid if `ptr::slice_from_raw_parts` does not exist
13     if !meets_msrv(msrv, msrvs::PTR_SLICE_RAW_PARTS) {
14         return;
15     }
16
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) {
20         return;
21     }
22
23     if let Some(CastChainInfo {
24         left_cast,
25         start_ty,
26         end_ty,
27     }) = expr_cast_chain_tys(cx, expr)
28     {
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 {
33                 span_lint_and_then(
34                     cx,
35                     CAST_SLICE_DIFFERENT_SIZES,
36                     expr.span,
37                     &format!(
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,
40                     ),
41                     |diag| {
42                         let ptr_snippet = source::snippet(cx, left_cast.span, "..");
43
44                         let (mutbl_fn_str, mutbl_ptr_str) = match end_ty.mutbl {
45                             Mutability::Mut => ("_mut", "mut"),
46                             Mutability::Not => ("", "const"),
47                         };
48                         let sugg = format!(
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`
52                             end_ty.ty
53                         );
54
55                         diag.span_suggestion(
56                             expr.span,
57                             &format!("replace with `ptr::slice_from_raw_parts{mutbl_fn_str}`"),
58                             sugg,
59                             rustc_errors::Applicability::HasPlaceholders,
60                         );
61                     },
62                 );
63             }
64         }
65     }
66 }
67
68 fn is_child_of_cast(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
69     let map = cx.tcx.hir();
70     if_chain! {
71         if let Some(parent_id) = map.find_parent_node(expr.hir_id);
72         if let Some(parent) = map.find(parent_id);
73         then {
74             let expr = match parent {
75                 Node::Block(block) => {
76                     if let Some(parent_expr) = block.expr {
77                         parent_expr
78                     } else {
79                         return false;
80                     }
81                 },
82                 Node::Expr(expr) => expr,
83                 _ => return false,
84             };
85
86             matches!(expr.kind, ExprKind::Cast(..))
87         } else {
88             false
89         }
90     }
91 }
92
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<'_>> {
96     match ty.kind() {
97         ty::RawPtr(TypeAndMut { ty: slice_ty, mutbl }) => match slice_ty.kind() {
98             ty::Slice(ty) => Some(TypeAndMut { ty: *ty, mutbl: *mutbl }),
99             _ => None,
100         },
101         _ => None,
102     }
103 }
104
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>,
113 }
114
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)?;
122
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) {
127             Some(CastChainInfo {
128                 end_ty: to_slice_ty,
129                 ..prev_info
130             })
131         } else {
132             let cast_from = cx.typeck_results().expr_ty(cast_expr);
133             let from_slice_ty = get_raw_slice_ty_mut(cast_from)?;
134             Some(CastChainInfo {
135                 left_cast: cast_expr,
136                 start_ty: from_slice_ty,
137                 end_ty: to_slice_ty,
138             })
139         }
140     } else {
141         None
142     }
143 }