rustc_borrowck/diagnostics/
find_use.rs
1use std::collections::VecDeque;
2
3use rustc_data_structures::fx::FxIndexSet;
4use rustc_middle::mir::visit::{MirVisitable, PlaceContext, Visitor};
5use rustc_middle::mir::{self, Body, Local, Location};
6use rustc_middle::ty::{RegionVid, TyCtxt};
7
8use crate::def_use::{self, DefUse};
9use crate::region_infer::{Cause, RegionInferenceContext};
10
11pub(crate) fn find<'tcx>(
12 body: &Body<'tcx>,
13 regioncx: &RegionInferenceContext<'tcx>,
14 tcx: TyCtxt<'tcx>,
15 region_vid: RegionVid,
16 start_point: Location,
17) -> Option<Cause> {
18 let mut uf = UseFinder { body, regioncx, tcx, region_vid, start_point };
19
20 uf.find()
21}
22
23struct UseFinder<'a, 'tcx> {
24 body: &'a Body<'tcx>,
25 regioncx: &'a RegionInferenceContext<'tcx>,
26 tcx: TyCtxt<'tcx>,
27 region_vid: RegionVid,
28 start_point: Location,
29}
30
31impl<'a, 'tcx> UseFinder<'a, 'tcx> {
32 fn find(&mut self) -> Option<Cause> {
33 let mut queue = VecDeque::new();
34 let mut visited = FxIndexSet::default();
35
36 queue.push_back(self.start_point);
37 while let Some(p) = queue.pop_front() {
38 if !self.regioncx.region_contains(self.region_vid, p) {
39 continue;
40 }
41
42 if !visited.insert(p) {
43 continue;
44 }
45
46 let block_data = &self.body[p.block];
47
48 match self.def_use(p, block_data.visitable(p.statement_index)) {
49 Some(DefUseResult::Def) => {}
50
51 Some(DefUseResult::UseLive { local }) => {
52 return Some(Cause::LiveVar(local, p));
53 }
54
55 Some(DefUseResult::UseDrop { local }) => {
56 return Some(Cause::DropVar(local, p));
57 }
58
59 None => {
60 if p.statement_index < block_data.statements.len() {
61 queue.push_back(p.successor_within_block());
62 } else {
63 queue.extend(
64 block_data
65 .terminator()
66 .successors()
67 .filter(|&bb| {
68 Some(&mir::UnwindAction::Cleanup(bb))
69 != block_data.terminator().unwind()
70 })
71 .map(|bb| Location { statement_index: 0, block: bb }),
72 );
73 }
74 }
75 }
76 }
77
78 None
79 }
80
81 fn def_use(&self, location: Location, thing: &dyn MirVisitable<'tcx>) -> Option<DefUseResult> {
82 let mut visitor = DefUseVisitor {
83 body: self.body,
84 tcx: self.tcx,
85 region_vid: self.region_vid,
86 def_use_result: None,
87 };
88
89 thing.apply(location, &mut visitor);
90
91 visitor.def_use_result
92 }
93}
94
95struct DefUseVisitor<'a, 'tcx> {
96 body: &'a Body<'tcx>,
97 tcx: TyCtxt<'tcx>,
98 region_vid: RegionVid,
99 def_use_result: Option<DefUseResult>,
100}
101
102enum DefUseResult {
103 Def,
104 UseLive { local: Local },
105 UseDrop { local: Local },
106}
107
108impl<'a, 'tcx> Visitor<'tcx> for DefUseVisitor<'a, 'tcx> {
109 fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
110 let local_ty = self.body.local_decls[local].ty;
111
112 let mut found_it = false;
113 self.tcx.for_each_free_region(&local_ty, |r| {
114 if r.as_var() == self.region_vid {
115 found_it = true;
116 }
117 });
118
119 if found_it {
120 self.def_use_result = match def_use::categorize(context) {
121 Some(DefUse::Def) => Some(DefUseResult::Def),
122 Some(DefUse::Use) => Some(DefUseResult::UseLive { local }),
123 Some(DefUse::Drop) => Some(DefUseResult::UseDrop { local }),
124 None => None,
125 };
126 }
127 }
128}