|
1 | 1 | use rustc_ast::Mutability;
|
2 |
| -use rustc_hir::{Expr, ExprKind, MutTy, TyKind, UnOp}; |
3 |
| -use rustc_middle::ty; |
4 |
| -use rustc_span::sym; |
| 2 | +use rustc_data_structures::fx::FxHashMap; |
| 3 | +use rustc_hir::{def::Res, Expr, ExprKind, HirId, Local, QPath, StmtKind, UnOp}; |
| 4 | +use rustc_middle::ty::{self, TypeAndMut}; |
| 5 | +use rustc_span::{sym, Span}; |
5 | 6 |
|
6 | 7 | use crate::{lints::InvalidReferenceCastingDiag, LateContext, LateLintPass, LintContext};
|
7 | 8 |
|
@@ -33,42 +34,74 @@ declare_lint! {
|
33 | 34 | "casts of `&T` to `&mut T` without interior mutability"
|
34 | 35 | }
|
35 | 36 |
|
36 |
| -declare_lint_pass!(InvalidReferenceCasting => [INVALID_REFERENCE_CASTING]); |
| 37 | +#[derive(Default)] |
| 38 | +pub struct InvalidReferenceCasting { |
| 39 | + casted: FxHashMap<HirId, Span>, |
| 40 | +} |
| 41 | + |
| 42 | +impl_lint_pass!(InvalidReferenceCasting => [INVALID_REFERENCE_CASTING]); |
37 | 43 |
|
38 | 44 | impl<'tcx> LateLintPass<'tcx> for InvalidReferenceCasting {
|
39 |
| - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { |
40 |
| - let ExprKind::Unary(UnOp::Deref, e) = &expr.kind else { |
| 45 | + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx rustc_hir::Stmt<'tcx>) { |
| 46 | + let StmtKind::Local(local) = stmt.kind else { |
41 | 47 | return;
|
42 | 48 | };
|
43 |
| - |
44 |
| - let e = e.peel_blocks(); |
45 |
| - let e = if let ExprKind::Cast(e, t) = e.kind |
46 |
| - && let TyKind::Ptr(MutTy { mutbl: Mutability::Mut, .. }) = t.kind { |
47 |
| - e |
48 |
| - } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind |
49 |
| - && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) |
50 |
| - && cx.tcx.is_diagnostic_item(sym::ptr_cast_mut, def_id) { |
51 |
| - expr |
52 |
| - } else { |
| 49 | + let Local { init: Some(init), els: None, .. } = local else { |
53 | 50 | return;
|
54 | 51 | };
|
55 | 52 |
|
56 |
| - let e = e.peel_blocks(); |
57 |
| - let e = if let ExprKind::Cast(e, t) = e.kind |
58 |
| - && let TyKind::Ptr(MutTy { mutbl: Mutability::Not, .. }) = t.kind { |
59 |
| - e |
60 |
| - } else if let ExprKind::Call(path, [arg]) = e.kind |
61 |
| - && let ExprKind::Path(ref qpath) = path.kind |
62 |
| - && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() |
63 |
| - && cx.tcx.is_diagnostic_item(sym::ptr_from_ref, def_id) { |
64 |
| - arg |
65 |
| - } else { |
| 53 | + if is_cast_from_const_to_mut(cx, init) { |
| 54 | + self.casted.insert(local.pat.hir_id, init.span); |
| 55 | + } |
| 56 | + } |
| 57 | + |
| 58 | + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) { |
| 59 | + let ExprKind::Unary(UnOp::Deref, e) = &expr.kind else { |
66 | 60 | return;
|
67 | 61 | };
|
68 | 62 |
|
69 |
| - let e = e.peel_blocks(); |
70 |
| - if let ty::Ref(..) = cx.typeck_results().node_type(e.hir_id).kind() { |
71 |
| - cx.emit_spanned_lint(INVALID_REFERENCE_CASTING, expr.span, InvalidReferenceCastingDiag); |
| 63 | + if is_cast_from_const_to_mut(cx, e) { |
| 64 | + cx.emit_spanned_lint(INVALID_REFERENCE_CASTING, expr.span, InvalidReferenceCastingDiag { orig_cast: None }); |
| 65 | + } else if let ExprKind::Path(QPath::Resolved(_, path)) = e.kind |
| 66 | + && let Res::Local(hir_id) = &path.res |
| 67 | + && let Some(orig_cast) = self.casted.get(hir_id) { |
| 68 | + cx.emit_spanned_lint(INVALID_REFERENCE_CASTING, expr.span, InvalidReferenceCastingDiag { orig_cast: Some(*orig_cast) }); |
72 | 69 | }
|
73 | 70 | }
|
74 | 71 | }
|
| 72 | + |
| 73 | +fn is_cast_from_const_to_mut<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool { |
| 74 | + let e = e.peel_blocks(); |
| 75 | + |
| 76 | + // <expr> as *mut ... |
| 77 | + let e = if let ExprKind::Cast(e, t) = e.kind |
| 78 | + && let ty::RawPtr(TypeAndMut { mutbl: Mutability::Mut, .. }) = cx.typeck_results().node_type(t.hir_id).kind() { |
| 79 | + e |
| 80 | + // <expr>.cast_mut() |
| 81 | + } else if let ExprKind::MethodCall(_, expr, [], _) = e.kind |
| 82 | + && let Some(def_id) = cx.typeck_results().type_dependent_def_id(e.hir_id) |
| 83 | + && cx.tcx.is_diagnostic_item(sym::ptr_cast_mut, def_id) { |
| 84 | + expr |
| 85 | + } else { |
| 86 | + return false; |
| 87 | + }; |
| 88 | + |
| 89 | + let e = e.peel_blocks(); |
| 90 | + |
| 91 | + // <expr> as *const ... |
| 92 | + let e = if let ExprKind::Cast(e, t) = e.kind |
| 93 | + && let ty::RawPtr(TypeAndMut { mutbl: Mutability::Not, .. }) = cx.typeck_results().node_type(t.hir_id).kind() { |
| 94 | + e |
| 95 | + // ptr::from_ref(<expr>) |
| 96 | + } else if let ExprKind::Call(path, [arg]) = e.kind |
| 97 | + && let ExprKind::Path(ref qpath) = path.kind |
| 98 | + && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id() |
| 99 | + && cx.tcx.is_diagnostic_item(sym::ptr_from_ref, def_id) { |
| 100 | + arg |
| 101 | + } else { |
| 102 | + return false; |
| 103 | + }; |
| 104 | + |
| 105 | + let e = e.peel_blocks(); |
| 106 | + matches!(cx.typeck_results().node_type(e.hir_id).kind(), ty::Ref(..)) |
| 107 | +} |
0 commit comments