rustc_borrowck/diagnostics/
find_use.rs
1use std::collections::VecDeque;
2
3use rustc_data_structures::fx::FxIndexSet;
4use rustc_middle::mir::visit::{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 let mut visitor = DefUseVisitor {
49 body: self.body,
50 tcx: self.tcx,
51 region_vid: self.region_vid,
52 def_use_result: None,
53 };
54
55 let is_statement = p.statement_index < block_data.statements.len();
56
57 if is_statement {
58 visitor.visit_statement(&block_data.statements[p.statement_index], p);
59 } else {
60 visitor.visit_terminator(block_data.terminator.as_ref().unwrap(), p);
61 }
62
63 match visitor.def_use_result {
64 Some(DefUseResult::Def) => {}
65
66 Some(DefUseResult::UseLive { local }) => {
67 return Some(Cause::LiveVar(local, p));
68 }
69
70 Some(DefUseResult::UseDrop { local }) => {
71 return Some(Cause::DropVar(local, p));
72 }
73
74 None => {
75 if is_statement {
76 queue.push_back(p.successor_within_block());
77 } else {
78 queue.extend(
79 block_data
80 .terminator()
81 .successors()
82 .filter(|&bb| {
83 Some(&mir::UnwindAction::Cleanup(bb))
84 != block_data.terminator().unwind()
85 })
86 .map(|bb| Location { statement_index: 0, block: bb }),
87 );
88 }
89 }
90 }
91 }
92
93 None
94 }
95}
96
97struct DefUseVisitor<'a, 'tcx> {
98 body: &'a Body<'tcx>,
99 tcx: TyCtxt<'tcx>,
100 region_vid: RegionVid,
101 def_use_result: Option<DefUseResult>,
102}
103
104enum DefUseResult {
105 Def,
106 UseLive { local: Local },
107 UseDrop { local: Local },
108}
109
110impl<'a, 'tcx> Visitor<'tcx> for DefUseVisitor<'a, 'tcx> {
111 fn visit_local(&mut self, local: Local, context: PlaceContext, _: Location) {
112 let local_ty = self.body.local_decls[local].ty;
113
114 let mut found_it = false;
115 self.tcx.for_each_free_region(&local_ty, |r| {
116 if r.as_var() == self.region_vid {
117 found_it = true;
118 }
119 });
120
121 if found_it {
122 self.def_use_result = match def_use::categorize(context) {
123 Some(DefUse::Def) => Some(DefUseResult::Def),
124 Some(DefUse::Use) => Some(DefUseResult::UseLive { local }),
125 Some(DefUse::Drop) => Some(DefUseResult::UseDrop { local }),
126 None => None,
127 };
128 }
129 }
130}