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