1 use rustc_ast::Mutability; 2 use rustc_hir::{Expr, ExprKind, MutTy, TyKind, UnOp}; 3 use rustc_middle::ty; 4 use rustc_span::sym; 5 6 use crate::{lints::InvalidReferenceCastingDiag, LateContext, LateLintPass, LintContext}; 7 8 declare_lint! { 9 /// The `invalid_reference_casting` lint checks for casts of `&T` to `&mut T` 10 /// without using interior mutability. 11 /// 12 /// ### Example 13 /// 14 /// ```rust,compile_fail 15 /// # #![deny(invalid_reference_casting)] 16 /// fn x(r: &i32) { 17 /// unsafe { 18 /// *(r as *const i32 as *mut i32) += 1; 19 /// } 20 /// } 21 /// ``` 22 /// 23 /// {{produces}} 24 /// 25 /// ### Explanation 26 /// 27 /// Casting `&T` to `&mut T` without using interior mutability is undefined behavior, 28 /// as it's a violation of Rust reference aliasing requirements. 29 /// 30 /// `UnsafeCell` is the only way to obtain aliasable data that is considered 31 /// mutable. 32 INVALID_REFERENCE_CASTING, 33 Allow, 34 "casts of `&T` to `&mut T` without interior mutability" 35 } 36 37 declare_lint_pass!(InvalidReferenceCasting => [INVALID_REFERENCE_CASTING]); 38 39 impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting { check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>)40 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { 41 let ExprKind::Unary(UnOp::Deref, e) = &expr.kind else { return; }; 42 43 let e = e.peel_blocks(); 44 let e = if let ExprKind::Cast(e, t) = e.kind 45 && let TyKind::Ptr(MutTy { mutbl: Mutability::Mut, .. }) = t.kind { 46 e 47 } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind 48 && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) 49 && cx.tcx.is_diagnostic_item(sym::ptr_cast_mut, def_id) { 50 expr 51 } else { 52 return; 53 }; 54 55 let e = e.peel_blocks(); 56 let e = if let ExprKind::Cast(e, t) = e.kind 57 && let TyKind::Ptr(MutTy { mutbl: Mutability::Not, .. }) = t.kind { 58 e 59 } else if let ExprKind::Call(path, [arg]) = e.kind 60 && let ExprKind::Path(ref qpath) = path.kind 61 && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() 62 && cx.tcx.is_diagnostic_item(sym::ptr_from_ref, def_id) { 63 arg 64 } else { 65 return; 66 }; 67 68 let e = e.peel_blocks(); 69 if let ty::Ref(..) = cx.typeck_results().node_type(e.hir_id).kind() { 70 cx.emit_spanned_lint(INVALID_REFERENCE_CASTING, expr.span, InvalidReferenceCastingDiag); 71 } 72 } 73 } 74