1use itertools::Itertools;
2use rustc_hir::def::DefKind;
3use rustc_hir::def_id::{DefId, LOCAL_CRATE};
4use rustc_middle::ty::{self, Ty, TyCtxt, TyKind};
5use rustc_span::Ident;
6use std::collections::HashMap;
7
8pub struct PathResolver<'tcx> {
10 tcx: TyCtxt<'tcx>,
11 path_map: HashMap<DefId, String>,
12}
13
14pub fn get_path_resolver<'tcx>(tcx: TyCtxt<'tcx>) -> PathResolver<'tcx> {
15 let mut resolver = PathResolver::new(tcx);
16 resolver.build(LOCAL_CRATE.as_def_id(), String::new());
17 resolver
18}
19
20fn join_path_with_ident(current_path: &str, ident: Ident) -> String {
21 if current_path.is_empty() {
22 ident.as_str().to_owned()
23 } else {
24 (current_path.to_string() + "::" + ident.as_str()).to_owned()
25 }
26}
27
28impl<'tcx> PathResolver<'tcx> {
29 fn new(tcx: TyCtxt<'tcx>) -> Self {
30 PathResolver {
31 tcx,
32 path_map: HashMap::new(),
33 }
34 }
35
36 fn build(&mut self, mod_id: DefId, current_path: String) {
37 let childs = if mod_id.is_local() {
38 self.tcx.module_children_local(mod_id.expect_local())
39 } else {
40 self.tcx.module_children(mod_id)
41 };
42
43 for child in childs {
44 if !child.vis.is_public() {
45 continue;
46 }
47 if let Some(did) = child.res.opt_def_id() {
48 let path = join_path_with_ident(¤t_path, child.ident);
49 self.path_map.entry(did).or_insert(path.clone());
50 if self.tcx.def_kind(did).is_module_like() {
51 self.build(did, path);
52 }
53 }
54 }
55 }
56
57 fn non_assoc_path_str(&self, def_id: DefId) -> String {
58 match self.path_map.get(&def_id) {
59 Some(path) => path.clone(),
60 None => {
61 if def_id.is_local() {
64 rap_error!(
65 "[PathResolver] cannot find path for {:?}, fallback to self.tcx.def_path_str",
66 def_id
67 );
68 }
69 self.tcx.def_path_str(def_id)
70 }
71 }
72 }
73
74 pub fn ty_str(&self, ty: Ty<'tcx>) -> String {
75 match ty.kind() {
76 TyKind::Adt(adt_def, args) => self.path_str_with_args(adt_def.did(), args),
77 TyKind::Array(inner_ty, const_) => {
78 format!("[{};{}]", self.ty_str(*inner_ty), const_)
79 }
80 TyKind::Tuple(tys) => {
81 format!("({})", tys.iter().map(|ty| self.ty_str(ty)).join(", "))
82 }
83 TyKind::Ref(region, inner_ty, mutability) => {
84 format!(
85 "&{} {}{}",
86 region,
87 mutability.prefix_str(),
88 self.ty_str(*inner_ty)
89 )
90 }
91 TyKind::RawPtr(inner_ty, mutability) => {
92 format!("*{} {}", mutability.ptr_str(), self.ty_str(*inner_ty))
93 }
94 TyKind::Slice(inner_ty) => {
95 format!("[{}]", self.ty_str(*inner_ty))
96 }
97 _ => ty.to_string(),
98 }
99 }
100
101 pub fn path_str_with_args(&self, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> String {
102 if let Some((assoc_id, kind)) = self.tcx.assoc_parent(def_id) {
104 rap_trace!("assoc item: {:?} => {:?}", assoc_id, kind);
105 let num_generic = self.tcx.generics_of(assoc_id).own_params.len();
107
108 let (parent_args, own_args) = args.split_at(num_generic);
109
110 let parent_path_str = match kind {
111 DefKind::Impl { of_trait: true } => {
113 let trait_ref = self
114 .tcx
115 .impl_trait_ref(assoc_id)
116 .instantiate(self.tcx, parent_args);
117
118 #[cfg(rapx_ge_99)]
119 let trait_ref = trait_ref.skip_norm_wip();
120
121 let self_ty_str = self.ty_str(trait_ref.self_ty());
122 let trait_str = self.non_assoc_path_str(trait_ref.def_id);
123 if trait_ref.args.len() > 1 {
124 format!(
125 "<{} as {}{}>",
126 self_ty_str,
127 trait_str,
128 self.generic_args_str(&trait_ref.args[1..])
129 )
130 } else {
131 format!("<{} as {}>", self_ty_str, trait_str)
132 }
133 }
134 DefKind::Impl { of_trait: false } => {
136 let self_ty = self
137 .tcx
138 .type_of(assoc_id)
139 .instantiate(self.tcx, parent_args);
140 #[cfg(rapx_ge_99)]
141 let self_ty = self_ty.skip_norm_wip();
142 self.ty_str(self_ty)
143 }
144 DefKind::Trait => {
146 let self_ty = parent_args[0].expect_ty();
147 let self_ty_str = self.ty_str(self_ty);
148 let trait_str = self.non_assoc_path_str(assoc_id);
149 if parent_args.len() > 1 {
150 format!(
151 "<{} as {}{}>",
152 self_ty_str,
153 trait_str,
154 self.generic_args_str(&parent_args[1..])
155 )
156 } else {
157 format!("<{} as {}>", self_ty_str, trait_str)
158 }
159 }
160 _ => {
161 unreachable!(
162 "unexpected assoc parent: {:?} => {:?}, def_id: {:?}, path: {:?}",
163 assoc_id,
164 kind,
165 def_id,
166 self.tcx.def_path_str_with_args(def_id, args)
167 );
168 }
169 };
170
171 if own_args.len() > 0 {
172 format!(
173 "{}::{}::{}",
174 parent_path_str,
175 self.tcx.item_name(def_id),
176 self.generic_args_str(own_args)
177 )
178 } else {
179 format!("{}::{}", parent_path_str, self.tcx.item_name(def_id))
180 }
181 } else {
182 if args.len() > 0 {
183 format!(
184 "{}::{}",
185 self.non_assoc_path_str(def_id),
186 self.generic_args_str(args)
187 )
188 } else {
189 format!("{}", self.non_assoc_path_str(def_id))
190 }
191 }
192 }
193
194 pub fn generic_arg_str(&self, arg: ty::GenericArg<'tcx>) -> String {
195 match arg.kind() {
196 ty::GenericArgKind::Lifetime(_) => "'_".to_string(),
197 ty::GenericArgKind::Type(ty) => self.ty_str(ty),
198 ty::GenericArgKind::Const(const_) => format!("{}", const_),
199 }
200 }
201
202 fn generic_args_str(&self, generic_args: &[ty::GenericArg<'tcx>]) -> String {
203 format!(
204 "<{}>",
205 generic_args
206 .iter()
207 .map(|arg| self.generic_arg_str(*arg))
208 .join(", ")
209 )
210 }
211}