rustc_mir_transform/
check_const_item_mutation.rs1use rustc_hir::HirId;
2use rustc_middle::mir::visit::Visitor;
3use rustc_middle::mir::*;
4use rustc_middle::ty::TyCtxt;
5use rustc_session::lint::builtin::CONST_ITEM_MUTATION;
6use rustc_span::Span;
7use rustc_span::def_id::DefId;
8
9use crate::errors;
10
11pub(super) struct CheckConstItemMutation;
12
13impl<'tcx> crate::MirLint<'tcx> for CheckConstItemMutation {
14 fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
15 let mut checker = ConstMutationChecker { body, tcx, target_local: None };
16 checker.visit_body(body);
17 }
18}
19
20struct ConstMutationChecker<'a, 'tcx> {
21 body: &'a Body<'tcx>,
22 tcx: TyCtxt<'tcx>,
23 target_local: Option<Local>,
24}
25
26impl<'tcx> ConstMutationChecker<'_, 'tcx> {
27 fn is_const_item(&self, local: Local) -> Option<DefId> {
28 if let LocalInfo::ConstRef { def_id } = *self.body.local_decls[local].local_info() {
29 Some(def_id)
30 } else {
31 None
32 }
33 }
34
35 fn is_const_item_without_destructor(&self, local: Local) -> Option<DefId> {
36 let def_id = self.is_const_item(local)?;
37
38 match self.tcx.calculate_dtor(def_id, |_, _| Ok(())) {
57 Some(_) => None,
58 None => Some(def_id),
59 }
60 }
61
62 fn should_lint_const_item_usage(
65 &self,
66 place: &Place<'tcx>,
67 const_item: DefId,
68 location: Location,
69 ) -> Option<(HirId, Span, Span)> {
70 if !place.projection.iter().any(|p| matches!(p, PlaceElem::Deref)) {
78 let source_info = self.body.source_info(location);
79 let lint_root = self.body.source_scopes[source_info.scope]
80 .local_data
81 .as_ref()
82 .assert_crate_local()
83 .lint_root;
84
85 Some((lint_root, source_info.span, self.tcx.def_span(const_item)))
86 } else {
87 None
88 }
89 }
90}
91
92impl<'tcx> Visitor<'tcx> for ConstMutationChecker<'_, 'tcx> {
93 fn visit_statement(&mut self, stmt: &Statement<'tcx>, loc: Location) {
94 if let StatementKind::Assign(box (lhs, _)) = &stmt.kind {
95 if !lhs.projection.is_empty()
99 && let Some(def_id) = self.is_const_item_without_destructor(lhs.local)
100 && let Some((lint_root, span, item)) =
101 self.should_lint_const_item_usage(lhs, def_id, loc)
102 {
103 self.tcx.emit_node_span_lint(
104 CONST_ITEM_MUTATION,
105 lint_root,
106 span,
107 errors::ConstMutate::Modify { konst: item },
108 );
109 }
110
111 self.target_local = lhs.as_local();
122 }
123 self.super_statement(stmt, loc);
124 self.target_local = None;
125 }
126
127 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, loc: Location) {
128 if let Rvalue::Ref(_, BorrowKind::Mut { .. }, place) = rvalue {
129 let local = place.local;
130 if let Some(def_id) = self.is_const_item(local) {
131 let method_did = self.target_local.and_then(|target_local| {
136 find_self_call(self.tcx, self.body, target_local, loc.block)
137 });
138 let lint_loc =
139 if method_did.is_some() { self.body.terminator_loc(loc.block) } else { loc };
140
141 let method_call = if let Some((method_did, _)) = method_did {
142 Some(self.tcx.def_span(method_did))
143 } else {
144 None
145 };
146 if let Some((lint_root, span, item)) =
147 self.should_lint_const_item_usage(place, def_id, lint_loc)
148 {
149 self.tcx.emit_node_span_lint(
150 CONST_ITEM_MUTATION,
151 lint_root,
152 span,
153 errors::ConstMutate::MutBorrow { method_call, konst: item },
154 );
155 }
156 }
157 }
158 self.super_rvalue(rvalue, loc);
159 }
160}