rustc_utils/source_map/spanner/
mir_span.rsuse either::Either;
use log::trace;
use rustc_middle::mir::{
self,
visit::{
MutatingUseContext, NonMutatingUseContext, NonUseContext, PlaceContext,
Visitor as MirVisitor,
},
Body, FakeReadCause, HasLocalDecls, Place, Statement, StatementKind, Terminator,
TerminatorKind, RETURN_PLACE,
};
use rustc_span::SpanData;
use smallvec::{smallvec, SmallVec};
use super::Spanner;
use crate::{mir::location_or_arg::LocationOrArg, BodyExt, PlaceExt, SpanExt};
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct MirSpannedPlace<'tcx> {
pub place: mir::Place<'tcx>,
pub span: SpanData,
pub locations: SmallVec<[LocationOrArg; 1]>,
}
pub struct MirSpanCollector<'a, 'tcx>(pub &'a mut Spanner<'tcx>, pub &'a Body<'tcx>);
macro_rules! try_span {
($self:expr, $span:expr) => {
match $span.as_local($self.0.item_span) {
Some(span) if !$self.0.invalid_span(span) => span,
_ => {
return;
}
}
};
}
impl<'tcx> MirVisitor<'tcx> for MirSpanCollector<'_, 'tcx> {
fn visit_body(&mut self, body: &Body<'tcx>) {
self.super_body(body);
let span = body.local_decls()[RETURN_PLACE].source_info.span;
let span = try_span!(self, span);
let locations = body
.all_returns()
.map(LocationOrArg::Location)
.collect::<SmallVec<_>>();
self.0.mir_spans.push(MirSpannedPlace {
span: span.data(),
locations,
place: Place::from_local(RETURN_PLACE, self.0.tcx),
});
}
fn visit_place(
&mut self,
place: &mir::Place<'tcx>,
context: PlaceContext,
location: mir::Location,
) {
trace!("place={place:?} context={context:?} location={location:?}");
let body = &self.1;
if place.ty(body.local_decls(), self.0.tcx).ty.is_unit() {
return;
}
let (span, locations) = match context {
PlaceContext::MutatingUse(MutatingUseContext::Store)
| PlaceContext::NonMutatingUse(
NonMutatingUseContext::Copy | NonMutatingUseContext::Move,
) => {
let source_info = body.source_info(location);
(source_info.span, smallvec![LocationOrArg::Location(
location
)])
}
PlaceContext::NonMutatingUse(NonMutatingUseContext::Inspect) => {
let source_info = body.source_info(location);
let locations = match body.stmt_at(location) {
Either::Left(Statement {
kind:
StatementKind::FakeRead(box (
FakeReadCause::ForLet(_) | FakeReadCause::ForMatchedPlace(_),
_,
)),
..
}) => match LocationOrArg::from_place(*place, body) {
Some(arg_location) => smallvec![arg_location],
None => {
let locations = assigning_locations(body, *place);
if locations.len() == 0 {
log::warn!("FakeRead of {place:?} has no assignments");
return;
}
locations
}
},
_ => {
return;
}
};
(source_info.span, locations)
}
PlaceContext::NonUse(NonUseContext::VarDebugInfo)
if body.args_iter().any(|local| local == place.local) =>
{
let source_info = body.local_decls()[place.local].source_info;
let location = match LocationOrArg::from_place(*place, body) {
Some(arg_location) => arg_location,
None => LocationOrArg::Location(location),
};
(source_info.span, smallvec![location])
}
_ => {
return;
}
};
let span = try_span!(self, span);
let spanned_place = MirSpannedPlace {
place: *place,
locations,
span: span.data(),
};
trace!("spanned place: {spanned_place:?}");
self.0.mir_spans.push(spanned_place);
}
fn visit_statement(
&mut self,
statement: &mir::Statement<'tcx>,
location: mir::Location,
) {
self.super_statement(statement, location);
if let mir::StatementKind::Assign(box (lhs, _)) = &statement.kind {
if lhs.ty(self.1.local_decls(), self.0.tcx).ty.is_unit() {
return;
}
let span = try_span!(self, statement.source_info.span);
let spanned_place = MirSpannedPlace {
place: *lhs,
locations: smallvec![LocationOrArg::Location(location)],
span: span.data(),
};
trace!("spanned place (assign): {spanned_place:?}");
self.0.mir_spans.push(spanned_place);
}
}
fn visit_terminator(
&mut self,
terminator: &mir::Terminator<'tcx>,
location: mir::Location,
) {
self.super_terminator(terminator, location);
let place = match &terminator.kind {
mir::TerminatorKind::Call { destination, .. } => *destination,
_ => {
return;
}
};
let span = try_span!(self, terminator.source_info.span);
let spanned_place = MirSpannedPlace {
place,
locations: smallvec![LocationOrArg::Location(location)],
span: span.data(),
};
trace!("spanned place (terminator): {spanned_place:?}");
self.0.mir_spans.push(spanned_place);
}
}
fn assigning_locations<'tcx>(
body: &Body<'tcx>,
place: mir::Place<'tcx>,
) -> SmallVec<[LocationOrArg; 1]> {
body
.all_locations()
.filter(|location| match body.stmt_at(*location) {
Either::Left(Statement {
kind: StatementKind::Assign(box (lhs, _)),
..
})
| Either::Right(Terminator {
kind: TerminatorKind::Call {
destination: lhs, ..
},
..
}) => *lhs == place,
_ => false,
})
.map(LocationOrArg::Location)
.collect::<SmallVec<_>>()
}