clippy_utils/mir/
mod.rs
1use rustc_hir::{Expr, HirId};
2use rustc_index::bit_set::DenseBitSet;
3use rustc_middle::mir::visit::{MutatingUseContext, NonMutatingUseContext, PlaceContext, Visitor};
4use rustc_middle::mir::{
5 BasicBlock, Body, InlineAsmOperand, Local, Location, Place, START_BLOCK, StatementKind, TerminatorKind, traversal,
6};
7use rustc_middle::ty::TyCtxt;
8
9mod possible_borrower;
10pub use possible_borrower::PossibleBorrowerMap;
11
12mod possible_origin;
13
14mod transitive_relation;
15
16#[derive(Clone, Debug, Default)]
17pub struct LocalUsage {
18 pub local_use_locs: Vec<Location>,
20 pub local_consume_or_mutate_locs: Vec<Location>,
22}
23
24pub fn visit_local_usage(locals: &[Local], mir: &Body<'_>, location: Location) -> Option<Vec<LocalUsage>> {
25 let init = vec![
26 LocalUsage {
27 local_use_locs: Vec::new(),
28 local_consume_or_mutate_locs: Vec::new(),
29 };
30 locals.len()
31 ];
32
33 traversal::Postorder::new(&mir.basic_blocks, location.block, ())
34 .collect::<Vec<_>>()
35 .into_iter()
36 .rev()
37 .try_fold(init, |usage, tbb| {
38 let tdata = &mir.basic_blocks[tbb];
39
40 if tdata.terminator().successors().any(|s| s == location.block) {
42 return None;
43 }
44
45 let mut v = V {
46 locals,
47 location,
48 results: usage,
49 };
50 v.visit_basic_block_data(tbb, tdata);
51 Some(v.results)
52 })
53}
54
55struct V<'a> {
56 locals: &'a [Local],
57 location: Location,
58 results: Vec<LocalUsage>,
59}
60
61impl<'tcx> Visitor<'tcx> for V<'_> {
62 fn visit_place(&mut self, place: &Place<'tcx>, ctx: PlaceContext, loc: Location) {
63 if loc.block == self.location.block && loc.statement_index <= self.location.statement_index {
64 return;
65 }
66
67 let local = place.local;
68
69 for (i, self_local) in self.locals.iter().enumerate() {
70 if local == *self_local {
71 if !matches!(
72 ctx,
73 PlaceContext::MutatingUse(MutatingUseContext::Drop) | PlaceContext::NonUse(_)
74 ) {
75 self.results[i].local_use_locs.push(loc);
76 }
77 if matches!(
78 ctx,
79 PlaceContext::NonMutatingUse(NonMutatingUseContext::Move)
80 | PlaceContext::MutatingUse(MutatingUseContext::Borrow)
81 ) {
82 self.results[i].local_consume_or_mutate_locs.push(loc);
83 }
84 }
85 }
86 }
87}
88
89pub fn block_in_cycle(body: &Body<'_>, block: BasicBlock) -> bool {
91 let mut seen = DenseBitSet::new_empty(body.basic_blocks.len());
92 let mut to_visit = Vec::with_capacity(body.basic_blocks.len() / 2);
93
94 seen.insert(block);
95 let mut next = block;
96 loop {
97 for succ in body.basic_blocks[next].terminator().successors() {
98 if seen.insert(succ) {
99 to_visit.push(succ);
100 } else if succ == block {
101 return true;
102 }
103 }
104
105 if let Some(x) = to_visit.pop() {
106 next = x;
107 } else {
108 return false;
109 }
110 }
111}
112
113pub fn used_exactly_once(mir: &Body<'_>, local: Local) -> Option<bool> {
115 visit_local_usage(
116 &[local],
117 mir,
118 Location {
119 block: START_BLOCK,
120 statement_index: 0,
121 },
122 )
123 .map(|mut vec| {
124 let LocalUsage { local_use_locs, .. } = vec.remove(0);
125 let mut locations = local_use_locs
126 .into_iter()
127 .filter(|&location| !is_local_assignment(mir, local, location));
128 if let Some(location) = locations.next() {
129 locations.next().is_none() && !block_in_cycle(mir, location.block)
130 } else {
131 false
132 }
133 })
134}
135
136#[allow(clippy::module_name_repetitions)]
138pub fn enclosing_mir(tcx: TyCtxt<'_>, hir_id: HirId) -> Option<&Body<'_>> {
139 let body_owner_local_def_id = tcx.hir().enclosing_body_owner(hir_id);
140 if tcx.hir().body_owner_kind(body_owner_local_def_id).is_fn_or_closure() {
141 Some(tcx.optimized_mir(body_owner_local_def_id.to_def_id()))
142 } else {
143 None
144 }
145}
146
147pub fn expr_local(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> Option<Local> {
150 enclosing_mir(tcx, expr.hir_id).and_then(|mir| {
151 mir.local_decls.iter_enumerated().find_map(|(local, local_decl)| {
152 if local_decl.source_info.span == expr.span {
153 Some(local)
154 } else {
155 None
156 }
157 })
158 })
159}
160
161pub fn local_assignments(mir: &Body<'_>, local: Local) -> Vec<Location> {
163 let mut locations = Vec::new();
164 for (block, data) in mir.basic_blocks.iter_enumerated() {
165 for statement_index in 0..=data.statements.len() {
166 let location = Location { block, statement_index };
167 if is_local_assignment(mir, local, location) {
168 locations.push(location);
169 }
170 }
171 }
172 locations
173}
174
175fn is_local_assignment(mir: &Body<'_>, local: Local, location: Location) -> bool {
178 let Location { block, statement_index } = location;
179 let basic_block = &mir.basic_blocks[block];
180 if statement_index < basic_block.statements.len() {
181 let statement = &basic_block.statements[statement_index];
182 if let StatementKind::Assign(box (place, _)) = statement.kind {
183 place.as_local() == Some(local)
184 } else {
185 false
186 }
187 } else {
188 let terminator = basic_block.terminator();
189 match &terminator.kind {
190 TerminatorKind::Call { destination, .. } => destination.as_local() == Some(local),
191 TerminatorKind::InlineAsm { operands, .. } => operands.iter().any(|operand| {
192 if let InlineAsmOperand::Out { place: Some(place), .. } = operand {
193 place.as_local() == Some(local)
194 } else {
195 false
196 }
197 }),
198 _ => false,
199 }
200 }
201}