Skip to main content

rapx/check/opt/
loop_visitors.rs

1use rustc_hir::{Expr, ExprKind, intravisit};
2use rustc_middle::ty::TypeckResults;
3use rustc_span::Span;
4
5pub struct LoopFinder<'tcx> {
6    pub typeck_results: &'tcx TypeckResults<'tcx>,
7    pub record: Vec<(Span, Vec<Span>)>,
8    pub target_def_id: rustc_span::def_id::DefId,
9}
10
11pub struct MethodCallFinder<'tcx> {
12    typeck_results: &'tcx TypeckResults<'tcx>,
13    record: Vec<Span>,
14    target_def_id: rustc_span::def_id::DefId,
15}
16
17impl<'tcx> MethodCallFinder<'tcx> {
18    pub fn new(
19        typeck_results: &'tcx TypeckResults<'tcx>,
20        target_def_id: rustc_span::def_id::DefId,
21    ) -> MethodCallFinder<'tcx> {
22        MethodCallFinder {
23            typeck_results,
24            record: Vec::new(),
25            target_def_id,
26        }
27    }
28
29    pub fn into_record(self) -> Vec<Span> {
30        self.record
31    }
32}
33
34impl<'tcx> LoopFinder<'tcx> {
35    pub fn new(
36        typeck_results: &'tcx TypeckResults<'tcx>,
37        target_def_id: rustc_span::def_id::DefId,
38    ) -> LoopFinder<'tcx> {
39        LoopFinder {
40            typeck_results,
41            record: Vec::new(),
42            target_def_id,
43        }
44    }
45
46    pub fn into_record(self) -> Vec<(Span, Vec<Span>)> {
47        self.record
48    }
49}
50
51impl<'tcx> intravisit::Visitor<'tcx> for MethodCallFinder<'tcx> {
52    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
53        if let ExprKind::MethodCall(.., span) = ex.kind {
54            let def_id = self
55                .typeck_results
56                .type_dependent_def_id(ex.hir_id)
57                .unwrap();
58            if def_id == self.target_def_id {
59                self.record.push(span);
60            }
61        }
62        intravisit::walk_expr(self, ex);
63    }
64}
65
66impl<'tcx> intravisit::Visitor<'tcx> for LoopFinder<'tcx> {
67    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
68        if let ExprKind::Loop(block, ..) = ex.kind {
69            let mut push_finder = MethodCallFinder::new(self.typeck_results, self.target_def_id);
70            intravisit::walk_block(&mut push_finder, block);
71            if push_finder.record.len() == 1 {
72                self.record.push((ex.span, push_finder.into_record()));
73            }
74        }
75        intravisit::walk_expr(self, ex);
76    }
77}