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