rapx/analysis/opt/checking/bounds_checking/
bounds_extend.rs1use 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}