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