rustc_lint/
static_mut_refs.rs

1use rustc_hir as hir;
2use rustc_hir::{Expr, Stmt};
3use rustc_middle::ty::{Mutability, TyKind};
4use rustc_session::lint::FutureIncompatibilityReason;
5use rustc_session::{declare_lint, declare_lint_pass};
6use rustc_span::edition::Edition;
7use rustc_span::{BytePos, Span};
8
9use crate::lints::{MutRefSugg, RefOfMutStatic};
10use crate::{LateContext, LateLintPass, LintContext};
11
12declare_lint! {
13    /// The `static_mut_refs` lint checks for shared or mutable references
14    /// of mutable static inside `unsafe` blocks and `unsafe` functions.
15    ///
16    /// ### Example
17    ///
18    /// ```rust,edition2021
19    /// fn main() {
20    ///     static mut X: i32 = 23;
21    ///     static mut Y: i32 = 24;
22    ///
23    ///     unsafe {
24    ///         let y = &X;
25    ///         let ref x = X;
26    ///         let (x, y) = (&X, &Y);
27    ///         foo(&X);
28    ///     }
29    /// }
30    ///
31    /// unsafe fn _foo() {
32    ///     static mut X: i32 = 23;
33    ///     static mut Y: i32 = 24;
34    ///
35    ///     let y = &X;
36    ///     let ref x = X;
37    ///     let (x, y) = (&X, &Y);
38    ///     foo(&X);
39    /// }
40    ///
41    /// fn foo<'a>(_x: &'a i32) {}
42    /// ```
43    ///
44    /// {{produces}}
45    ///
46    /// ### Explanation
47    ///
48    /// Shared or mutable references of mutable static are almost always a mistake and
49    /// can lead to undefined behavior and various other problems in your code.
50    ///
51    /// This lint is "warn" by default on editions up to 2021, in 2024 is "deny".
52    pub STATIC_MUT_REFS,
53    Warn,
54    "shared references or mutable references of mutable static is discouraged",
55    @future_incompatible = FutureIncompatibleInfo {
56        reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
57        reference: "<https://doc.rust-lang.org/nightly/edition-guide/rust-2024/static-mut-references.html>",
58        explain_reason: false,
59    };
60    @edition Edition2024 => Deny;
61}
62
63declare_lint_pass!(StaticMutRefs => [STATIC_MUT_REFS]);
64
65impl<'tcx> LateLintPass<'tcx> for StaticMutRefs {
66    #[allow(rustc::usage_of_ty_tykind)]
67    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
68        let err_span = expr.span;
69        match expr.kind {
70            hir::ExprKind::AddrOf(borrow_kind, m, ex)
71                if matches!(borrow_kind, hir::BorrowKind::Ref)
72                    && let Some(err_span) = path_is_static_mut(ex, err_span) =>
73            {
74                let source_map = cx.sess().source_map();
75                let snippet = source_map.span_to_snippet(err_span);
76
77                let sugg_span = if let Ok(snippet) = snippet {
78                    // ( ( &IDENT ) )
79                    // ~~~~ exclude these from the suggestion span to avoid unmatching parens
80                    let exclude_n_bytes: u32 = snippet
81                        .chars()
82                        .take_while(|ch| ch.is_whitespace() || *ch == '(')
83                        .map(|ch| ch.len_utf8() as u32)
84                        .sum();
85
86                    err_span.with_lo(err_span.lo() + BytePos(exclude_n_bytes)).with_hi(ex.span.lo())
87                } else {
88                    err_span.with_hi(ex.span.lo())
89                };
90
91                emit_static_mut_refs(cx, err_span, sugg_span, m, !expr.span.from_expansion());
92            }
93            hir::ExprKind::MethodCall(_, e, _, _)
94                if let Some(err_span) = path_is_static_mut(e, expr.span)
95                    && let typeck = cx.typeck_results()
96                    && let Some(method_def_id) = typeck.type_dependent_def_id(expr.hir_id)
97                    && let inputs =
98                        cx.tcx.fn_sig(method_def_id).skip_binder().inputs().skip_binder()
99                    && let Some(receiver) = inputs.get(0)
100                    && let TyKind::Ref(_, _, m) = receiver.kind() =>
101            {
102                emit_static_mut_refs(cx, err_span, err_span.shrink_to_lo(), *m, false);
103            }
104            _ => {}
105        }
106    }
107
108    fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'_>) {
109        if let hir::StmtKind::Let(loc) = stmt.kind
110            && let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind
111            && let hir::ByRef::Yes(m) = ba.0
112            && let Some(init) = loc.init
113            && let Some(err_span) = path_is_static_mut(init, init.span)
114        {
115            emit_static_mut_refs(cx, err_span, err_span.shrink_to_lo(), m, false);
116        }
117    }
118}
119
120fn path_is_static_mut(mut expr: &hir::Expr<'_>, mut err_span: Span) -> Option<Span> {
121    if err_span.from_expansion() {
122        err_span = expr.span;
123    }
124
125    while let hir::ExprKind::Field(e, _) = expr.kind {
126        expr = e;
127    }
128
129    if let hir::ExprKind::Path(qpath) = expr.kind
130        && let hir::QPath::Resolved(_, path) = qpath
131        && let hir::def::Res::Def(def_kind, _) = path.res
132        && let hir::def::DefKind::Static { safety: _, mutability: Mutability::Mut, nested: false } =
133            def_kind
134    {
135        return Some(err_span);
136    }
137    None
138}
139
140fn emit_static_mut_refs(
141    cx: &LateContext<'_>,
142    span: Span,
143    sugg_span: Span,
144    mutable: Mutability,
145    suggest_addr_of: bool,
146) {
147    let (shared_label, shared_note, mut_note, sugg) = match mutable {
148        Mutability::Mut => {
149            let sugg =
150                if suggest_addr_of { Some(MutRefSugg::Mut { span: sugg_span }) } else { None };
151            ("mutable ", false, true, sugg)
152        }
153        Mutability::Not => {
154            let sugg =
155                if suggest_addr_of { Some(MutRefSugg::Shared { span: sugg_span }) } else { None };
156            ("shared ", true, false, sugg)
157        }
158    };
159
160    cx.emit_span_lint(
161        STATIC_MUT_REFS,
162        span,
163        RefOfMutStatic { span, sugg, shared_label, shared_note, mut_note },
164    );
165}