1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
use rustc_hir as hir;
use rustc_hir_pretty::qpath_to_string;
use rustc_lint_defs::builtin::STATIC_MUT_REFS;
use rustc_middle::ty::{Mutability, TyCtxt};
use rustc_span::Span;

use crate::errors;

/// Check for shared or mutable references of `static mut` inside expression
pub fn maybe_expr_static_mut(tcx: TyCtxt<'_>, expr: hir::Expr<'_>) {
    let span = expr.span;
    let hir_id = expr.hir_id;
    if let hir::ExprKind::AddrOf(borrow_kind, m, expr) = expr.kind
        && matches!(borrow_kind, hir::BorrowKind::Ref)
        && let Some(var) = is_path_static_mut(*expr)
    {
        handle_static_mut_ref(
            tcx,
            span,
            var,
            span.edition().at_least_rust_2024(),
            matches!(m, Mutability::Mut),
            hir_id,
        );
    }
}

/// Check for shared or mutable references of `static mut` inside statement
pub fn maybe_stmt_static_mut(tcx: TyCtxt<'_>, stmt: hir::Stmt<'_>) {
    if let hir::StmtKind::Let(loc) = stmt.kind
        && let hir::PatKind::Binding(ba, _, _, _) = loc.pat.kind
        && matches!(ba.0, rustc_ast::ByRef::Yes)
        && let Some(init) = loc.init
        && let Some(var) = is_path_static_mut(*init)
    {
        handle_static_mut_ref(
            tcx,
            init.span,
            var,
            loc.span.edition().at_least_rust_2024(),
            matches!(ba.1, Mutability::Mut),
            stmt.hir_id,
        );
    }
}

fn is_path_static_mut(expr: hir::Expr<'_>) -> Option<String> {
    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 { mutability: Mutability::Mut, nested: false } = def_kind
    {
        return Some(qpath_to_string(&qpath));
    }
    None
}

fn handle_static_mut_ref(
    tcx: TyCtxt<'_>,
    span: Span,
    var: String,
    e2024: bool,
    mutable: bool,
    hir_id: hir::HirId,
) {
    if e2024 {
        let (sugg, shared) = if mutable {
            (errors::StaticMutRefSugg::Mut { span, var }, "mutable")
        } else {
            (errors::StaticMutRefSugg::Shared { span, var }, "shared")
        };
        tcx.sess.psess.dcx.emit_err(errors::StaticMutRef { span, sugg, shared });
        return;
    }

    let (sugg, shared) = if mutable {
        (errors::RefOfMutStaticSugg::Mut { span, var }, "mutable")
    } else {
        (errors::RefOfMutStaticSugg::Shared { span, var }, "shared")
    };
    tcx.emit_node_span_lint(
        STATIC_MUT_REFS,
        hir_id,
        span,
        errors::RefOfMutStatic { span, sugg, shared },
    );
}