rapx/analysis/api_dependency/
visit.rs1use 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 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 !self.config.include_generic && is_generic {
155 rap_trace!("skip generic fn");
156 return;
157 }
158
159 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}