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