rapx/check/opt/data_collection/reallocation/
unreserved_hash.rs1use crate::{analysis::dataflow::*, check::opt::OptCheck};
2use rustc_middle::{mir::Local, ty::TyCtxt};
3
4use annotate_snippets::Level;
5use rustc_span::Span;
6
7use crate::check::opt::check_utils::node_matches_call;
8use crate::check::opt::report::OptReport;
9
10crate::def_paths! {
11 hashset_insert: "std::collections::HashSet::insert",
12 hashmap_insert: "std::collections::HashMap::insert",
13 hashset_new: "std::collections::HashSet::new",
14 hashmap_new: "std::collections::HashMap::new",
15 entry: "std::collections::HashMap::entry",
16}
17
18pub struct UnreservedHashCheck {
19 record: Vec<(Span, Span)>,
20}
21
22fn find_downside_hash_insert_node(graph: &Graph, node_idx: Local) -> Option<Local> {
23 let def_paths = &DEFPATHS.get().unwrap();
24 graph.find_first_node(
25 node_idx,
26 Direction::Downside,
27 &mut |graph: &Graph, idx: Local| {
28 let node = &graph.nodes[idx];
29 for op in node.ops.iter() {
30 if let NodeOp::Call(def_id) = op {
31 if *def_id == def_paths.hashmap_insert.last_def_id()
32 || *def_id == def_paths.hashset_insert.last_def_id()
33 || *def_id == def_paths.entry.last_def_id()
34 {
35 return true;
36 }
37 }
38 }
39 false
40 },
41 &mut Graph::equivalent_edge_validator,
42 )
43}
44
45impl OptCheck for UnreservedHashCheck {
46 fn new() -> Self {
47 Self { record: Vec::new() }
48 }
49
50 fn check(&mut self, graph: &Graph, tcx: &TyCtxt) {
51 let def_paths = &DEFPATHS.get_or_init(|| DefPaths::new(tcx));
52 for (node_idx, node) in graph.nodes.iter_enumerated() {
53 if node_matches_call(
54 node,
55 &[
56 def_paths.hashmap_new.last_def_id(),
57 def_paths.hashset_new.last_def_id(),
58 ],
59 ) {
60 if let Some(insert_idx) = find_downside_hash_insert_node(graph, node_idx) {
61 let insert_node = &graph.nodes[insert_idx];
62 self.record.push((node.span, insert_node.span));
63 }
64 }
65 }
66 }
67
68 fn report(&self, graph: &Graph) {
69 for (hash_span, insert_span) in self.record.iter() {
70 report_unreserved_hash_bug(graph, *hash_span, *insert_span);
71 }
72 }
73
74 fn cnt(&self) -> usize {
75 self.record.len()
76 }
77}
78
79fn report_unreserved_hash_bug(graph: &Graph, hash_span: Span, insert_span: Span) {
80 OptReport::from_graph(graph)
81 .file_name(hash_span)
82 .title("Improper data collection detected")
83 .annotate(Level::Error, hash_span, "Space unreserved.")
84 .annotate(Level::Info, insert_span, "Insertion happens here.")
85 .footer("Reserve enough space.")
86 .emit();
87}