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