1#[cfg(not(rapx_has_skip_norm_wip))]
2use crate::compat::SkipNormWip;
3use itertools::Itertools;
4use rustc_hir::def::DefKind;
5use rustc_hir::def_id::{DefId, LOCAL_CRATE};
6use rustc_middle::ty::{self, Ty, TyCtxt, TyKind};
7use std::collections::HashMap;
8
9pub struct PathResolver<'tcx> {
11 tcx: TyCtxt<'tcx>,
12 path_map: HashMap<DefId, String>,
13}
14
15pub fn get_path_resolver<'tcx>(tcx: TyCtxt<'tcx>) -> PathResolver<'tcx> {
16 let mut resolver = PathResolver::new(tcx);
17 resolver.build(LOCAL_CRATE.as_def_id(), String::new());
18 resolver
19}
20
21fn join_path_with_double_colon(parts: &[&str]) -> String {
22 parts.iter().filter(|s| !s.is_empty()).join("::")
23}
24
25impl<'tcx> PathResolver<'tcx> {
26 fn new(tcx: TyCtxt<'tcx>) -> Self {
27 PathResolver {
28 tcx,
29 path_map: HashMap::new(),
30 }
31 }
32
33 fn build(&mut self, mod_id: DefId, current_path: String) {
34 rap_trace!("enter module: {:?}, path: {}", mod_id, current_path);
35 let childs = if mod_id.is_local() {
36 self.tcx.module_children_local(mod_id.expect_local())
37 } else {
38 self.tcx.module_children(mod_id)
39 };
40
41 for child in childs {
42 rap_trace!(
43 "processing child: {:?}, ident = {}",
44 child.res,
45 child.ident.as_str()
46 );
47 if !child.vis.is_public() || child.ident.as_str() == "_" {
48 continue;
49 }
50 if let Some(did) = child.res.opt_def_id() {
51 let path = join_path_with_double_colon(&[¤t_path, child.ident.as_str()]);
52 self.path_map.entry(did).or_insert(path.clone());
53 if self.tcx.def_kind(did).is_module_like() {
54 self.build(did, path);
55 }
56 }
57 }
58 }
59
60 fn non_assoc_path_str(&self, def_id: DefId) -> String {
61 match self.path_map.get(&def_id) {
62 Some(path) => path.clone(),
63 None => {
64 if def_id.is_local() {
67 rap_error!(
68 "[PathResolver] cannot find path for {:?}, fallback to self.tcx.def_path_str",
69 def_id
70 );
71 }
72 self.tcx.def_path_str(def_id)
73 }
74 }
75 }
76
77 pub fn path_exists(&self, did: DefId) -> bool {
78 let Some((assoc_id, kind)) = self.tcx.assoc_parent(did) else {
79 return did.is_local() && self.path_map.contains_key(&did)
81 || !did.is_local() && self.tcx.visibility(did).is_public();
84 };
85
86 if !self.tcx.visibility(did).is_public() {
87 return false;
88 }
89
90 match kind {
91 DefKind::Impl { .. } => {
92 let self_ty = self
93 .tcx
94 .type_of(assoc_id)
95 .instantiate_identity()
96 .skip_norm_wip();
97 match self_ty.kind() {
98 TyKind::Adt(adt_def, _) => self.path_exists(adt_def.did()),
101 _ => true,
102 }
103 }
104 DefKind::Trait => self.path_exists(assoc_id),
105 _ => panic!(
106 "unexpected parent kind: {:?} for assoc item: {:?}",
107 kind, did
108 ),
109 }
110 }
111
112 pub fn ty_str(&self, ty: Ty<'tcx>) -> String {
113 match ty.kind() {
114 TyKind::Adt(adt_def, args) => self.path_str_with_args(adt_def.did(), args),
115 TyKind::Array(inner_ty, const_) => {
116 format!("[{};{}]", self.ty_str(*inner_ty), const_)
117 }
118 TyKind::Tuple(tys) => {
119 format!("({})", tys.iter().map(|ty| self.ty_str(ty)).join(", "))
120 }
121 TyKind::Ref(region, inner_ty, mutability) => {
122 format!(
123 "&{} {}{}",
124 region,
125 mutability.prefix_str(),
126 self.ty_str(*inner_ty)
127 )
128 }
129 TyKind::RawPtr(inner_ty, mutability) => {
130 format!("*{} {}", mutability.ptr_str(), self.ty_str(*inner_ty))
131 }
132 TyKind::Slice(inner_ty) => {
133 format!("[{}]", self.ty_str(*inner_ty))
134 }
135 #[cfg(rapx_alias_ty_structured_kind)]
136 TyKind::Alias(is_rigid, alias_ty) => match alias_ty.kind {
137 ty::AliasTyKind::Projection { def_id } => {
138 self.path_str_with_args(def_id, alias_ty.args)
139 }
140 ty::AliasTyKind::Opaque { .. } => {
141 let ty_str = alias_ty.to_ty(self.tcx, *is_rigid).to_string();
142 rap_warn!(
143 "encounter opaque type {}, type string might be private",
144 ty_str
145 );
146 ty_str
147 }
148 kind => {
149 panic!(
150 "unexpected alias kind: {:?} for alias_ty: {:?}",
151 kind, alias_ty
152 );
153 }
154 },
155 #[cfg(not(rapx_alias_ty_structured_kind))]
156 TyKind::Alias(alias_kind, alias_ty) => match alias_kind {
157 ty::AliasTyKind::Projection => {
158 self.path_str_with_args(alias_ty.def_id, alias_ty.args)
159 }
160 ty::AliasTyKind::Opaque => {
161 let ty_str = alias_ty.to_ty(self.tcx).to_string();
162 rap_warn!(
163 "encounter opaque type {}, type string might be private",
164 ty_str
165 );
166 ty_str
167 }
168 kind => {
169 panic!(
170 "unexpected alias kind: {:?} for alias_ty: {:?}",
171 kind, alias_ty
172 );
173 }
174 },
175 _ => ty.to_string(),
176 }
177 }
178
179 #[allow(unused)]
180 pub fn path_str(&self, def_id: DefId) -> String {
181 self.path_str_with_args(def_id, ty::GenericArgs::identity_for_item(self.tcx, def_id))
182 }
183
184 pub fn path_str_with_args(&self, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> String {
185 if let Some((assoc_id, kind)) = self.tcx.assoc_parent(def_id) {
187 rap_trace!("assoc item: {:?} => {:?}", assoc_id, kind);
188 let num_generic = self.tcx.generics_of(assoc_id).own_params.len();
190
191 let (parent_args, own_args) = args.split_at(num_generic);
192
193 let parent_path_str = match kind {
194 DefKind::Impl { of_trait: true } => {
196 let trait_ref = self
197 .tcx
198 .impl_trait_ref(assoc_id)
199 .instantiate(self.tcx, parent_args);
200
201 #[cfg(rapx_ge_99)]
202 let trait_ref = trait_ref.skip_norm_wip();
203
204 self.qualified_path_str(
205 trait_ref.self_ty(),
206 trait_ref.def_id,
207 &trait_ref.args[1..],
208 )
209 }
210 DefKind::Impl { of_trait: false } => {
212 let self_ty = self
213 .tcx
214 .type_of(assoc_id)
215 .instantiate(self.tcx, parent_args);
216 #[cfg(rapx_ge_99)]
217 let self_ty = self_ty.skip_norm_wip();
218 self.ty_str(self_ty)
219 }
220 DefKind::Trait => {
222 self.qualified_path_str(parent_args[0].expect_ty(), assoc_id, &parent_args[1..])
223 }
224 _ => {
225 unreachable!(
226 "unexpected assoc parent: {:?} => {:?}, def_id: {:?}, path: {:?}",
227 assoc_id,
228 kind,
229 def_id,
230 self.tcx.def_path_str_with_args(def_id, args)
231 );
232 }
233 };
234
235 let args_str = self.non_syn_generic_args_str(def_id, own_args);
236
237 join_path_with_double_colon(&[
238 &parent_path_str,
239 self.tcx.item_name(def_id).as_str(),
240 &args_str,
241 ])
242 } else {
243 let path_str = self.non_assoc_path_str(def_id);
245 let args_str = self.non_syn_generic_args_str(def_id, args);
246 join_path_with_double_colon(&[path_str.as_str(), args_str.as_str()])
247 }
248 }
249
250 pub fn generic_arg_str(&self, arg: ty::GenericArg<'tcx>) -> String {
251 match arg.kind() {
252 ty::GenericArgKind::Lifetime(_) => "'_".to_string(),
253 ty::GenericArgKind::Type(ty) => self.ty_str(ty),
254 ty::GenericArgKind::Const(const_) => format!("{}", const_),
255 }
256 }
257
258 fn qualified_path_str(
260 &self,
261 self_ty: Ty<'tcx>,
262 trait_id: DefId,
263 args: &[ty::GenericArg<'tcx>],
264 ) -> String {
265 let self_ty_str = self.ty_str(self_ty);
266 let trait_str = self.non_assoc_path_str(trait_id);
267 if args.is_empty() {
268 format!("<{} as {}>", self_ty_str, trait_str)
269 } else {
270 format!(
271 "<{} as {}{}>",
272 self_ty_str,
273 trait_str,
274 self.generic_args_str(args)
275 )
276 }
277 }
278
279 fn generic_args_str(&self, generic_args: &[ty::GenericArg<'tcx>]) -> String {
280 format!(
281 "<{}>",
282 generic_args
283 .iter()
284 .map(|arg| self.generic_arg_str(*arg))
285 .join(", ")
286 )
287 }
288
289 fn non_syn_generic_args_str(&self, def_id: DefId, args: &[ty::GenericArg<'tcx>]) -> String {
290 let defs = self.tcx.generics_of(def_id).own_params.as_slice();
291
292 assert!(defs.len() == args.len());
293
294 let non_syn_args = args
295 .iter()
296 .zip(defs.iter())
297 .filter_map(|(arg, param)| {
298 if param.kind.is_synthetic() {
299 None
300 } else {
301 Some(*arg)
302 }
303 })
304 .collect_vec();
305
306 if non_syn_args.is_empty() {
308 String::new()
309 } else {
310 self.generic_args_str(&non_syn_args)
311 }
312 }
313}