|
| 1 | +use crate::lints::UseOfStaticMut; |
| 2 | +use crate::{LateContext, LateLintPass, LintContext}; |
| 3 | +use rustc_ast::BorrowKind; |
| 4 | +use rustc_hir::{ |
| 5 | + def::{DefKind, Res::Def}, |
| 6 | + intravisit::FnKind, |
| 7 | + Block, BlockCheckMode, Body, ExprKind, FnDecl, QPath, Stmt, StmtKind, |
| 8 | + UnsafeSource::UserProvided, |
| 9 | + Unsafety, |
| 10 | +}; |
| 11 | +use rustc_span::{def_id::LocalDefId, Span}; |
| 12 | +use rustc_type_ir::Mutability; |
| 13 | + |
| 14 | +declare_lint! { |
| 15 | + /// The `static_mut_ref` lint checks for use of mutable static |
| 16 | + /// inside `unsafe` blocks and `unsafe` functions. |
| 17 | + /// |
| 18 | + /// ### Example |
| 19 | + /// |
| 20 | + /// ```rust,no_run,edition2024 |
| 21 | + /// fn main() { |
| 22 | + /// static mut X: i32 = 23; |
| 23 | + /// unsafe { |
| 24 | + /// let y = &X; |
| 25 | + /// } |
| 26 | + /// } |
| 27 | + /// |
| 28 | + /// unsafe fn foo() { |
| 29 | + /// static mut X: i32 = 23; |
| 30 | + /// let y = &X; |
| 31 | + /// } |
| 32 | + /// ``` |
| 33 | + /// |
| 34 | + /// {{produces}} |
| 35 | + /// |
| 36 | + /// ### Explanation |
| 37 | + /// |
| 38 | + /// Use of mutable static is almost always a mistake and can lead to |
| 39 | + /// undefined behavior and various other problems in your code. |
| 40 | + /// |
| 41 | + /// This lint is "warm" by default on editions up to 2021, from 2024 it is |
| 42 | + /// a hard error. |
| 43 | + pub STATIC_MUT_REF, |
| 44 | + Warn, |
| 45 | + "use of mutable static" |
| 46 | +} |
| 47 | + |
| 48 | +declare_lint_pass!(StaticMutRef => [STATIC_MUT_REF]); |
| 49 | + |
| 50 | +impl<'tcx> LateLintPass<'tcx> for StaticMutRef { |
| 51 | + fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) { |
| 52 | + if let BlockCheckMode::UnsafeBlock(src) = block.rules |
| 53 | + && matches!(src, UserProvided) |
| 54 | + { |
| 55 | + check_stmts(cx, block.stmts); |
| 56 | + } |
| 57 | + } |
| 58 | + |
| 59 | + fn check_fn( |
| 60 | + &mut self, |
| 61 | + cx: &LateContext<'tcx>, |
| 62 | + fn_kind: FnKind<'tcx>, |
| 63 | + _: &'tcx FnDecl<'tcx>, |
| 64 | + body: &'tcx Body<'tcx>, |
| 65 | + _: Span, |
| 66 | + _: LocalDefId, |
| 67 | + ) { |
| 68 | + if let FnKind::ItemFn(_, _, h) = fn_kind |
| 69 | + && matches!(h.unsafety, Unsafety::Unsafe) |
| 70 | + { |
| 71 | + if let ExprKind::Block(block, _) = body.value.kind { |
| 72 | + check_stmts(cx, block.stmts); |
| 73 | + } |
| 74 | + } |
| 75 | + } |
| 76 | +} |
| 77 | + |
| 78 | +fn check_stmts(cx: &LateContext<'_>, stmts: &[Stmt<'_>]) { |
| 79 | + for stmt in stmts.iter() { |
| 80 | + if let StmtKind::Local(loc) = stmt.kind |
| 81 | + && let Some(init) = loc.init |
| 82 | + && let ExprKind::AddrOf(borrow_kind, _, expr) = init.kind |
| 83 | + && matches!(borrow_kind, BorrowKind::Ref) |
| 84 | + && let ExprKind::Path(qpath) = expr.kind |
| 85 | + && let QPath::Resolved(_, path) = qpath |
| 86 | + && let Def(def_kind, _) = path.res |
| 87 | + && let DefKind::Static(mt) = def_kind |
| 88 | + && matches!(mt, Mutability::Mut) |
| 89 | + { |
| 90 | + let span = init.span; |
| 91 | + cx.emit_spanned_lint(STATIC_MUT_REF, span, UseOfStaticMut { span }); |
| 92 | + } |
| 93 | + } |
| 94 | +} |
0 commit comments