rustc_utils/source_map/spanner/
mir_span.rs1use either::Either;
2use log::trace;
3use rustc_middle::mir::{
4 self, Body, FakeReadCause, HasLocalDecls, Place, RETURN_PLACE, Statement,
5 StatementKind, Terminator, TerminatorKind,
6 visit::{
7 MutatingUseContext, NonMutatingUseContext, NonUseContext, PlaceContext,
8 Visitor as MirVisitor,
9 },
10};
11use rustc_span::SpanData;
12use smallvec::{SmallVec, smallvec};
13
14use super::Spanner;
15use crate::{BodyExt, PlaceExt, SpanExt, mir::location_or_arg::LocationOrArg};
16
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct MirSpannedPlace<'tcx> {
19 pub place: mir::Place<'tcx>,
20 pub span: SpanData,
21 pub locations: SmallVec<[LocationOrArg; 1]>,
22}
23
24pub struct MirSpanCollector<'a, 'tcx>(pub &'a mut Spanner<'tcx>, pub &'a Body<'tcx>);
25
26macro_rules! try_span {
27 ($self:expr, $span:expr) => {
28 match $span.as_local($self.0.item_span) {
29 Some(span) if !$self.0.invalid_span(span) => span,
30 _ => {
31 return;
32 }
33 }
34 };
35}
36
37impl<'tcx> MirVisitor<'tcx> for MirSpanCollector<'_, 'tcx> {
38 fn visit_body(&mut self, body: &Body<'tcx>) {
39 self.super_body(body);
40
41 let span = body.local_decls()[RETURN_PLACE].source_info.span;
43 let span = try_span!(self, span);
44 let locations = body
45 .all_returns()
46 .map(LocationOrArg::Location)
47 .collect::<SmallVec<_>>();
48 self.0.mir_spans.push(MirSpannedPlace {
49 span: span.data(),
50 locations,
51 place: Place::from_local(RETURN_PLACE, self.0.tcx),
52 });
53 }
54
55 fn visit_place(
56 &mut self,
57 place: &mir::Place<'tcx>,
58 context: PlaceContext,
59 location: mir::Location,
60 ) {
61 trace!("place={place:?} context={context:?} location={location:?}");
62
63 let body = &self.1;
69 if place.ty(body.local_decls(), self.0.tcx).ty.is_unit() {
70 return;
71 }
72
73 let (span, locations) = match context {
84 PlaceContext::MutatingUse(MutatingUseContext::Store)
85 | PlaceContext::NonMutatingUse(
86 NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
87 ) => {
88 let source_info = body.source_info(location);
89 (source_info.span, smallvec![LocationOrArg::Location(
90 location
91 )])
92 }
93 PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) => {
94 let source_info = body.source_info(location);
95 let locations = match body.stmt_at(location) {
100 Either::Left(Statement {
101 kind:
102 StatementKind::FakeRead(box (
103 FakeReadCause::ForLet(_) | FakeReadCause::ForMatchedPlace(_),
104 _,
105 )),
106 ..
107 }) => match LocationOrArg::from_place(*place, body) {
108 Some(arg_location) => smallvec![arg_location],
109 None => {
110 let locations = assigning_locations(body, *place);
111 if locations.len() == 0 {
112 log::warn!("FakeRead of {place:?} has no assignments");
113 return;
114 }
115 locations
116 }
117 },
118 _ => {
119 return;
120 }
121 };
122 (source_info.span, locations)
123 }
124 PlaceContext::NonUse(NonUseContext::VarDebugInfo)
125 if body.args_iter().any(|local| local == place.local) =>
126 {
127 let source_info = body.local_decls()[place.local].source_info;
128 let location = match LocationOrArg::from_place(*place, body) {
129 Some(arg_location) => arg_location,
130 None => LocationOrArg::Location(location),
131 };
132 (source_info.span, smallvec![location])
133 }
134 _ => {
135 return;
136 }
137 };
138
139 let span = try_span!(self, span);
140
141 let spanned_place = MirSpannedPlace {
142 place: *place,
143 locations,
144 span: span.data(),
145 };
146 trace!("spanned place: {spanned_place:?}");
147
148 self.0.mir_spans.push(spanned_place);
149 }
150
151 fn visit_statement(
156 &mut self,
157 statement: &mir::Statement<'tcx>,
158 location: mir::Location,
159 ) {
160 self.super_statement(statement, location);
161
162 if let mir::StatementKind::Assign(box (lhs, _)) = &statement.kind {
163 if lhs.ty(self.1.local_decls(), self.0.tcx).ty.is_unit() {
164 return;
165 }
166
167 let span = try_span!(self, statement.source_info.span);
168 let spanned_place = MirSpannedPlace {
169 place: *lhs,
170 locations: smallvec![LocationOrArg::Location(location)],
171 span: span.data(),
172 };
173 trace!("spanned place (assign): {spanned_place:?}");
174 self.0.mir_spans.push(spanned_place);
175 }
176 }
177
178 fn visit_terminator(
179 &mut self,
180 terminator: &mir::Terminator<'tcx>,
181 location: mir::Location,
182 ) {
183 self.super_terminator(terminator, location);
184
185 let place = match &terminator.kind {
186 mir::TerminatorKind::Call { destination, .. } => *destination,
187 _ => {
188 return;
189 }
190 };
191
192 let span = try_span!(self, terminator.source_info.span);
193 let spanned_place = MirSpannedPlace {
194 place,
195 locations: smallvec![LocationOrArg::Location(location)],
196 span: span.data(),
197 };
198 trace!("spanned place (terminator): {spanned_place:?}");
199 self.0.mir_spans.push(spanned_place);
200 }
201}
202
203fn assigning_locations<'tcx>(
204 body: &Body<'tcx>,
205 place: mir::Place<'tcx>,
206) -> SmallVec<[LocationOrArg; 1]> {
207 body
208 .all_locations()
209 .filter(|location| match body.stmt_at(*location) {
210 Either::Left(Statement {
211 kind: StatementKind::Assign(box (lhs, _)),
212 ..
213 })
214 | Either::Right(Terminator {
215 kind: TerminatorKind::Call {
216 destination: lhs, ..
217 },
218 ..
219 }) => *lhs == place,
220 _ => false,
221 })
222 .map(LocationOrArg::Location)
223 .collect::<SmallVec<_>>()
224}