diff options
Diffstat (limited to 'compiler/rustc_lint/src')
| -rw-r--r-- | compiler/rustc_lint/src/lib.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/lints.rs | 32 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/static_mut_refs.rs | 154 |
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 }, + ); +} |
