rapx/analysis/scan/
visitor.rs

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