rustc_mir_transform/
check_mut_restriction.rs1use rustc_middle::mir::visit::{PlaceContext, Visitor};
2use rustc_middle::mir::*;
3use rustc_middle::ty::{self, TyCtxt};
4use rustc_span::Span;
5
6use crate::diagnostics;
7
8pub(super) struct CheckMutRestriction;
9
10impl<'tcx> crate::MirLint<'tcx> for CheckMutRestriction {
11 fn run_lint(&self, tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
12 if body.tainted_by_errors.is_some() {
13 return;
14 }
15 let mut checker = MutRestrictionChecker { body, tcx, mutating_span: body.span };
16 checker.visit_body(body);
17 }
18}
19
20struct MutRestrictionChecker<'a, 'tcx> {
21 body: &'a Body<'tcx>,
22 tcx: TyCtxt<'tcx>,
23 mutating_span: Span,
24}
25
26impl<'tcx> Visitor<'tcx> for MutRestrictionChecker<'_, 'tcx> {
27 fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
28 self.mutating_span = terminator.source_info.span;
29 self.super_terminator(terminator, location);
30 }
31
32 fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
33 self.mutating_span = statement.source_info.span;
34 self.super_statement(statement, location);
35 }
36
37 fn visit_place(&mut self, place: &Place<'tcx>, context: PlaceContext, location: Location) {
38 if context.is_mutating_use() {
39 let body_did = self.body.source.instance.def_id();
40
41 for (place_base, elem) in place.iter_projections() {
42 let ProjectionElem::Field(field_idx, _field_ty) = elem else {
46 continue;
47 };
48
49 let base_ty = place_base.ty(self.body, self.tcx);
50
51 let ty::Adt(adt_def, _args) = *base_ty.ty.kind() else {
58 continue;
59 };
60
61 let variant_def = if let Some(idx) = base_ty.variant_index {
62 assert!(adt_def.is_enum());
63 adt_def.variant(idx)
64 } else {
65 adt_def.non_enum_variant()
66 };
67
68 let field_def: &ty::FieldDef = &variant_def.fields[field_idx];
69 let mut_restriction = field_def.mut_restriction;
70
71 if !mut_restriction.is_allowed_in(body_did, self.tcx) {
72 self.tcx.dcx().emit_err(diagnostics::MutOfRestrictedField {
73 mut_span: self.mutating_span,
74 restriction_span: mut_restriction.expect_span(),
75 name: field_def.name,
76 restriction_path: mut_restriction.restriction_path(self.tcx),
77 });
78 }
79 }
80 }
81
82 self.super_place(place, context, location);
83 }
84
85 fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
86 if let Rvalue::Aggregate(aggr, _) = rvalue
87 && let AggregateKind::Adt(adt_did, variant_idx, _args, _user_ty, active_field) = &**aggr
88 {
89 let body_did = self.body.source.instance.def_id();
90 let adt = self.tcx.adt_def(*adt_did);
91 let variant = &adt.variants()[*variant_idx];
92
93 if let Some(field_idx) = active_field {
94 let field_def = &variant.fields[*field_idx];
96 let mut_restriction = field_def.mut_restriction;
97 if !mut_restriction.is_allowed_in(body_did, self.tcx) {
98 self.tcx.dcx().emit_err(diagnostics::ConstructionOfTyWithMutRestrictedField {
99 construction_span: self.mutating_span,
100 restriction_span: mut_restriction.expect_span(),
101 name: variant.name,
102 descr: adt.variant_descr(),
103 restriction_path: mut_restriction.restriction_path(self.tcx),
104 });
105 }
106 } else {
107 let mut_restriction =
109 variant.fields.iter().fold(ty::RestrictionKind::Unrestricted, |acc, field| {
110 acc.stricter_of(field.mut_restriction, self.tcx)
111 });
112 if !mut_restriction.is_allowed_in(body_did, self.tcx) {
113 self.tcx.dcx().emit_err(diagnostics::ConstructionOfTyWithMutRestrictedField {
114 construction_span: self.mutating_span,
115 restriction_span: mut_restriction.expect_span(),
116 name: variant.name,
117 descr: adt.variant_descr(),
118 restriction_path: mut_restriction.restriction_path(self.tcx),
119 });
120 }
121 }
122 }
123
124 self.super_rvalue(rvalue, location);
125 }
126}