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