rustc_lint/
interior_mutable_consts.rs1use rustc_hir::def::{DefKind, Res};
2use rustc_hir::{Expr, ExprKind, ItemKind, Node, find_attr};
3use rustc_middle::ty::adjustment::Adjust;
4use rustc_session::{declare_lint, declare_lint_pass};
5
6use crate::diagnostics::{
7 ConstItemInteriorMutationsDiag, ConstItemInteriorMutationsSuggestionStatic,
8};
9use crate::{LateContext, LateLintPass, LintContext};
10
11#[doc = r" The `const_item_interior_mutations` lint checks for calls which"]
#[doc = r" mutates an interior mutable const-item."]
#[doc = r""]
#[doc = r" ### Example"]
#[doc = r""]
#[doc = r" ```rust"]
#[doc = r" use std::sync::Once;"]
#[doc = r""]
#[doc =
r" const INIT: Once = Once::new(); // using `INIT` will always create a temporary and"]
#[doc =
r" // never modify it-self on use, should be a `static`"]
#[doc = r" // instead for shared use"]
#[doc = r""]
#[doc = r" fn init() {"]
#[doc = r" INIT.call_once(|| {"]
#[doc = r#" println!("Once::call_once first call");"#]
#[doc = r" });"]
#[doc =
r" INIT.call_once(|| { // this second will also print"]
#[doc =
r#" println!("Once::call_once second call"); // as each call to `INIT` creates"#]
#[doc = r" }); // new temporary"]
#[doc = r" }"]
#[doc = r" ```"]
#[doc = r""]
#[doc = r" {{produces}}"]
#[doc = r""]
#[doc = r" ### Explanation"]
#[doc = r""]
#[doc =
r" Calling a method which mutates an interior mutable type has no effect as const-item"]
#[doc =
r" are essentially inlined wherever they are used, meaning that they are copied"]
#[doc =
r" directly into the relevant context when used rendering modification through"]
#[doc = r" interior mutability ineffective across usage of that const-item."]
#[doc = r""]
#[doc =
r" The current implementation of this lint only warns on significant `std` and"]
#[doc =
r" `core` interior mutable types, like `Once`, `AtomicI32`, ... this is done out"]
#[doc =
r" of prudence to avoid false-positive and may be extended in the future."]
pub static CONST_ITEM_INTERIOR_MUTATIONS: &::rustc_lint_defs::Lint =
&::rustc_lint_defs::Lint {
name: "CONST_ITEM_INTERIOR_MUTATIONS",
default_level: ::rustc_lint_defs::Warn,
desc: "checks for calls which mutates a interior mutable const-item",
is_externally_loaded: false,
..::rustc_lint_defs::Lint::default_fields_for_macro()
};declare_lint! {
12 pub CONST_ITEM_INTERIOR_MUTATIONS,
47 Warn,
48 "checks for calls which mutates a interior mutable const-item"
49}
50
51pub struct InteriorMutableConsts;
#[automatically_derived]
impl ::core::marker::Copy for InteriorMutableConsts { }
#[automatically_derived]
#[doc(hidden)]
unsafe impl ::core::clone::TrivialClone for InteriorMutableConsts { }
#[automatically_derived]
impl ::core::clone::Clone for InteriorMutableConsts {
#[inline]
fn clone(&self) -> InteriorMutableConsts { *self }
}
impl ::rustc_lint_defs::LintPass for InteriorMutableConsts {
fn name(&self) -> &'static str { "InteriorMutableConsts" }
fn get_lints(&self) -> ::rustc_lint_defs::LintVec {
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[CONST_ITEM_INTERIOR_MUTATIONS]))
}
}
impl InteriorMutableConsts {
#[allow(unused)]
pub fn lint_vec() -> ::rustc_lint_defs::LintVec {
::alloc::boxed::box_assume_init_into_vec_unsafe(::alloc::intrinsics::write_box_via_move(::alloc::boxed::Box::new_uninit(),
[CONST_ITEM_INTERIOR_MUTATIONS]))
}
}declare_lint_pass!(InteriorMutableConsts => [CONST_ITEM_INTERIOR_MUTATIONS]);
52
53impl<'tcx> LateLintPass<'tcx> for InteriorMutableConsts {
54 fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
55 let typeck = cx.typeck_results();
56
57 let (method_did, receiver) = match expr.kind {
58 ExprKind::MethodCall(_, receiver, _, _) => {
60 (typeck.type_dependent_def_id(expr.hir_id), receiver)
61 }
62 ExprKind::Call(path, [receiver, ..]) => match receiver.kind {
64 ExprKind::AddrOf(_, _, receiver) => match path.kind {
65 ExprKind::Path(ref qpath) => {
66 (cx.qpath_res(qpath, path.hir_id).opt_def_id(), receiver)
67 }
68 _ => return,
69 },
70 _ => return,
71 },
72 _ => return,
73 };
74
75 let Some(method_did) = method_did else {
76 return;
77 };
78
79 if let ExprKind::Path(qpath) = &receiver.kind
80 && let Res::Def(DefKind::Const { .. } | DefKind::AssocConst { .. }, const_did) =
81 typeck.qpath_res(qpath, receiver.hir_id)
82 && !cx
85 .typeck_results()
86 .expr_adjustments(receiver)
87 .into_iter()
88 .any(|adj| #[allow(non_exhaustive_omitted_patterns)] match adj.kind {
Adjust::Deref(_) => true,
_ => false,
}matches!(adj.kind, Adjust::Deref(_)))
89 && {
{
'done:
{
for i in
::rustc_attr_ir::HasAttrs::get_attrs(method_did, &cx.tcx) {
#[allow(unused_imports)]
use ::rustc_attr_ir::AttributeKind::*;
let i: &::rustc_attr_ir::Attribute = i;
match i {
::rustc_attr_ir::Attribute::Parsed(RustcShouldNotBeCalledOnConstItems)
=> {
break 'done Some(());
}
::rustc_attr_ir::Attribute::Unparsed(..) =>
{}
#[deny(unreachable_patterns)]
_ => {}
}
}
None
}
}
}.is_some()find_attr!(
91 cx.tcx, method_did,
92 RustcShouldNotBeCalledOnConstItems
93 )
94 && let Some(method_name) = cx.tcx.opt_item_ident(method_did)
95 && let Some(const_name) = cx.tcx.opt_item_ident(const_did)
96 && let Some(const_ty) = typeck.node_type_opt(receiver.hir_id)
97 {
98 let sugg_static = if let Some(Node::Item(const_item)) =
100 cx.tcx.hir_get_if_local(const_did)
101 && let ItemKind::Const(ident, _generics, _ty, _body_id) = const_item.kind
102 {
103 if let Some(vis_span) = const_item.vis_span.find_ancestor_inside(const_item.span)
104 && const_item.span.can_be_used_for_suggestions()
105 && vis_span.can_be_used_for_suggestions()
106 {
107 Some(ConstItemInteriorMutationsSuggestionStatic::Spanful {
108 const_: const_item.vis_span.between(ident.span),
109 before: if !vis_span.is_empty() { " " } else { "" },
110 const_name,
111 })
112 } else {
113 Some(ConstItemInteriorMutationsSuggestionStatic::Spanless { const_name })
114 }
115 } else {
116 None
117 };
118
119 cx.emit_span_lint(
120 CONST_ITEM_INTERIOR_MUTATIONS,
121 expr.span,
122 ConstItemInteriorMutationsDiag {
123 method_name,
124 const_name,
125 const_ty,
126 receiver_span: receiver.span,
127 sugg_static,
128 },
129 );
130 }
131 }
132}