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}