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

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