Skip to main content

rapx/check/opt/checking/bounds_checking/
bounds_loop_push.rs

1use once_cell::sync::OnceCell;
2
3use rustc_hir::intravisit;
4use rustc_middle::ty::TyCtxt;
5use rustc_span::Span;
6
7use crate::analysis::dataflow::Graph;
8use crate::helpers::def_path::DefPath;
9use crate::utils::span::{
10    span_to_first_line, span_to_trimmed_span,
11};
12
13use annotate_snippets::Level;
14
15use crate::check::opt::report::OptReport;
16
17use super::super::super::LEVEL;
18use super::super::super::NO_STD;
19use super::super::super::loop_visitors::LoopFinder;
20static DEFPATHS: OnceCell<DefPaths> = OnceCell::new();
21
22struct DefPaths {
23    vec_push: DefPath,
24}
25
26impl DefPaths {
27    pub fn new(tcx: &TyCtxt<'_>) -> Self {
28        let no_std = NO_STD.lock().unwrap();
29        if *no_std {
30            Self {
31                vec_push: DefPath::new("alloc::vec::Vec::push", tcx),
32            }
33        } else {
34            Self {
35                vec_push: DefPath::new("std::vec::Vec::push", tcx),
36            }
37        }
38    }
39}
40
41use crate::check::opt::OptCheck;
42
43pub struct BoundsLoopPushCheck {
44    pub record: Vec<(Span, Vec<Span>)>,
45}
46
47impl OptCheck for BoundsLoopPushCheck {
48    fn new() -> Self {
49        Self { record: Vec::new() }
50    }
51
52    fn check(&mut self, graph: &Graph, tcx: &TyCtxt) {
53        let def_paths = &DEFPATHS.get_or_init(|| DefPaths::new(tcx));
54        let level = LEVEL.lock().unwrap();
55        if *level == 2 {
56            let def_id = graph.def_id;
57            let body = tcx.hir_body_owned_by(def_id.as_local().unwrap());
58            let typeck_results = tcx.typeck(def_id.as_local().unwrap());
59            let target_def_id = def_paths.vec_push.last_def_id();
60            let mut loop_finder = LoopFinder::new(typeck_results, target_def_id);
61            intravisit::walk_body(&mut loop_finder, body);
62            self.record = loop_finder.into_record();
63        }
64    }
65
66    fn report(&self, _: &Graph) {
67        for (loop_span, push_record) in self.record.iter() {
68            report_loop_push_bug(*loop_span, push_record);
69        }
70    }
71
72    fn cnt(&self) -> usize {
73        self.record.iter().map(|(_, spans)| spans.len()).sum()
74    }
75}
76
77fn report_loop_push_bug(loop_span: Span, push_record: &Vec<Span>) {
78    let trimmed = span_to_trimmed_span(span_to_first_line(loop_span));
79    let mut report = OptReport::new(loop_span, loop_span)
80        .title("Unnecessary bounds checkings detected")
81        .annotate(Level::Info, trimmed, "A loop operation.");
82    for push_span in push_record {
83        report = report.annotate(Level::Error, *push_span, "Push happens here.");
84    }
85    report.emit();
86}