rapx/analysis/opt/checking/bounds_checking/
bounds_extend.rs

1use once_cell::sync::OnceCell;
2
3use rustc_middle::ty::TyCtxt;
4use rustc_span::Span;
5
6use crate::{
7    analysis::{
8        core::dataflow::{graph::*, *},
9        utils::def_path::DefPath,
10    },
11    utils::log::{relative_pos_range, span_to_filename, span_to_line_number, span_to_source_code},
12};
13use annotate_snippets::{Level, Renderer, Snippet};
14
15use super::super::super::LEVEL;
16use super::super::super::NO_STD;
17use crate::analysis::opt::OptCheck;
18static DEFPATHS: OnceCell<DefPaths> = OnceCell::new();
19
20struct DefPaths {
21    vec_extend_from_slice: DefPath,
22}
23
24impl DefPaths {
25    pub fn new(tcx: &TyCtxt<'_>) -> Self {
26        let no_std = NO_STD.lock().unwrap();
27        if *no_std {
28            Self {
29                vec_extend_from_slice: DefPath::new("alloc::vec::Vec::extend_from_slice", &tcx),
30            }
31        } else {
32            Self {
33                vec_extend_from_slice: DefPath::new("std::vec::Vec::extend_from_slice", &tcx),
34            }
35        }
36    }
37}
38
39pub struct BoundsExtendCheck {
40    pub record: Vec<Span>,
41}
42
43fn is_extend_from_slice(node: &GraphNode) -> bool {
44    let def_paths = DEFPATHS.get().unwrap();
45    for op in node.ops.iter() {
46        if let NodeOp::Call(def_id) = op {
47            if *def_id == def_paths.vec_extend_from_slice.last_def_id() {
48                return true;
49            }
50        }
51    }
52    false
53}
54
55impl OptCheck for BoundsExtendCheck {
56    fn new() -> Self {
57        Self { record: Vec::new() }
58    }
59
60    fn check(&mut self, graph: &Graph, tcx: &TyCtxt) {
61        let level = LEVEL.lock().unwrap();
62        if *level <= 1 {
63            return;
64        }
65        let _ = &DEFPATHS.get_or_init(|| DefPaths::new(tcx));
66        for node in graph.nodes.iter() {
67            if is_extend_from_slice(node) {
68                self.record.push(node.span);
69            }
70        }
71    }
72
73    fn report(&self, graph: &Graph) {
74        for span in self.record.iter() {
75            report_extend_bug(graph, *span);
76        }
77    }
78
79    fn cnt(&self) -> usize {
80        self.record.len()
81    }
82}
83
84fn report_extend_bug(graph: &Graph, span: Span) {
85    let code_source = span_to_source_code(graph.span);
86    let filename = span_to_filename(graph.span);
87    let snippet = Snippet::source(&code_source)
88        .line_start(span_to_line_number(graph.span))
89        .origin(&filename)
90        .fold(true)
91        .annotation(
92            Level::Error
93                .span(relative_pos_range(graph.span, span))
94                .label("Checked here."),
95        );
96    let message = Level::Warning
97        .title("Unnecessary bound checkings detected")
98        .snippet(snippet)
99        .footer(Level::Help.title("Manipulate memory directly."));
100    let renderer = Renderer::styled();
101    println!("{}", renderer.render(message));
102}