rapx/analysis/utils/
path.rs1use 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(&self, def_id: DefId) -> String {
102 self.path_str_with_args(def_id, ty::GenericArgs::identity_for_item(self.tcx, def_id))
103 }
104
105 pub fn path_str_with_args(&self, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> String {
106 if let Some((assoc_id, kind)) = self.tcx.assoc_parent(def_id) {
108 rap_trace!("assoc item: {:?} => {:?}", assoc_id, kind);
109 let num_generic = self.tcx.generics_of(assoc_id).own_params.len();
111
112 let (parent_args, own_args) = args.split_at(num_generic);
113
114 let parent_path_str = match kind {
115 DefKind::Impl { of_trait: true } => {
117 let trait_ref = self
118 .tcx
119 .impl_trait_ref(assoc_id)
120 .instantiate(self.tcx, parent_args);
121
122 let self_ty_str = self.ty_str(trait_ref.self_ty());
123 let trait_str = self.non_assoc_path_str(trait_ref.def_id);
124 if trait_ref.args.len() > 1 {
125 format!(
126 "<{} as {}{}>",
127 self_ty_str,
128 trait_str,
129 self.generic_args_str(&trait_ref.args[1..])
130 )
131 } else {
132 format!("<{} as {}>", self_ty_str, trait_str)
133 }
134 }
135 DefKind::Impl { of_trait: false } => {
137 let self_ty = self
138 .tcx
139 .type_of(assoc_id)
140 .instantiate(self.tcx, parent_args);
141 self.ty_str(self_ty)
142 }
143 DefKind::Trait => {
145 let self_ty = parent_args[0].expect_ty();
146 let self_ty_str = self.ty_str(self_ty);
147 let trait_str = self.non_assoc_path_str(assoc_id);
148 if parent_args.len() > 1 {
149 format!(
150 "<{} as {}{}>",
151 self_ty_str,
152 trait_str,
153 self.generic_args_str(&parent_args[1..])
154 )
155 } else {
156 format!("<{} as {}>", self_ty_str, trait_str)
157 }
158 }
159 _ => {
160 unreachable!(
161 "unexpected assoc parent: {:?} => {:?}, def_id: {:?}, path: {:?}",
162 assoc_id,
163 kind,
164 def_id,
165 self.tcx.def_path_str_with_args(def_id, args)
166 );
167 }
168 };
169
170 if own_args.len() > 0 {
171 format!(
172 "{}::{}::{}",
173 parent_path_str,
174 self.tcx.item_name(def_id),
175 self.generic_args_str(own_args)
176 )
177 } else {
178 format!("{}::{}", parent_path_str, self.tcx.item_name(def_id))
179 }
180 } else {
181 if args.len() > 0 {
182 format!(
183 "{}::{}",
184 self.non_assoc_path_str(def_id),
185 self.generic_args_str(args)
186 )
187 } else {
188 format!("{}", self.non_assoc_path_str(def_id))
189 }
190 }
191 }
192
193 pub fn generic_arg_str(&self, arg: ty::GenericArg<'tcx>) -> String {
194 match arg.kind() {
195 ty::GenericArgKind::Lifetime(_) => "'_".to_string(),
196 ty::GenericArgKind::Type(ty) => self.ty_str(ty),
197 ty::GenericArgKind::Const(const_) => format!("{}", const_),
198 }
199 }
200
201 fn generic_args_str(&self, generic_args: &[ty::GenericArg<'tcx>]) -> String {
202 format!(
203 "<{}>",
204 generic_args
205 .iter()
206 .map(|arg| self.generic_arg_str(*arg))
207 .join(", ")
208 )
209 }
210}