rapx/check/opt/checking/bounds_checking/
bounds_loop_push.rs1use 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}