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

1use annotate_snippets::{Level, Renderer, Snippet};
2
3use once_cell::sync::OnceCell;
4
5use rustc_middle::ty::TyCtxt;
6use rustc_span::Span;
7
8use crate::analysis::core::dataflow::graph::GraphNode;
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
17struct DefPaths {
18    vec_remove: DefPath,
19    vec_insert: DefPath,
20}
21
22static DEFPATHS: OnceCell<DefPaths> = OnceCell::new();
23
24impl DefPaths {
25    fn new(tcx: &TyCtxt<'_>) -> Self {
26        Self {
27            vec_remove: DefPath::new("std::vec::Vec::remove", tcx),
28            vec_insert: DefPath::new("std::vec::Vec::insert", tcx),
29        }
30    }
31}
32
33pub struct VecRemoveCheck {
34    record: Vec<Span>,
35}
36
37fn is_vec_insert_or_remove(node: &GraphNode) -> bool {
38    let def_paths = DEFPATHS.get().unwrap();
39    for op in node.ops.iter() {
40        if let NodeOp::Call(def_id) = op {
41            if *def_id == def_paths.vec_remove.last_def_id()
42                || *def_id == def_paths.vec_insert.last_def_id()
43            {
44                return true;
45            }
46        }
47    }
48    false
49}
50
51fn is_0_usize(node: &GraphNode) -> bool {
52    for op in node.ops.iter() {
53        if let NodeOp::Const(desc, _) = op {
54            if desc.eq("0_usize") {
55                return true;
56            }
57        }
58    }
59    false
60}
61
62impl OptCheck for VecRemoveCheck {
63    fn new() -> Self {
64        Self { record: vec![] }
65    }
66
67    fn check(&mut self, graph: &Graph, tcx: &TyCtxt) {
68        let _ = &DEFPATHS.get_or_init(|| DefPaths::new(tcx));
69        for node in graph.nodes.iter() {
70            if is_vec_insert_or_remove(node) {
71                let index_edge = &graph.edges[node.in_edges[1]];
72                let index_node = &graph.nodes[index_edge.src];
73                if is_0_usize(index_node) {
74                    self.record.push(node.span);
75                }
76            }
77        }
78    }
79
80    fn report(&self, graph: &Graph) {
81        for span in self.record.iter() {
82            report_vec_remove_bug(graph, *span);
83        }
84    }
85
86    fn cnt(&self) -> usize {
87        self.record.len()
88    }
89}
90
91fn report_vec_remove_bug(graph: &Graph, span: Span) {
92    let code_source = span_to_source_code(graph.span);
93    let filename = span_to_filename(graph.span);
94    let snippet = Snippet::source(&code_source)
95        .line_start(span_to_line_number(graph.span))
96        .origin(&filename)
97        .fold(true)
98        .annotation(
99            Level::Error
100                .span(relative_pos_range(graph.span, span))
101                .label("Vec increasement / decreasement happens here."),
102        );
103    let message = Level::Warning
104        .title("Improper data collection detected")
105        .snippet(snippet)
106        .footer(Level::Help.title("Use VecQueue instead of Vec."));
107    let renderer = Renderer::styled();
108    println!("{}", renderer.render(message));
109}