rapx 0.7.29

A static analysis platform for Rust program analysis and verification
use rustc_hir::{Expr, ExprKind, intravisit};
use rustc_middle::ty::TypeckResults;
use rustc_span::Span;

pub struct LoopFinder<'tcx> {
    pub typeck_results: &'tcx TypeckResults<'tcx>,
    pub record: Vec<(Span, Vec<Span>)>,
    pub target_def_id: rustc_span::def_id::DefId,
}

pub struct PushFinder<'tcx> {
    typeck_results: &'tcx TypeckResults<'tcx>,
    record: Vec<Span>,
    target_def_id: rustc_span::def_id::DefId,
}

impl<'tcx> PushFinder<'tcx> {
    pub fn new(
        typeck_results: &'tcx TypeckResults<'tcx>,
        target_def_id: rustc_span::def_id::DefId,
    ) -> PushFinder<'tcx> {
        PushFinder {
            typeck_results,
            record: Vec::new(),
            target_def_id,
        }
    }

    pub fn into_record(self) -> Vec<Span> {
        self.record
    }
}

impl<'tcx> LoopFinder<'tcx> {
    pub fn new(
        typeck_results: &'tcx TypeckResults<'tcx>,
        target_def_id: rustc_span::def_id::DefId,
    ) -> LoopFinder<'tcx> {
        LoopFinder {
            typeck_results,
            record: Vec::new(),
            target_def_id,
        }
    }

    pub fn into_record(self) -> Vec<(Span, Vec<Span>)> {
        self.record
    }
}

impl<'tcx> intravisit::Visitor<'tcx> for PushFinder<'tcx> {
    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
        if let ExprKind::MethodCall(.., span) = ex.kind {
            let def_id = self
                .typeck_results
                .type_dependent_def_id(ex.hir_id)
                .unwrap();
            if def_id == self.target_def_id {
                self.record.push(span);
            }
        }
        intravisit::walk_expr(self, ex);
    }
}

impl<'tcx> intravisit::Visitor<'tcx> for LoopFinder<'tcx> {
    fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
        if let ExprKind::Loop(block, ..) = ex.kind {
            let mut push_finder = PushFinder::new(self.typeck_results, self.target_def_id);
            intravisit::walk_block(&mut push_finder, block);
            if push_finder.record.len() == 1 {
                self.record.push((ex.span, push_finder.into_record()));
            }
        }
        intravisit::walk_expr(self, ex);
    }
}