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 rap_debug!(
54 "fn_sig: {}",
55 self.tcx.type_of(fn_did).instantiate_identity()
56 );
57 rap_debug!(
58 "visibility: {:?}",
59 self.tcx
60 .effective_visibilities(())
61 .effective_vis(fn_did.as_local().unwrap())
62 .unwrap()
63 );
64
65 if !is_api_public(fn_did, self.tcx) {
66 rap_debug!("skip for not public API");
67 return;
68 }
69
70 let is_generic = self
71 .tcx
72 .generics_of(fn_did)
73 .requires_monomorphization(self.tcx);
74 let fn_sig = self.tcx.fn_sig(fn_did);
75 rap_debug!("fn_sig: {}", fn_sig.instantiate_identity());
76 for input in fn_sig.instantiate_identity().inputs_and_output().iter() {
77 rap_debug!("param: {:?}", input);
78 let input_ty = input.skip_binder();
79 if let TyKind::Ref(r, ty, _) = input.skip_binder().kind() {
80 rap_debug!("region kind: {:?} {:?}", r.type_flags(), r.kind());
81 match r.kind() {
82 ty::ReEarlyParam(re) => {
83 rap_debug!("ReEarlyParam: {:?}", re);
84 }
85 ty::ReBound(idx, bound) => {
86 rap_debug!("ReBound: {:?} {:?}", idx, bound);
87 }
88 _ => {}
89 }
90 }
91 }
92
93 rap_debug!("type(debug): {:?}", self.tcx.type_of(fn_did));
94 rap_debug!("fn_sig(debug): {:?}", fn_sig);
95 let late_fn_sig = self
96 .tcx
97 .liberate_late_bound_regions(fn_did, fn_sig.instantiate_identity());
98 rap_debug!("late_fn_sig: {:?}", late_fn_sig);
99
100 if is_generic {
101 self.stats.pub_generic_api.insert(fn_did);
102 } else {
103 self.stats.pub_non_generic_api.insert(fn_did);
104 }
105
106 if fk.header().map_or(false, |header| header.is_unsafe()) {
107 self.stats.pub_unsafe_api.insert(fn_did);
108 }
109 }
110}
111
112impl<'tcx> Visitor<'tcx> for FnVisitor<'tcx> {
113 type NestedFilter = nested_filter::OnlyBodies;
114
115 fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
116 self.tcx
117 }
118
119 fn visit_fn(
120 &mut self,
121 fk: FnKind<'tcx>,
122 fd: &'tcx FnDecl<'tcx>,
123 b: BodyId,
124 span: Span,
125 id: LocalDefId,
126 ) -> Self::Result {
127 self.work_at_fn(fk, fd, b, span, id);
128 walk_fn(self, fk, fd, b, id);
129 }
130
131 fn visit_block(&mut self, b: &'tcx rustc_hir::Block<'tcx>) -> Self::Result {
132 let r = b.rules;
133 if matches!(r, rustc_hir::BlockCheckMode::UnsafeBlock(_)) {
134 self.stats.unsafe_block.push(*b)
135 }
136 walk_block(self, b);
137 }
138}