Skip to main content

rapx/check/opt/data_collection/reallocation/
unreserved_vec.rs

1use crate::{analysis::dataflow::*, check::opt::OptCheck};
2use rustc_hir::intravisit;
3use rustc_middle::mir::Local;
4use rustc_middle::ty::TyCtxt;
5
6use annotate_snippets::Level;
7use rustc_span::Span;
8
9use crate::check::opt::check_utils::node_matches_call;
10use crate::check::opt::report::OptReport;
11
12use super::super::super::LEVEL;
13use super::super::super::loop_visitors::LoopFinder;
14
15crate::def_paths! {
16    vec_new: "std::vec::Vec::new",
17    vec_push: "std::vec::Vec::push",
18    vec_with_capacity: "std::vec::Vec::with_capacity",
19    vec_reserve: "std::vec::Vec::reserve",
20}
21
22pub struct UnreservedVecCheck {
23    record: Vec<Span>,
24}
25
26fn find_upside_reservation(graph: &Graph, node_idx: Local) -> Option<Local> {
27    let def_paths = &DEFPATHS.get().unwrap();
28    graph.find_first_node(
29        node_idx,
30        Direction::Upside,
31        &mut |graph: &Graph, idx: Local| {
32            let node = &graph.nodes[idx];
33            for op in node.ops.iter() {
34                if let NodeOp::Call(def_id) = op {
35                    if *def_id == def_paths.vec_with_capacity.last_def_id()
36                        || *def_id == def_paths.vec_reserve.last_def_id()
37                    {
38                        return true;
39                    }
40                }
41            }
42            false
43        },
44        &mut Graph::equivalent_edge_validator,
45    )
46}
47
48impl OptCheck for UnreservedVecCheck {
49    fn new() -> Self {
50        Self { record: Vec::new() }
51    }
52
53    fn check(&mut self, graph: &Graph, tcx: &TyCtxt) {
54        let def_paths = &DEFPATHS.get_or_init(|| DefPaths::new(tcx));
55        let level = LEVEL.lock().unwrap();
56        if *level == 2 {
57            for (node_idx, node) in graph.nodes.iter_enumerated() {
58                if node_matches_call(node, &[def_paths.vec_new.last_def_id()]) {
59                    self.record.push(node.span);
60                }
61                if node_matches_call(node, &[def_paths.vec_push.last_def_id()]) {
62                    if find_upside_reservation(graph, node_idx).is_none() {
63                        self.record.push(node.span);
64                    }
65                }
66            }
67        }
68
69        let def_id = graph.def_id;
70        let body = tcx.hir_body_owned_by(def_id.as_local().unwrap());
71        let typeck_results = tcx.typeck(def_id.as_local().unwrap());
72        let target_def_id = def_paths.vec_push.last_def_id();
73        let mut loop_finder = LoopFinder::new(typeck_results, target_def_id);
74        intravisit::walk_body(&mut loop_finder, body);
75        for (_, push_record) in loop_finder.into_record() {
76            for push_span in push_record {
77                if let Some((node_idx, _)) = graph.query_node_by_span(push_span, false) {
78                    if find_upside_reservation(graph, node_idx).is_none() {
79                        self.record.push(push_span);
80                    }
81                }
82            }
83        }
84    }
85
86    fn report(&self, graph: &Graph) {
87        for span in self.record.iter() {
88            report_unreserved_vec_bug(graph, *span);
89        }
90    }
91
92    fn cnt(&self) -> usize {
93        self.record.len()
94    }
95}
96
97fn report_unreserved_vec_bug(graph: &Graph, span: Span) {
98    OptReport::from_graph(graph)
99        .file_name(span)
100        .title("Improper data collection detected")
101        .annotate(Level::Error, span, "Space unreserved.")
102        .footer("Reserve enough space.")
103        .emit();
104}