|  | use rustc_hir as hir; | 
|  | use rustc_hir::{Expr, Stmt}; | 
|  | use rustc_middle::ty::{Mutability, TyKind}; | 
|  | use rustc_session::lint::FutureIncompatibilityReason; | 
|  | use rustc_session::{declare_lint, declare_lint_pass}; | 
|  | use rustc_span::edition::Edition; | 
|  | use rustc_span::{BytePos, Span}; | 
|  |  | 
|  | use crate::lints::{MutRefSugg, RefOfMutStatic}; | 
|  | use crate::{LateContext, LateLintPass, LintContext}; | 
|  |  | 
|  | declare_lint! { | 
|  | /// The `static_mut_refs` lint checks for shared or mutable references | 
|  | /// of mutable static inside `unsafe` blocks and `unsafe` functions. | 
|  | /// | 
|  | /// ### Example | 
|  | /// | 
|  | /// ```rust,edition2021 | 
|  | /// fn main() { | 
|  | ///     static mut X: i32 = 23; | 
|  | ///     static mut Y: i32 = 24; | 
|  | /// | 
|  | ///     unsafe { | 
|  | ///         let y = &X; | 
|  | ///         let ref x = X; | 
|  | ///         let (x, y) = (&X, &Y); | 
|  | ///         foo(&X); | 
|  | ///     } | 
|  | /// } | 
|  | /// | 
|  | /// unsafe fn _foo() { | 
|  | ///     static mut X: i32 = 23; | 
|  | ///     static mut Y: i32 = 24; | 
|  | /// | 
|  | ///     let y = &X; | 
|  | ///     let ref x = X; | 
|  | ///     let (x, y) = (&X, &Y); | 
|  | ///     foo(&X); | 
|  | /// } | 
|  | /// | 
|  | /// fn foo<'a>(_x: &'a i32) {} | 
|  | /// ``` | 
|  | /// | 
|  | /// {{produces}} | 
|  | /// | 
|  | /// ### Explanation | 
|  | /// | 
|  | /// Shared or mutable references of mutable static are almost always a mistake and | 
|  | /// can lead to undefined behavior and various other problems in your code. | 
|  | /// | 
|  | /// This lint is "warn" by default on editions up to 2021, in 2024 is "deny". | 
|  | pub STATIC_MUT_REFS, | 
|  | Warn, | 
|  | "creating a shared reference to mutable static", | 
|  | @future_incompatible = FutureIncompatibleInfo { | 
|  | reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024), | 
|  | reference: "<https://doc.rust-lang.org/edition-guide/rust-2024/static-mut-references.html>", | 
|  | explain_reason: false, | 
|  | }; | 
|  | @edition Edition2024 => Deny; | 
|  | } | 
|  |  | 
|  | declare_lint_pass!(StaticMutRefs => [STATIC_MUT_REFS]); | 
|  |  | 
|  | impl<'tcx> LateLintPass<'tcx> for StaticMutRefs { | 
|  | #[allow(rustc::usage_of_ty_tykind)] | 
|  | fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) { | 
|  | let err_span = expr.span; | 
|  | match expr.kind { | 
|  | hir::ExprKind::AddrOf(borrow_kind, m, ex) | 
|  | if matches!(borrow_kind, hir::BorrowKind::Ref) | 
|  | && let Some(err_span) = path_is_static_mut(ex, err_span) => | 
|  | { | 
|  | let source_map = cx.sess().source_map(); | 
|  | let snippet = source_map.span_to_snippet(err_span); | 
|  |  | 
|  | let sugg_span = if let Ok(snippet) = snippet { | 
|  | // ( ( &IDENT ) ) | 
|  | // ~~~~ exclude these from the suggestion span to avoid unmatching parens | 
|  | let exclude_n_bytes: u32 = snippet | 
|  | .chars() | 
|  | .take_while(|ch| ch.is_whitespace() || *ch == '(') | 
|  | .map(|ch| ch.len_utf8() as u32) | 
|  | .sum(); | 
|  |  | 
|  | err_span.with_lo(err_span.lo() + BytePos(exclude_n_bytes)).with_hi(ex.span.lo()) | 
|  | } else { | 
|  | err_span.with_hi(ex.span.lo()) | 
|  | }; | 
|  |  | 
|  | emit_static_mut_refs(cx, err_span, sugg_span, m, !expr.span.from_expansion()); | 
|  | } | 
|  | hir::ExprKind::MethodCall(_, e, _, _) | 
|  | if let Some(err_span) = path_is_static_mut(e, expr.span) | 
|  | && let typeck = cx.typeck_results() | 
|  | && let Some(method_def_id) = typeck.type_dependent_def_id(expr.hir_id) | 
|  | && let inputs = | 
|  | cx.tcx.fn_sig(method_def_id).skip_binder().inputs().skip_binder() | 
|  | && let Some(receiver) = inputs.get(0) | 
|  | && let TyKind::Ref(_, _, m) = receiver.kind() => | 
|  | { | 
|  | emit_static_mut_refs(cx, err_span, err_span.shrink_to_lo(), *m, false); | 
|  | } | 
|  | _ => {} | 
|  | } | 
|  | } | 
|  |  | 
|  | fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &Stmt<'_>) { | 
|  | if let hir::StmtKind::Let(loc) = stmt.kind | 
|  | && let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind | 
|  | && let hir::ByRef::Yes(m) = ba.0 | 
|  | && let Some(init) = loc.init | 
|  | && let Some(err_span) = path_is_static_mut(init, init.span) | 
|  | { | 
|  | emit_static_mut_refs(cx, err_span, err_span.shrink_to_lo(), m, false); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | fn path_is_static_mut(mut expr: &hir::Expr<'_>, mut err_span: Span) -> Option<Span> { | 
|  | if err_span.from_expansion() { | 
|  | err_span = expr.span; | 
|  | } | 
|  |  | 
|  | while let hir::ExprKind::Field(e, _) = expr.kind { | 
|  | expr = e; | 
|  | } | 
|  |  | 
|  | if let hir::ExprKind::Path(qpath) = expr.kind | 
|  | && let hir::QPath::Resolved(_, path) = qpath | 
|  | && let hir::def::Res::Def(def_kind, _) = path.res | 
|  | && let hir::def::DefKind::Static { safety: _, mutability: Mutability::Mut, nested: false } = | 
|  | def_kind | 
|  | { | 
|  | return Some(err_span); | 
|  | } | 
|  | None | 
|  | } | 
|  |  | 
|  | fn emit_static_mut_refs( | 
|  | cx: &LateContext<'_>, | 
|  | span: Span, | 
|  | sugg_span: Span, | 
|  | mutable: Mutability, | 
|  | suggest_addr_of: bool, | 
|  | ) { | 
|  | let (shared_label, shared_note, mut_note, sugg) = match mutable { | 
|  | Mutability::Mut => { | 
|  | let sugg = | 
|  | if suggest_addr_of { Some(MutRefSugg::Mut { span: sugg_span }) } else { None }; | 
|  | ("mutable ", false, true, sugg) | 
|  | } | 
|  | Mutability::Not => { | 
|  | let sugg = | 
|  | if suggest_addr_of { Some(MutRefSugg::Shared { span: sugg_span }) } else { None }; | 
|  | ("shared ", true, false, sugg) | 
|  | } | 
|  | }; | 
|  |  | 
|  | cx.emit_span_lint( | 
|  | STATIC_MUT_REFS, | 
|  | span, | 
|  | RefOfMutStatic { span, sugg, shared_label, shared_note, mut_note }, | 
|  | ); | 
|  | } |