rapx/check/opt/data_collection/reallocation/
unreserved_vec.rs1use 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}