Skip to main content

rapx/analysis/api_dependency/
visit.rs

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