rapx/helpers/
path.rs

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
8/// A utility to resolve the actual visible path for re-export items.
9pub 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(&current_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 from local crate, but we cannot find it in path_map,
62                // report this error.
63                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        // `{assoc_path}::{item_name}`
107        if let Some((assoc_id, kind)) = self.tcx.assoc_parent(def_id) {
108            rap_trace!("assoc item: {:?} => {:?}", assoc_id, kind);
109            // the number of generic of assoc parent
110            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                // Trait Impl
116                DefKind::Impl { of_trait: true } => {
117                    #[cfg(rapx_rustc_ge_193)]
118                    let trait_ref = self
119                        .tcx
120                        .impl_trait_ref(assoc_id)
121                        .instantiate(self.tcx, parent_args);
122                    #[cfg(not(rapx_rustc_ge_193))]
123                    let trait_ref = self
124                        .tcx
125                        .impl_trait_ref(assoc_id)
126                        .expect("trait impl must have trait ref")
127                        .instantiate(self.tcx, parent_args);
128
129                    #[cfg(rapx_rustc_ge_198)]
130                    let trait_ref = trait_ref.skip_norm_wip();
131
132                    let self_ty_str = self.ty_str(trait_ref.self_ty());
133                    let trait_str = self.non_assoc_path_str(trait_ref.def_id);
134                    if trait_ref.args.len() > 1 {
135                        format!(
136                            "<{} as {}{}>",
137                            self_ty_str,
138                            trait_str,
139                            self.generic_args_str(&trait_ref.args[1..])
140                        )
141                    } else {
142                        format!("<{} as {}>", self_ty_str, trait_str)
143                    }
144                }
145                // inherent impl
146                DefKind::Impl { of_trait: false } => {
147                    let self_ty = self
148                        .tcx
149                        .type_of(assoc_id)
150                        .instantiate(self.tcx, parent_args);
151                    #[cfg(rapx_rustc_ge_198)]
152                    let self_ty = self_ty.skip_norm_wip();
153                    self.ty_str(self_ty)
154                }
155                // Trait
156                DefKind::Trait => {
157                    let self_ty = parent_args[0].expect_ty();
158                    let self_ty_str = self.ty_str(self_ty);
159                    let trait_str = self.non_assoc_path_str(assoc_id);
160                    if parent_args.len() > 1 {
161                        format!(
162                            "<{} as {}{}>",
163                            self_ty_str,
164                            trait_str,
165                            self.generic_args_str(&parent_args[1..])
166                        )
167                    } else {
168                        format!("<{} as {}>", self_ty_str, trait_str)
169                    }
170                }
171                _ => {
172                    unreachable!(
173                        "unexpected assoc parent: {:?} => {:?}, def_id: {:?}, path: {:?}",
174                        assoc_id,
175                        kind,
176                        def_id,
177                        self.tcx.def_path_str_with_args(def_id, args)
178                    );
179                }
180            };
181
182            if own_args.len() > 0 {
183                format!(
184                    "{}::{}::{}",
185                    parent_path_str,
186                    self.tcx.item_name(def_id),
187                    self.generic_args_str(own_args)
188                )
189            } else {
190                format!("{}::{}", parent_path_str, self.tcx.item_name(def_id))
191            }
192        } else {
193            if args.len() > 0 {
194                format!(
195                    "{}::{}",
196                    self.non_assoc_path_str(def_id),
197                    self.generic_args_str(args)
198                )
199            } else {
200                format!("{}", self.non_assoc_path_str(def_id))
201            }
202        }
203    }
204
205    pub fn generic_arg_str(&self, arg: ty::GenericArg<'tcx>) -> String {
206        match arg.kind() {
207            ty::GenericArgKind::Lifetime(_) => "'_".to_string(),
208            ty::GenericArgKind::Type(ty) => self.ty_str(ty),
209            ty::GenericArgKind::Const(const_) => format!("{}", const_),
210        }
211    }
212
213    fn generic_args_str(&self, generic_args: &[ty::GenericArg<'tcx>]) -> String {
214        format!(
215            "<{}>",
216            generic_args
217                .iter()
218                .map(|arg| self.generic_arg_str(*arg))
219                .join(", ")
220        )
221    }
222}