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 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 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}