rapx/analysis/scan/
visitor.rs

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