rapx/analysis/opt/data_collection/suboptimal/
slice_contains.rs1use annotate_snippets::{Level, Renderer, Snippet};
2
3use once_cell::sync::OnceCell;
4
5use rustc_hir::{intravisit, Expr, ExprKind};
6use rustc_middle::ty::TyCtxt;
7use rustc_middle::ty::TypeckResults;
8use rustc_span::Span;
9
10use crate::analysis::core::dataflow::graph::Graph;
11use crate::analysis::opt::OptCheck;
12use crate::analysis::utils::def_path::DefPath;
13
14use crate::utils::log::{
15 relative_pos_range, span_to_filename, span_to_line_number, span_to_source_code,
16};
17
18struct DefPaths {
19 slice_contains: DefPath,
20}
21
22static DEFPATHS: OnceCell<DefPaths> = OnceCell::new();
23
24impl DefPaths {
25 pub fn new(tcx: &TyCtxt<'_>) -> Self {
26 Self {
27 slice_contains: DefPath::new("slice::contains", tcx),
28 }
29 }
30}
31
32struct ContainsFinder<'tcx> {
33 typeck_results: &'tcx TypeckResults<'tcx>,
34 record: Vec<Span>,
35}
36
37impl<'tcx> intravisit::Visitor<'tcx> for ContainsFinder<'tcx> {
38 fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
39 if let ExprKind::MethodCall(.., span) = ex.kind {
40 let def_id = self
41 .typeck_results
42 .type_dependent_def_id(ex.hir_id)
43 .unwrap();
44 let target_def_id = (&DEFPATHS.get().unwrap()).slice_contains.last_def_id();
45 if def_id == target_def_id {
46 self.record.push(span);
47 }
48 }
49 intravisit::walk_expr(self, ex);
50 }
51}
52
53pub struct SliceContainsCheck {
54 record: Vec<Span>,
55}
56
57impl OptCheck for SliceContainsCheck {
58 fn new() -> Self {
59 Self { record: Vec::new() }
60 }
61
62 fn check(&mut self, graph: &Graph, tcx: &TyCtxt) {
63 let _ = &DEFPATHS.get_or_init(|| DefPaths::new(tcx));
64 let def_id = graph.def_id;
65 let body = tcx.hir_body_owned_by(def_id.as_local().unwrap());
66 let typeck_results = tcx.typeck(def_id.as_local().unwrap());
67 let mut contains_finder = ContainsFinder {
68 typeck_results,
69 record: Vec::new(),
70 };
71 intravisit::walk_body(&mut contains_finder, body);
72 self.record = contains_finder.record;
73 }
74
75 fn report(&self, graph: &Graph) {
76 for contains_span in self.record.iter() {
77 report_slice_contains_bug(graph, *contains_span);
78 }
79 }
80
81 fn cnt(&self) -> usize {
82 self.record.len()
83 }
84}
85
86fn report_slice_contains_bug(graph: &Graph, contains_span: Span) {
87 let code_source = span_to_source_code(graph.span);
88 let filename = span_to_filename(graph.span);
89 let snippet = Snippet::source(&code_source)
90 .line_start(span_to_line_number(graph.span))
91 .origin(&filename)
92 .fold(true)
93 .annotation(
94 Level::Error
95 .span(relative_pos_range(graph.span, contains_span))
96 .label("Slice contains happens here."),
97 );
98 let message = Level::Warning
99 .title("Improper data collection detected")
100 .snippet(snippet)
101 .footer(Level::Help.title("Use Set instead of Slice."));
102 let renderer = Renderer::styled();
103 println!("{}", renderer.render(message));
104}