1 use rustc_hir::{Expr, HirId};
2 use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
3 use rustc_middle::mir::{
4 traversal, Body, InlineAsmOperand, Local, Location, Place, StatementKind, TerminatorKind, START_BLOCK,
6 use rustc_middle::ty::TyCtxt;
9 pub use possible_borrower::PossibleBorrowerMap;
13 mod transitive_relation;
15 #[derive(Clone, Debug, Default)]
16 pub struct LocalUsage {
17 /// The locations where the local is used, if any.
18 pub local_use_locs: Vec<Location>,
19 /// The locations where the local is consumed or mutated, if any.
20 pub local_consume_or_mutate_locs: Vec<Location>,
23 pub fn visit_local_usage(locals: &[Local], mir: &Body<'_>, location: Location) -> Option<Vec<LocalUsage>> {
26 local_use_locs: Vec::new(),
27 local_consume_or_mutate_locs: Vec::new(),
32 traversal::ReversePostorder::new(mir, location.block).try_fold(init, |usage, (tbb, tdata)| {
34 if tdata.terminator().successors().any(|s| s == location.block) {
43 v.visit_basic_block_data(tbb, tdata);
51 results: Vec<LocalUsage>,
54 impl<'a, 'tcx> Visitor<'tcx> for V<'a> {
55 fn visit_place(&mut self, place: &Place<'tcx>, ctx: PlaceContext, loc: Location) {
56 if loc.block == self.location.block && loc.statement_index <= self.location.statement_index {
60 let local = place.local;
62 for (i, self_local) in self.locals.iter().enumerate() {
63 if local == *self_local {
66 PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_)
68 self.results[i].local_use_locs.push(loc);
72 PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
73 | PlaceContext::MutatingUse(MutatingUseContext::Borrow)
75 self.results[i].local_consume_or_mutate_locs.push(loc);
82 /// Convenience wrapper around `visit_local_usage`.
83 pub fn used_exactly_once(mir: &rustc_middle::mir::Body<'_>, local: rustc_middle::mir::Local) -> Option<bool> {
93 let LocalUsage { local_use_locs, .. } = vec.remove(0);
96 .filter(|location| !is_local_assignment(mir, local, *location))
102 /// Returns the `mir::Body` containing the node associated with `hir_id`.
103 #[allow(clippy::module_name_repetitions)]
104 pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> &Body<'_> {
105 let body_owner_local_def_id = tcx.hir().enclosing_body_owner(hir_id);
106 tcx.optimized_mir(body_owner_local_def_id.to_def_id())
109 /// Tries to determine the `Local` corresponding to `expr`, if any.
110 /// This function is expensive and should be used sparingly.
111 pub fn expr_local(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option<Local> {
112 let mir = enclosing_mir(tcx, expr.hir_id);
113 mir.local_decls.iter_enumerated().find_map(|(local, local_decl)| {
114 if local_decl.source_info.span == expr.span {
122 /// Returns a vector of `mir::Location` where `local` is assigned.
123 pub fn local_assignments(mir: &Body<'_>, local: Local) -> Vec<Location> {
124 let mut locations = Vec::new();
125 for (block, data) in mir.basic_blocks.iter_enumerated() {
126 for statement_index in 0..=data.statements.len() {
127 let location = Location { block, statement_index };
128 if is_local_assignment(mir, local, location) {
129 locations.push(location);
136 // `is_local_assignment` is based on `is_place_assignment`:
137 // https://github.com/rust-lang/rust/blob/b7413511dc85ec01ef4b91785f86614589ac6103/compiler/rustc_middle/src/mir/visit.rs#L1350
138 fn is_local_assignment(mir: &Body<'_>, local: Local, location: Location) -> bool {
139 let Location { block, statement_index } = location;
140 let basic_block = &mir.basic_blocks[block];
141 if statement_index < basic_block.statements.len() {
142 let statement = &basic_block.statements[statement_index];
143 if let StatementKind::Assign(box (place, _)) = statement.kind {
144 place.as_local() == Some(local)
149 let terminator = basic_block.terminator();
150 match &terminator.kind {
151 TerminatorKind::Call { destination, .. } => destination.as_local() == Some(local),
152 TerminatorKind::InlineAsm { operands, .. } => operands.iter().any(|operand| {
153 if let InlineAsmOperand::Out { place: Some(place), .. } = operand {
154 place.as_local() == Some(local)