|
| 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 | +} |
0 commit comments