1use rustc_hir as hir;
2use rustc_hir::{Expr, Stmt};
3use rustc_middle::ty::{Mutability, TyKind};
4use rustc_session::lint::fcw;
5use rustc_session::{declare_lint, declare_lint_pass};
6use rustc_span::{BytePos, Span};
7
8use crate::lints::{MutRefSugg, RefOfMutStatic};
9use crate::{LateContext, LateLintPass, LintContext};
10
11#[doc =
r" The `static_mut_refs` lint checks for shared or mutable references"]
#[doc = r" of mutable static inside `unsafe` blocks and `unsafe` functions."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust,edition2021"]
#[doc = r" fn main() {"]
#[doc = r" static mut X: i32 = 23;"]
#[doc = r" static mut Y: i32 = 24;"]
#[doc = r""]
#[doc = r" unsafe {"]
#[doc = r" let y = &X;"]
#[doc = r" let ref x = X;"]
#[doc = r" let (x, y) = (&X, &Y);"]
#[doc = r" foo(&X);"]
#[doc = r" }"]
#[doc = r" }"]
#[doc = r""]
#[doc = r" unsafe fn _foo() {"]
#[doc = r" static mut X: i32 = 23;"]
#[doc = r" static mut Y: i32 = 24;"]
#[doc = r""]
#[doc = r" let y = &X;"]
#[doc = r" let ref x = X;"]
#[doc = r" let (x, y) = (&X, &Y);"]
#[doc = r" foo(&X);"]
#[doc = r" }"]
#[doc = r""]
#[doc = r" fn foo<'a>(_x: &'a i32) {}"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" Shared or mutable references of mutable static are almost always a mistake and"]
#[doc =
r" can lead to undefined behavior and various other problems in your code."]
#[doc = r""]
#[doc =
r#" This lint is "warn" by default on editions up to 2021, in 2024 is "deny"."#]
pub static STATIC_MUT_REFS: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "STATIC_MUT_REFS",
default_level: ::rustc_lint_defs::Warn,
desc: "creating a shared reference to mutable static",
is_externally_loaded: false,
future_incompatible: Some(::rustc_lint_defs::FutureIncompatibleInfo {
reason: ::rustc_lint_defs::FutureIncompatibilityReason::EditionError(::rustc_lint_defs::EditionFcw {
edition: rustc_span::edition::Edition::Edition2024,
page_slug: "static-mut-references",
}),
explain_reason: false,
..::rustc_lint_defs::FutureIncompatibleInfo::default_fields_for_macro()
}),
edition_lint_opts: Some((::rustc_lint_defs::Edition::Edition2024,
::rustc_lint_defs::Deny)),
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
12 pub STATIC_MUT_REFS,
52 Warn,
53 "creating a shared reference to mutable static",
54 @future_incompatible = FutureIncompatibleInfo {
55 reason: fcw!(EditionError 2024 "static-mut-references"),
56 explain_reason: false,
57 };
58 @edition Edition2024 => Deny;
59}
60
61pub struct StaticMutRefs;
#[automatically_derived]
impl ::core::marker::Copy for StaticMutRefs { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for StaticMutRefs { }
#[automatically_derived]
impl ::core::clone::Clone for StaticMutRefs {
#[inline]
fn clone(&self) -> StaticMutRefs { *self }
}
impl ::rustc_lint_defs::LintPass for StaticMutRefs {
fn name(&self) -> &'static str { "StaticMutRefs" }
fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
<[_]>::into_vec(::alloc::boxed::box_new([STATIC_MUT_REFS]))
}
}
impl StaticMutRefs {
#[allow(unused)]
pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
<[_]>::into_vec(::alloc::boxed::box_new([STATIC_MUT_REFS]))
}
}declare_lint_pass!(StaticMutRefs => [STATIC_MUT_REFS]);
62
63impl<'tcx> LateLintPass<'tcx> for StaticMutRefs {
64 #[allow(rustc::usage_of_ty_tykind)]
65 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
66 let err_span = expr.span;
67 match expr.kind {
68 hir::ExprKind::AddrOf(borrow_kind, m, ex)
69 if #[allow(non_exhaustive_omitted_patterns)] match borrow_kind {
hir::BorrowKind::Ref => true,
_ => false,
}matches!(borrow_kind, hir::BorrowKind::Ref)
70 && let Some(err_span) = path_is_static_mut(ex, err_span) =>
71 {
72 let source_map = cx.sess().source_map();
73 let snippet = source_map.span_to_snippet(err_span);
74
75 let sugg_span = if let Ok(snippet) = snippet {
76 let exclude_n_bytes: u32 = snippet
79 .chars()
80 .take_while(|ch| ch.is_whitespace() || *ch == '(')
81 .map(|ch| ch.len_utf8() as u32)
82 .sum();
83
84 err_span.with_lo(err_span.lo() + BytePos(exclude_n_bytes)).with_hi(ex.span.lo())
85 } else {
86 err_span.with_hi(ex.span.lo())
87 };
88
89 emit_static_mut_refs(cx, err_span, sugg_span, m, !expr.span.from_expansion());
90 }
91 hir::ExprKind::MethodCall(_, e, _, _)
92 if let Some(err_span) = path_is_static_mut(e, expr.span)
93 && let typeck = cx.typeck_results()
94 && let Some(method_def_id) = typeck.type_dependent_def_id(expr.hir_id)
95 && let inputs =
96 cx.tcx.fn_sig(method_def_id).skip_binder().inputs().skip_binder()
97 && let Some(receiver) = inputs.get(0)
98 && let TyKind::Ref(_, _, m) = receiver.kind() =>
99 {
100 emit_static_mut_refs(cx, err_span, err_span.shrink_to_lo(), *m, false);
101 }
102 _ => {}
103 }
104 }
105
106 fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'_>) {
107 if let hir::StmtKind::Let(loc) = stmt.kind
108 && let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind
109 && let hir::ByRef::Yes(_, m) = ba.0
110 && let Some(init) = loc.init
111 && let Some(err_span) = path_is_static_mut(init, init.span)
112 {
113 emit_static_mut_refs(cx, err_span, err_span.shrink_to_lo(), m, false);
114 }
115 }
116}
117
118fn path_is_static_mut(mut expr: &hir::Expr<'_>, mut err_span: Span) -> Option<Span> {
119 if err_span.from_expansion() {
120 err_span = expr.span;
121 }
122
123 while let hir::ExprKind::Field(e, _) = expr.kind {
124 expr = e;
125 }
126
127 if let hir::ExprKind::Path(qpath) = expr.kind
128 && let hir::QPath::Resolved(_, path) = qpath
129 && let hir::def::Res::Def(def_kind, _) = path.res
130 && let hir::def::DefKind::Static { safety: _, mutability: Mutability::Mut, nested: false } =
131 def_kind
132 {
133 return Some(err_span);
134 }
135 None
136}
137
138fn emit_static_mut_refs(
139 cx: &LateContext<'_>,
140 span: Span,
141 sugg_span: Span,
142 mutable: Mutability,
143 suggest_addr_of: bool,
144) {
145 let (shared_label, shared_note, mut_note, sugg) = match mutable {
146 Mutability::Mut => {
147 let sugg =
148 if suggest_addr_of { Some(MutRefSugg::Mut { span: sugg_span }) } else { None };
149 ("mutable ", false, true, sugg)
150 }
151 Mutability::Not => {
152 let sugg =
153 if suggest_addr_of { Some(MutRefSugg::Shared { span: sugg_span }) } else { None };
154 ("shared ", true, false, sugg)
155 }
156 };
157
158 cx.emit_span_lint(
159 STATIC_MUT_REFS,
160 span,
161 RefOfMutStatic { span, sugg, shared_label, shared_note, mut_note },
162 );
163}