rapx/verify/slicer/
call_visit.rs1use crate::compat::{FxHashMap, Spanned};
9use rustc_hir::def_id::DefId;
10use rustc_middle::mir::{BasicBlock, Body, Operand, Place};
11use rustc_middle::ty::{TyCtxt, TyKind};
12
13use crate::analysis::dataflow::types::DataflowGraph;
14
15use super::super::{
16 call_summary,
17 def_use::{PlaceKey, RelevantPlaces, call_args_uses_at, operand_uses},
18};
19
20use super::types::RelevantItem;
21
22pub(crate) fn visit<'tcx>(
24 tcx: TyCtxt<'tcx>,
25 def_id: DefId,
26 block: BasicBlock,
27 func: &Operand<'tcx>,
28 args: &[Spanned<Operand<'tcx>>],
29 destination: &Place<'tcx>,
30 flow: &DataflowGraph,
31 body: &Body<'tcx>,
32 relevant: &mut RelevantPlaces,
33 items: &mut Vec<RelevantItem<'tcx>>,
34) {
35 let mut defs = RelevantPlaces::new();
36 defs.insert_mir_place(destination);
37
38 let tpos = body.basic_blocks[block].statements.len();
39 let mut arg_uses = RelevantPlaces::new();
40 for &edge_idx in &flow.node(destination.local).in_edges {
41 let edge = &flow.edges[edge_idx];
42 if edge.block == block.as_usize() && edge.statement_index == tpos {
43 arg_uses.insert_local(edge.src);
44 }
45 }
46
47 let summary = call_summary::dependency_summary(tcx, func, args.len(), &call_context_from_args(args));
48
49 if defs.intersects(relevant) {
50 if summary.unsupported {
51 items.push(RelevantItem::UnknownCall);
52 }
53 items.push(RelevantItem::Terminator { def_id, block, switch_succ: None });
54 relevant.remove_all(&defs);
55 relevant.extend(call_args_uses_at(args, &summary.return_depends_on_args));
56 return;
57 }
58
59 let dest_is_ptr = matches!(
67 body.local_decls[destination.local].ty.kind(),
68 TyKind::RawPtr(..) | TyKind::Ref(..)
69 );
70 if dest_is_ptr && summary.must_write_args.is_empty() {
71 if summary.unsupported {
72 items.push(RelevantItem::UnknownCall);
73 }
74 items.push(RelevantItem::Terminator { def_id, block, switch_succ: None });
75 relevant.remove_all(&defs);
76 relevant.extend(call_args_uses_at(args, &summary.return_depends_on_args));
77 return;
78 }
79
80 let relevant_written_arg = summary.must_write_args.iter().any(|index| {
81 args.get(*index)
82 .is_some_and(|arg| operand_uses(&arg.node).intersects(relevant))
83 });
84 let summarized_write = !summary.must_write_args.is_empty();
85 if relevant_written_arg
86 || summarized_write
87 || (summary.unsupported && arg_uses.intersects(relevant))
88 {
89 if summary.unsupported {
90 items.push(RelevantItem::UnknownCall);
91 }
92 items.push(RelevantItem::Terminator { def_id, block, switch_succ: None });
93 relevant.extend(call_args_uses_at(args, &summary.must_write_args));
94 }
95
96 if !relevant.need_len.is_empty() {
101 let callee = crate::helpers::mir_utils::dep_callee_def_id(func);
102 if crate::verify::api_classify::is_len(callee) {
103 if let Some(first) = args.first() {
104 let arg_place = crate::helpers::mir_utils::operand_place(&first.node);
105 if let Some(arg_key) = arg_place {
106 let matches = relevant.need_len.contains(&arg_key)
107 || relevant.need_len.iter().any(|nl| {
108 crate::verify::def_use::trace_place_origin(flow, nl)
109 == crate::verify::def_use::trace_place_origin(flow, &arg_key)
110 });
111 if matches {
112 let dest_key = PlaceKey::from_mir_place(destination);
113 if relevant.places.insert(dest_key.clone()) {
114 relevant.just_added.insert(dest_key.clone());
115 }
116 if let Some(local) = dest_key.local() {
117 relevant.locals.insert(local);
118 }
119 items.push(RelevantItem::Terminator { def_id, block, switch_succ: None });
120 }
121 }
122 }
123 }
124 }
125}
126
127fn call_context_from_args(args: &[Spanned<Operand<'_>>]) -> call_summary::CallContext {
131 let mut concrete = FxHashMap::default();
132 for (i, arg) in args.iter().enumerate() {
133 if let Some(v) = crate::helpers::mir_utils::operand_const_u64(&arg.node) {
134 concrete.insert(i, v as i128);
135 }
136 }
137 call_summary::CallContext { concrete }
138}