rapx/analysis/opt/data_collection/suboptimal/
participant.rs

1use annotate_snippets::{Level, Renderer, Snippet};
2
3use once_cell::sync::OnceCell;
4
5use rustc_hir::def_id::DefId;
6use rustc_middle::ty::TyCtxt;
7use rustc_span::Span;
8
9use crate::analysis::core::dataflow::graph::{Graph, NodeOp};
10use crate::analysis::opt::OptCheck;
11use crate::analysis::utils::def_path::DefPath;
12
13use crate::utils::log::{
14    relative_pos_range, span_to_filename, span_to_line_number, span_to_source_code,
15};
16
17static DEFPATHS: OnceCell<DefPaths> = OnceCell::new();
18struct DefPaths {
19    hashset_new: DefPath,
20    hashset_with_capacity: DefPath,
21    hashmap_new: DefPath,
22    hashmap_with_capacity: DefPath,
23    btreeset_new: DefPath,
24    btreemap_new: DefPath,
25}
26
27impl DefPaths {
28    pub fn new(tcx: &TyCtxt<'_>) -> Self {
29        Self {
30            hashset_new: DefPath::new("std::collections::HashSet::new", tcx),
31            hashset_with_capacity: DefPath::new("std::collections::HashSet::with_capacity", tcx),
32            hashmap_new: DefPath::new("std::collections::HashMap::new", tcx),
33            hashmap_with_capacity: DefPath::new("std::collections::HashMap::with_capacity", tcx),
34            btreeset_new: DefPath::new("std::collections::BTreeSet::new", tcx),
35            btreemap_new: DefPath::new("std::collections::BTreeMap::new", tcx),
36        }
37    }
38
39    fn has_id(&self, id: DefId) -> bool {
40        id == self.hashset_new.last_def_id()
41            || id == self.hashmap_new.last_def_id()
42            || id == self.btreemap_new.last_def_id()
43            || id == self.btreeset_new.last_def_id()
44            || id == self.hashmap_with_capacity.last_def_id()
45            || id == self.hashset_with_capacity.last_def_id()
46    }
47}
48
49pub struct ParticipantCheck {
50    record: Vec<Span>, //Can split into 4 categories
51}
52
53impl OptCheck for ParticipantCheck {
54    fn new() -> Self {
55        Self { record: vec![] }
56    }
57
58    fn check(&mut self, graph: &Graph, tcx: &TyCtxt) {
59        let def_paths = &DEFPATHS.get_or_init(|| DefPaths::new(tcx));
60        for node in graph.nodes.iter() {
61            for op in node.ops.iter() {
62                if let NodeOp::Call(def_id) = op {
63                    if def_paths.has_id(*def_id) {
64                        self.record.push(node.span);
65                    }
66                }
67            }
68        }
69    }
70
71    fn report(&self, graph: &Graph) {
72        for span in self.record.iter() {
73            report_participant(graph, *span);
74        }
75    }
76
77    fn cnt(&self) -> usize {
78        self.record.len()
79    }
80}
81
82fn report_participant(graph: &Graph, span: Span) {
83    let code_source = span_to_source_code(graph.span);
84    let filename = span_to_filename(span);
85    let snippet = Snippet::source(&code_source)
86        .line_start(span_to_line_number(graph.span))
87        .origin(&filename)
88        .fold(true)
89        .annotation(
90            Level::Error
91                .span(relative_pos_range(graph.span, span))
92                .label("Data collection created here"),
93        );
94    let message = Level::Warning
95        .title("Suboptimal data collection detected")
96        .snippet(snippet)
97        .footer(Level::Help.title("Use faster data collection or hash operators instead. Static container is also a choice"));
98    let renderer = Renderer::styled();
99    println!("{}", renderer.render(message));
100}