Skip to main content

rapx/analysis/scan/
visitor.rs

1use 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}