rustc_lint/
static_mut_refs.rsuse 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! {
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) =>
{
let source_map = cx.sess().source_map();
let snippet = source_map.span_to_snippet(err_span);
let sugg_span = if let Ok(snippet) = snippet {
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,
});
}