rustc_mir_transform/
check_mut_restriction.rs1use rustc_hir::def::{CtorOf, DefKind};
2use rustc_middle::mir::visit::{PlaceContext, Visitor};
3use rustc_middle::mir::*;
4use rustc_middle::ty::{self, TyCtxt};
5use rustc_span::Span;
6
7use crate::diagnostics;
8
9pub(super) struct CheckMutRestriction;
10
11impl<'tcx> crate::MirLint<'tcx> for CheckMutRestriction {
12 fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
13 if body.tainted_by_errors.is_some() {
14 return;
15 }
16 let mut checker = MutRestrictionChecker { body, tcx, mutating_span: body.span };
17 checker.visit_body(body);
18 }
19}
20
21struct MutRestrictionChecker<'a, 'tcx> {
22 body: &'a Body<'tcx>,
23 tcx: TyCtxt<'tcx>,
24 mutating_span: Span,
25}
26
27impl<'tcx> Visitor<'tcx> for MutRestrictionChecker<'_, 'tcx> {
28 fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
29 self.mutating_span = terminator.source_info.span;
30 self.super_terminator(terminator, location);
31 }
32
33 fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
34 self.mutating_span = statement.source_info.span;
35 self.super_statement(statement, location);
36 }
37
38 fn visit_const_operand(&mut self, constant: &ConstOperand<'tcx>, location: Location) {
40 if let ty::FnDef(def_id, _) = *constant.const_.ty().kind()
41 && let DefKind::Ctor(ctor_of, _) = self.tcx.def_kind(def_id)
42 {
43 let body_did = self.body.source.instance.def_id();
44 let adt_did = match ctor_of {
45 CtorOf::Struct => self.tcx.parent(def_id),
46 CtorOf::Variant => self.tcx.parent(self.tcx.parent(def_id)),
47 };
48 let adt = self.tcx.adt_def(adt_did);
49 let variant = match ctor_of {
50 CtorOf::Struct => adt.non_enum_variant(),
51 CtorOf::Variant => adt.variant_with_ctor_id(def_id),
52 };
53
54 let mut_restriction =
55 variant.fields.iter().fold(ty::RestrictionKind::Unrestricted, |acc, field| {
56 acc.stricter_of(field.mut_restriction, self.tcx)
57 });
58 if !mut_restriction.is_allowed_in(body_did, self.tcx) {
59 self.tcx.dcx().emit_err(diagnostics::ConstructionOfTyWithMutRestrictedField {
60 construction_span: constant.span,
61 restriction_span: mut_restriction.expect_span(),
62 name: variant.name,
63 descr: adt.variant_descr(),
64 restriction_path: mut_restriction.restriction_path(self.tcx),
65 });
66 }
67 }
68
69 self.super_const_operand(constant, location);
70 }
71
72 fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
73 if context.is_mutating_use() {
74 let body_did = self.body.source.instance.def_id();
75
76 for (place_base, elem) in place.iter_projections() {
77 let ProjectionElem::Field(field_idx, _field_ty) = elem else {
81 continue;
82 };
83
84 let base_ty = place_base.ty(self.body, self.tcx);
85
86 let ty::Adt(adt_def, _args) = *base_ty.ty.kind() else {
93 continue;
94 };
95
96 let variant_def = if let Some(idx) = base_ty.variant_index {
97 assert!(adt_def.is_enum());
98 adt_def.variant(idx)
99 } else {
100 adt_def.non_enum_variant()
101 };
102
103 let field_def: &ty::FieldDef = &variant_def.fields[field_idx];
104 let mut_restriction = field_def.mut_restriction;
105
106 if !mut_restriction.is_allowed_in(body_did, self.tcx) {
107 self.tcx.dcx().emit_err(diagnostics::MutOfRestrictedField {
108 mut_span: self.mutating_span,
109 restriction_span: mut_restriction.expect_span(),
110 name: field_def.name,
111 restriction_path: mut_restriction.restriction_path(self.tcx),
112 });
113 }
114 }
115 }
116
117 self.super_place(place, context, location);
118 }
119
120 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
121 if let Rvalue::Aggregate(aggr, _) = rvalue
122 && let AggregateKind::Adt(adt_did, variant_idx, _args, _user_ty, active_field) = &**aggr
123 {
124 let body_did = self.body.source.instance.def_id();
125 let adt = self.tcx.adt_def(*adt_did);
126 let variant = &adt.variants()[*variant_idx];
127
128 if let Some(field_idx) = active_field {
129 let field_def = &variant.fields[*field_idx];
131 let mut_restriction = field_def.mut_restriction;
132 if !mut_restriction.is_allowed_in(body_did, self.tcx) {
133 self.tcx.dcx().emit_err(diagnostics::ConstructionOfTyWithMutRestrictedField {
134 construction_span: self.mutating_span,
135 restriction_span: mut_restriction.expect_span(),
136 name: variant.name,
137 descr: adt.variant_descr(),
138 restriction_path: mut_restriction.restriction_path(self.tcx),
139 });
140 }
141 } else {
142 let mut_restriction =
144 variant.fields.iter().fold(ty::RestrictionKind::Unrestricted, |acc, field| {
145 acc.stricter_of(field.mut_restriction, self.tcx)
146 });
147 if !mut_restriction.is_allowed_in(body_did, self.tcx) {
148 self.tcx.dcx().emit_err(diagnostics::ConstructionOfTyWithMutRestrictedField {
149 construction_span: self.mutating_span,
150 restriction_span: mut_restriction.expect_span(),
151 name: variant.name,
152 descr: adt.variant_descr(),
153 restriction_path: mut_restriction.restriction_path(self.tcx),
154 });
155 }
156 }
157 }
158
159 self.super_rvalue(rvalue, location);
160 }
161}