|
| 1 | +use rustc_hir::{Expr, ExprKind}; |
| 2 | +use rustc_lint::LateContext; |
| 3 | +use rustc_middle::ty::Ty; |
| 4 | + |
| 5 | +pub fn check<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>, ty_into: Ty<'_>) { |
| 6 | + // Check this part: |
| 7 | + // ``` |
| 8 | + // &some_ref as *const _ as usize |
| 9 | + // ^^^^^^^^ |
| 10 | + // &some_ref as *const _ as *const u8 |
| 11 | + // ^^^^^^^^^^^^ |
| 12 | + // ``` |
| 13 | + if !ty_into.is_numeric() && !ty_into.is_unsafe_ptr() { |
| 14 | + return; |
| 15 | + } |
| 16 | + |
| 17 | + #[allow(clippy::items_after_statements)] // It is pretty reasonable to have it here? |
| 18 | + const EXPLICIT_DEREF: bool = true; // *mut and *const have to be explicity deref'd |
| 19 | + |
| 20 | + // In case of casting into recursive pointer type (e.g. *mut *mut u8), skip this check. |
| 21 | + // Technically `builtin_deref` should always return us here a type, however, |
| 22 | + // lets not risk a potential crash with unwrap(). |
| 23 | + if ty_into.is_unsafe_ptr() && ty_into.builtin_deref(EXPLICIT_DEREF).is_some_and(Ty::is_unsafe_ptr) { |
| 24 | + return; |
| 25 | + } |
| 26 | + |
| 27 | + // The type from MIR is already inferred, so we cannot check if it is "as *const _" |
| 28 | + // if !ty_from.is_unsafe_ptr() || |
| 29 | + // !ty_from.builtin_deref(true).is_some_and(Ty::is_ty_or_numeric_infer) { |
| 30 | + // return; |
| 31 | + // } |
| 32 | + |
| 33 | + // Get these parts of the expression: |
| 34 | + // ``` |
| 35 | + // &some_ref as *const _ as usize |
| 36 | + // ^^^^^^^^^ ^^^^^^^^^^^ |
| 37 | + // &some_ref as *const _ as *const u8 |
| 38 | + // ^^^^^^^^^ ^^^^^^^^^^^ |
| 39 | + // ``` |
| 40 | + let ExprKind::Cast(nested_cast_expr, nested_cast_ty) = cast_expr.kind else { |
| 41 | + return; |
| 42 | + }; |
| 43 | + |
| 44 | + // Check this part: |
| 45 | + // ``` |
| 46 | + // &some_ref as *const _ as usize |
| 47 | + // ^^^^^^^^ |
| 48 | + // &some_ref as *const _ as *const u8 |
| 49 | + // ^^^^^^^^ |
| 50 | + // ``` |
| 51 | + // Here we cannot use MIR data (like `cast_from` from function above) as it has already |
| 52 | + // the type inferred. We have to use the data from HIR. |
| 53 | + let rustc_hir::TyKind::Ptr(rustc_hir::MutTy { ty, .. }) = nested_cast_ty.kind else { |
| 54 | + return; |
| 55 | + }; |
| 56 | + if !matches!(ty.kind, rustc_hir::TyKind::Infer) { |
| 57 | + return; |
| 58 | + } |
| 59 | + |
| 60 | + // Get this part of the expression: |
| 61 | + // ``` |
| 62 | + // &some_ref as *const _ as usize |
| 63 | + // ^^^^^^^^ |
| 64 | + // &some_ref as *const _ as *const u8 |
| 65 | + // ^^^^^^^^ |
| 66 | + // ``` |
| 67 | + let ExprKind::AddrOf(_, _, some_ref) = nested_cast_expr.kind else { |
| 68 | + return; |
| 69 | + }; |
| 70 | + |
| 71 | + // Check if `some_ref` is indeed a reference. |
| 72 | + if !cx.typeck_results().expr_ty(some_ref).is_ref() { |
| 73 | + return; |
| 74 | + } |
| 75 | + |
| 76 | + clippy_utils::diagnostics::span_lint( |
| 77 | + cx, |
| 78 | + super::ACCIDENTAL_DOUBLE_REF_INTO_FLAT_ADDR_CAST, |
| 79 | + nested_cast_expr.span, |
| 80 | + "accidental cast", |
| 81 | + ); |
| 82 | +} |
0 commit comments