about summary refs log tree commit diff
path: root/compiler/rustc_lint/src
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_lint/src')
-rw-r--r--compiler/rustc_lint/src/lib.rs3
-rw-r--r--compiler/rustc_lint/src/lints.rs32
-rw-r--r--compiler/rustc_lint/src/static_mut_refs.rs154
3 files changed, 189 insertions, 0 deletions
diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs
index e8229b72182..d1da2809cc7 100644
--- a/compiler/rustc_lint/src/lib.rs
+++ b/compiler/rustc_lint/src/lib.rs
@@ -81,6 +81,7 @@ mod ptr_nulls;
 mod redundant_semicolon;
 mod reference_casting;
 mod shadowed_into_iter;
+mod static_mut_refs;
 mod tail_expr_drop_order;
 mod traits;
 mod types;
@@ -120,6 +121,7 @@ use rustc_middle::query::Providers;
 use rustc_middle::ty::TyCtxt;
 use shadowed_into_iter::ShadowedIntoIter;
 pub use shadowed_into_iter::{ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER};
+use static_mut_refs::*;
 use tail_expr_drop_order::TailExprDropOrder;
 use traits::*;
 use types::*;
@@ -246,6 +248,7 @@ late_lint_methods!(
             ImplTraitOvercaptures: ImplTraitOvercaptures,
             TailExprDropOrder: TailExprDropOrder,
             IfLetRescope: IfLetRescope::default(),
+            StaticMutRefs: StaticMutRefs,
         ]
     ]
 );
diff --git a/compiler/rustc_lint/src/lints.rs b/compiler/rustc_lint/src/lints.rs
index 7fabfc9a784..11006862d05 100644
--- a/compiler/rustc_lint/src/lints.rs
+++ b/compiler/rustc_lint/src/lints.rs
@@ -3061,3 +3061,35 @@ pub(crate) struct UnsafeAttrOutsideUnsafeSuggestion {
 pub(crate) struct OutOfScopeMacroCalls {
     pub path: String,
 }
+
+#[derive(LintDiagnostic)]
+#[diag(lint_static_mut_refs_lint)]
+pub(crate) struct RefOfMutStatic<'a> {
+    #[label]
+    pub span: Span,
+    #[subdiagnostic]
+    pub sugg: Option<MutRefSugg>,
+    pub shared_label: &'a str,
+    #[note(lint_shared_note)]
+    pub shared_note: bool,
+    #[note(lint_mut_note)]
+    pub mut_note: bool,
+}
+
+#[derive(Subdiagnostic)]
+pub(crate) enum MutRefSugg {
+    #[multipart_suggestion(lint_suggestion, style = "verbose", applicability = "maybe-incorrect")]
+    Shared {
+        #[suggestion_part(code = "&raw const ")]
+        span: Span,
+    },
+    #[multipart_suggestion(
+        lint_suggestion_mut,
+        style = "verbose",
+        applicability = "maybe-incorrect"
+    )]
+    Mut {
+        #[suggestion_part(code = "&raw mut ")]
+        span: Span,
+    },
+}
diff --git a/compiler/rustc_lint/src/static_mut_refs.rs b/compiler/rustc_lint/src/static_mut_refs.rs
new file mode 100644
index 00000000000..3dd26fb9c53
--- /dev/null
+++ b/compiler/rustc_lint/src/static_mut_refs.rs
@@ -0,0 +1,154 @@
+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::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,
+    "shared references or mutable references of mutable static is discouraged",
+    @future_incompatible = FutureIncompatibleInfo {
+        reason: FutureIncompatibilityReason::EditionError(Edition::Edition2024),
+        reference: "<https://doc.rust-lang.org/nightly/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) =>
+            {
+                emit_static_mut_refs(
+                    cx,
+                    err_span,
+                    err_span.with_hi(ex.span.lo()),
+                    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 },
+    );
+}