rapx/analysis/utils/
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                    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                // inherent impl
136                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                // Trait
144                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}