Skip to main content

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_with_args(&self, def_id: DefId, args: ty::GenericArgsRef<'tcx>) -> String {
102        // `{assoc_path}::{item_name}`
103        if let Some((assoc_id, kind)) = self.tcx.assoc_parent(def_id) {
104            rap_trace!("assoc item: {:?} => {:?}", assoc_id, kind);
105            // the number of generic of assoc parent
106            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                // Trait Impl
112                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                // inherent impl
135                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                // Trait
145                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}