Skip to main content

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

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