rapx/analysis/core/api_dependency/
visit.rs1use 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 !self.config.include_generic && is_generic {
143 rap_trace!("skip generic fn");
144 return;
145 }
146
147 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}