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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
// `#![no_std]`: these arrive with the standard prelude and name no path, so a `std::`
// search cannot see them - and a `#[derive]` can use them without the name appearing
// in this file at all, which is why they are not trimmed by inspection.
use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;
use crate::rustc_hir::def::{DefKind, Res};
use crate::rustc_hir::{Expr, ExprKind, ItemKind, Node, find_attr};
use crate::rustc_lint_defs::{declare_lint, declare_lint_pass};
use crate::rustc_middle::ty::adjustment::Adjust;
use crate::rustc_lint::diagnostics::{
ConstItemInteriorMutationsDiag, ConstItemInteriorMutationsSuggestionStatic,
};
use crate::rustc_lint::{LateContext, LateLintPass, LintContext};
declare_lint! {
/// The `const_item_interior_mutations` lint checks for calls which
/// mutates an interior mutable const-item.
///
/// ### Explanation
///
/// Calling a method which mutates an interior mutable type has no effect as const-item
/// are essentially inlined wherever they are used, meaning that they are copied
/// directly into the relevant context when used rendering modification through
/// interior mutability ineffective across usage of that const-item.
///
/// The current implementation of this lint only warns on significant `std` and
/// `core` interior mutable types, like `Once`, `AtomicI32`, ... this is done out
/// of prudence to avoid false-positive and may be extended in the future.
pub CONST_ITEM_INTERIOR_MUTATIONS,
Warn,
"checks for calls which mutates a interior mutable const-item"
}
declare_lint_pass!(InteriorMutableConsts => [CONST_ITEM_INTERIOR_MUTATIONS]);
impl<'tcx> LateLintPass<'tcx> for InteriorMutableConsts {
fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
let typeck = cx.typeck_results();
let (method_did, receiver) = match expr.kind {
// matching on `<receiver>.method(..)`
ExprKind::MethodCall(_, receiver, _, _) => {
(typeck.type_dependent_def_id(expr.hir_id), receiver)
}
// matching on `function(&<receiver>, ...)`
ExprKind::Call(path, [receiver, ..]) => match receiver.kind {
ExprKind::AddrOf(_, _, receiver) => match path.kind {
ExprKind::Path(ref qpath) => {
(cx.qpath_res(qpath, path.hir_id).opt_def_id(), receiver)
}
_ => return,
},
_ => return,
},
_ => return,
};
let Some(method_did) = method_did else {
return;
};
if let ExprKind::Path(qpath) = &receiver.kind
&& let Res::Def(DefKind::Const { .. } | DefKind::AssocConst { .. }, const_did) =
typeck.qpath_res(qpath, receiver.hir_id)
// Don't consider derefs as those can do arbitrary things
// like using thread local (see rust-lang/rust#150157)
&& !cx
.typeck_results()
.expr_adjustments(receiver)
.into_iter()
.any(|adj| matches!(adj.kind, Adjust::Deref(_)))
// Let's do the attribute check after the other checks for perf reasons
&& find_attr!(
cx.tcx, method_did,
RustcShouldNotBeCalledOnConstItems
)
&& let Some(method_name) = cx.tcx.opt_item_ident(method_did)
&& let Some(const_name) = cx.tcx.opt_item_ident(const_did)
&& let Some(const_ty) = typeck.node_type_opt(receiver.hir_id)
{
// Find the local `const`-item and create the suggestion to use `static` instead
let sugg_static = if let Some(Node::Item(const_item)) =
cx.tcx.hir_get_if_local(const_did)
&& let ItemKind::Const(ident, _generics, _ty, _body_id) = const_item.kind
{
if let Some(vis_span) = const_item.vis_span.find_ancestor_inside(const_item.span)
&& const_item.span.can_be_used_for_suggestions()
&& vis_span.can_be_used_for_suggestions()
{
Some(ConstItemInteriorMutationsSuggestionStatic::Spanful {
const_: const_item.vis_span.between(ident.span),
before: if !vis_span.is_empty() { " " } else { "" },
const_name,
})
} else {
Some(ConstItemInteriorMutationsSuggestionStatic::Spanless { const_name })
}
} else {
None
};
cx.emit_span_lint(
CONST_ITEM_INTERIOR_MUTATIONS,
expr.span,
ConstItemInteriorMutationsDiag {
method_name,
const_name,
const_ty,
receiver_span: receiver.span,
sugg_static,
},
);
}
}
}