rapx/analysis/core/api_dependency/
visit.rs

1use super::graph::ApiDependencyGraph;
2use super::graph::{DepEdge, DepNode};
3use super::is_def_id_public;
4use crate::analysis::core::api_dependency::mono;
5use crate::{rap_debug, rap_trace};
6use rustc_hir::LangItem;
7use rustc_hir::{
8    BodyId, BodyOwnerKind, FnDecl,
9    def_id::{DefId, LocalDefId},
10    intravisit::{FnKind, Visitor},
11};
12use rustc_middle::ty::{self, FnSig, ParamEnv, Ty, TyCtxt, TyKind};
13use rustc_span::Span;
14use std::io::Write;
15
16#[derive(Clone, Copy, Debug, Eq, PartialEq, PartialOrd, Hash)]
17pub struct Config {
18    pub ignore_const_generic: bool,
19    pub include_unsafe: bool,
20    pub include_drop: bool,
21    pub include_generic: bool,
22    pub pub_only: bool,
23}
24
25impl Default for Config {
26    fn default() -> Self {
27        Config {
28            pub_only: true,
29            ignore_const_generic: true,
30            include_unsafe: false,
31            include_drop: false,
32            include_generic: true,
33        }
34    }
35}
36
37pub struct FnVisitor<'tcx> {
38    tcx: TyCtxt<'tcx>,
39    apis: Vec<DefId>,
40    generic_apis: Vec<DefId>,
41    config: Config,
42}
43
44impl<'tcx> FnVisitor<'tcx> {
45    pub fn new(config: Config, tcx: TyCtxt<'tcx>) -> FnVisitor<'tcx> {
46        FnVisitor {
47            tcx,
48            apis: Vec::new(),
49            generic_apis: Vec::new(),
50            config,
51        }
52    }
53
54    pub fn count_api(&self) -> usize {
55        self.apis.len()
56    }
57
58    pub fn count_generic_api(&self) -> usize {
59        self.generic_apis.len()
60    }
61
62    pub fn non_generic_apis(&self) -> &[DefId] {
63        &self.apis
64    }
65
66    pub fn generic_apis(&self) -> &[DefId] {
67        &self.generic_apis
68    }
69
70    pub fn write_funcs<T: Write>(&self, f: &mut T) {
71        for id in &self.apis {
72            write!(f, "{}\n", self.tcx.def_path_str(id)).expect("fail when write funcs");
73        }
74    }
75}
76
77pub fn has_const_generics(generics: &ty::Generics, tcx: TyCtxt<'_>) -> bool {
78    if generics
79        .own_params
80        .iter()
81        .any(|param| matches!(param.kind, ty::GenericParamDefKind::Const { .. }))
82    {
83        return true;
84    }
85
86    if let Some(parent_def_id) = generics.parent {
87        let parent = tcx.generics_of(parent_def_id);
88        has_const_generics(parent, tcx)
89    } else {
90        false
91    }
92}
93
94fn is_drop_impl(tcx: TyCtxt<'_>, fn_did: DefId) -> bool {
95    if let Some(impl_id) = tcx.trait_impl_of_assoc(fn_did) {
96        let trait_did = tcx.impl_trait_id(impl_id);
97        if tcx.is_lang_item(trait_did, LangItem::Drop) {
98            return true;
99        }
100    }
101    false
102}
103
104impl<'tcx> Visitor<'tcx> for FnVisitor<'tcx> {
105    fn visit_fn<'v>(
106        &mut self,
107        fk: FnKind<'v>,
108        _fd: &'v FnDecl<'v>,
109        _b: BodyId,
110        span: Span,
111        id: LocalDefId,
112    ) -> Self::Result {
113        let fn_did = id.to_def_id();
114        let generics = self.tcx.generics_of(fn_did);
115        rap_trace!(
116            "visit fn: {:?} (path: {}), generics: {:?}, span: {:?}",
117            fn_did,
118            self.tcx.def_path_str(fn_did),
119            generics,
120            span,
121        );
122
123        if self.tcx.def_path_str(fn_did).ends_with("dummy") && self.tcx.def_span(fn_did).is_dummy()
124        {
125            rap_trace!("skip rustc dummy fn");
126            return;
127        }
128
129        if self.config.pub_only && !is_def_id_public(fn_did, self.tcx) {
130            rap_trace!("skip for non-public");
131            return;
132        }
133
134        if !self.config.include_drop && is_drop_impl(self.tcx, fn_did) {
135            rap_trace!("skip drop impl");
136            return;
137        }
138
139        let is_generic = generics.requires_monomorphization(self.tcx);
140
141        // if config.resolve_generic is false, skip all generic functions
142        if !self.config.include_generic && is_generic {
143            rap_trace!("skip generic fn");
144            return;
145        }
146
147        // if config.ignore_const_generic is true,
148        // skip functions with const generics
149        if self.config.ignore_const_generic && has_const_generics(generics, self.tcx) {
150            rap_trace!("skip const generic fn");
151            return;
152        }
153
154        if !self.config.include_unsafe && fk.header().unwrap().safety().is_unsafe() {
155            rap_trace!("skip unsafe fn");
156            return;
157        }
158
159        if is_generic {
160            self.generic_apis.push(fn_did);
161        } else {
162            self.apis.push(fn_did);
163        }
164    }
165}