Skip to main content

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

1use 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}