rapx/analysis/scan/
visitor.rs1use super::statistic::Statistics;
2
3use rustc_hir::{
4 BodyId, FnDecl,
5 def_id::{DefId, LocalDefId},
6 intravisit::{FnKind, Visitor, walk_block, walk_fn},
7};
8use rustc_middle::{
9 hir::nested_filter,
10 ty::{self, TyCtxt, TyKind},
11};
12use rustc_span::Span;
13
14pub struct FnVisitor<'tcx> {
15 tcx: TyCtxt<'tcx>,
16 stats: Statistics<'tcx>,
17}
18
19fn is_api_public(fn_def_id: impl Into<DefId>, tcx: TyCtxt<'_>) -> bool {
20 let fn_def_id: DefId = fn_def_id.into();
21 let local_id = fn_def_id.expect_local();
22 rap_trace!(
23 "vis: {:?} (path: {}) => {:?}",
24 fn_def_id,
25 tcx.def_path_str(fn_def_id),
26 tcx.effective_visibilities(()).effective_vis(local_id)
27 );
28 tcx.effective_visibilities(()).is_directly_public(local_id)
29 || tcx.effective_visibilities(()).is_exported(local_id)
30}
31
32impl<'tcx> FnVisitor<'tcx> {
33 pub fn new(tcx: TyCtxt<'tcx>) -> FnVisitor<'tcx> {
34 FnVisitor {
35 tcx,
36 stats: Statistics::default(),
37 }
38 }
39 pub fn statistic(self) -> Statistics<'tcx> {
40 self.stats
41 }
42 fn work_at_fn<'v>(
43 &mut self,
44 fk: FnKind<'v>,
45 _fd: &'v FnDecl<'v>,
46 _b: BodyId,
47 _span: Span,
48 id: LocalDefId,
49 ) {
50 let fn_did = id.to_def_id();
51 rap_debug!("API path: {}", self.tcx.def_path_str(fn_did));
52 #[cfg(not(rapx_ge_99))]
53 #[cfg(not(rapx_ge_99))]
54 rap_debug!(
55 "fn_sig: {}",
56 self.tcx.type_of(fn_did).instantiate_identity()
57 );
58 #[cfg(rapx_ge_99)]
59 rap_debug!(
60 "fn_sig: {}",
61 self.tcx
62 .type_of(fn_did)
63 .instantiate_identity()
64 .skip_norm_wip()
65 );
66 rap_debug!(
67 "visibility: {:?}",
68 self.tcx
69 .effective_visibilities(())
70 .effective_vis(fn_did.as_local().unwrap())
71 .unwrap()
72 );
73
74 if !is_api_public(fn_did, self.tcx) {
75 rap_debug!("skip for not public API");
76 return;
77 }
78
79 let is_generic = self
80 .tcx
81 .generics_of(fn_did)
82 .requires_monomorphization(self.tcx);
83 let fn_sig = self.tcx.fn_sig(fn_did);
84 #[cfg(not(rapx_ge_99))]
85 rap_debug!("fn_sig: {}", fn_sig.instantiate_identity());
86 #[cfg(rapx_ge_99)]
87 rap_debug!("fn_sig: {:?}", fn_sig);
88 let inst_fn_sig = fn_sig.instantiate_identity();
89 #[cfg(rapx_ge_99)]
90 let inst_fn_sig = inst_fn_sig.skip_norm_wip();
91 let inputs = inst_fn_sig.inputs_and_output();
92 for input in inputs.iter() {
93 rap_debug!("param: {:?}", input);
94 if let TyKind::Ref(r, _ty, _) = input.skip_binder().kind() {
95 rap_debug!("region kind: {:?} {:?}", r.type_flags(), r.kind());
96 match r.kind() {
97 ty::ReEarlyParam(re) => {
98 rap_debug!("ReEarlyParam: {:?}", re);
99 }
100 ty::ReBound(idx, bound) => {
101 rap_debug!("ReBound: {:?} {:?}", idx, bound);
102 }
103 _ => {}
104 }
105 }
106 }
107
108 #[cfg(not(rapx_ge_99))]
109 rap_debug!("type(debug): {:?}", self.tcx.type_of(fn_did));
110 #[cfg(rapx_ge_99)]
111 rap_debug!(
112 "type(debug): {:?}",
113 self.tcx
114 .type_of(fn_did)
115 .instantiate_identity()
116 .skip_norm_wip()
117 );
118 rap_debug!("fn_sig(debug): {:?}", fn_sig);
119 let late_fn_sig = self.tcx.liberate_late_bound_regions(fn_did, inst_fn_sig);
120 rap_debug!("late_fn_sig: {:?}", late_fn_sig);
121
122 if is_generic {
123 self.stats.pub_generic_api.insert(fn_did);
124 } else {
125 self.stats.pub_non_generic_api.insert(fn_did);
126 }
127
128 if fk.header().map_or(false, |header| header.is_unsafe()) {
129 self.stats.pub_unsafe_api.insert(fn_did);
130 }
131 }
132}
133
134impl<'tcx> Visitor<'tcx> for FnVisitor<'tcx> {
135 type NestedFilter = nested_filter::OnlyBodies;
136
137 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
138 self.tcx
139 }
140
141 fn visit_fn(
142 &mut self,
143 fk: FnKind<'tcx>,
144 fd: &'tcx FnDecl<'tcx>,
145 b: BodyId,
146 span: Span,
147 id: LocalDefId,
148 ) -> Self::Result {
149 self.work_at_fn(fk, fd, b, span, id);
150 walk_fn(self, fk, fd, b, id);
151 }
152
153 fn visit_block(&mut self, b: &'tcx rustc_hir::Block<'tcx>) -> Self::Result {
154 let r = b.rules;
155 if matches!(r, rustc_hir::BlockCheckMode::UnsafeBlock(_)) {
156 self.stats.unsafe_block.push(*b)
157 }
158 walk_block(self, b);
159 }
160}