rapx/analysis/utils/
def_path.rs

1use rustc_hir::def::{DefKind, Res};
2use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LOCAL_CRATE};
3use rustc_hir::HirId;
4use rustc_hir::PrimTy;
5use rustc_hir::{ImplItemId, ItemKind, Mutability, Node, OwnerId, TraitItemId};
6use rustc_middle::ty::fast_reject::SimplifiedType;
7use rustc_middle::ty::TyCtxt;
8use rustc_middle::ty::{FloatTy, IntTy, UintTy};
9use rustc_span::symbol::{Ident, Symbol};
10
11pub fn path_str_def_id<'tcx>(tcx: TyCtxt<'tcx>, path_str: &str) -> DefId {
12    let path: Vec<&str> = path_str.split("::").collect();
13    def_path_last_def_id(&tcx, &path)
14}
15
16pub fn def_path_last_def_id<'tcx>(tcx: &TyCtxt<'tcx>, path: &[&str]) -> DefId {
17    def_path_def_ids(tcx, path)
18        .last()
19        .expect(&format!("can not resolve {:?}", path))
20}
21
22pub struct DefPath {
23    def_ids: Vec<DefId>,
24}
25
26impl DefPath {
27    //path like "std::vec::Vec"
28    pub fn new(raw: &str, tcx: &TyCtxt<'_>) -> Self {
29        let path: Vec<&str> = raw.split("::").collect();
30        let def_ids: Vec<DefId> = def_path_def_ids(tcx, &path).collect();
31        if def_ids.len() == 0 {
32            panic!("Fail to parse def path {}", raw);
33        }
34        DefPath { def_ids }
35    }
36
37    pub fn last_def_id(&self) -> DefId {
38        *self.def_ids.last().unwrap()
39    }
40}
41
42/* Modified from Clippy
43 * https://github.com/rust-lang/rust-clippy/blob/6d61bd/clippy_utils/src/lib.rs
44 * Note: Commit 6b61bd matches rustc nightly 2024-06-30
45 * */
46
47/// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`].
48pub fn def_path_def_ids(tcx: &TyCtxt<'_>, path: &[&str]) -> impl Iterator<Item = DefId> {
49    def_path_res(tcx, path)
50        .into_iter()
51        .filter_map(|res| res.opt_def_id())
52}
53
54pub fn def_path_res(tcx: &TyCtxt<'_>, path: &[&str]) -> Vec<Res> {
55    let (base, path) = match path {
56        [primitive] => {
57            return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)];
58        }
59        [base, path @ ..] => (base, path),
60        _ => return Vec::new(),
61    };
62
63    let base_sym = Symbol::intern(base);
64
65    let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym {
66        Some(LOCAL_CRATE.as_def_id())
67    } else {
68        None
69    };
70
71    let crates = find_primitive_impls(tcx, base)
72        .chain(local_crate)
73        .map(|id| Res::Def(tcx.def_kind(id), id))
74        .chain(find_crates(tcx, base_sym))
75        .collect();
76
77    def_path_res_with_base(tcx, crates, path)
78}
79
80pub fn def_path_res_with_base(tcx: &TyCtxt<'_>, mut base: Vec<Res>, mut path: &[&str]) -> Vec<Res> {
81    while let [segment, rest @ ..] = path {
82        path = rest;
83        let segment = Symbol::intern(segment);
84
85        base = base
86            .into_iter()
87            .filter_map(|res| res.opt_def_id())
88            .flat_map(|def_id| {
89                // When the current def_id is e.g. `struct S`, check the impl items in
90                // `impl S { ... }`
91                let inherent_impl_children = tcx
92                    .inherent_impls(def_id)
93                    .iter()
94                    .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment));
95
96                let direct_children = item_children_by_name(tcx, def_id, segment);
97
98                inherent_impl_children.chain(direct_children)
99            })
100            .collect();
101    }
102
103    base
104}
105
106fn find_primitive_impls<'tcx>(
107    tcx: &TyCtxt<'tcx>,
108    name: &str,
109) -> impl Iterator<Item = DefId> + 'tcx {
110    let ty = match name {
111        "bool" => SimplifiedType::Bool,
112        "char" => SimplifiedType::Char,
113        "str" => SimplifiedType::Str,
114        "array" => SimplifiedType::Array,
115        "slice" => SimplifiedType::Slice,
116        // FIXME: rustdoc documents these two using just `pointer`.
117        //
118        // Maybe this is something we should do here too.
119        "const_ptr" => SimplifiedType::Ptr(Mutability::Not),
120        "mut_ptr" => SimplifiedType::Ptr(Mutability::Mut),
121        "isize" => SimplifiedType::Int(IntTy::Isize),
122        "i8" => SimplifiedType::Int(IntTy::I8),
123        "i16" => SimplifiedType::Int(IntTy::I16),
124        "i32" => SimplifiedType::Int(IntTy::I32),
125        "i64" => SimplifiedType::Int(IntTy::I64),
126        "i128" => SimplifiedType::Int(IntTy::I128),
127        "usize" => SimplifiedType::Uint(UintTy::Usize),
128        "u8" => SimplifiedType::Uint(UintTy::U8),
129        "u16" => SimplifiedType::Uint(UintTy::U16),
130        "u32" => SimplifiedType::Uint(UintTy::U32),
131        "u64" => SimplifiedType::Uint(UintTy::U64),
132        "u128" => SimplifiedType::Uint(UintTy::U128),
133        "f32" => SimplifiedType::Float(FloatTy::F32),
134        "f64" => SimplifiedType::Float(FloatTy::F64),
135        _ => {
136            return [].iter().copied();
137        }
138    };
139
140    tcx.incoherent_impls(ty).iter().copied()
141}
142
143fn non_local_item_children_by_name(tcx: &TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
144    match tcx.def_kind(def_id) {
145        DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx
146            .module_children(def_id)
147            .iter()
148            .filter(|item| item.ident.name == name)
149            .map(|child| child.res.expect_non_local())
150            .collect(),
151        DefKind::Impl { .. } => tcx
152            .associated_item_def_ids(def_id)
153            .iter()
154            .copied()
155            .filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name)
156            .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id))
157            .collect(),
158        _ => Vec::new(),
159    }
160}
161
162fn local_item_children_by_name(tcx: &TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec<Res> {
163    let root_mod;
164    let hir_node = tcx.hir_node_by_def_id(local_id);
165    let item_kind = match hir_node {
166        Node::Crate(module) => {
167            root_mod = ItemKind::Mod(hir_node.ident().unwrap(), module);
168            &root_mod
169        }
170        Node::Item(item) => &item.kind,
171        _ => return Vec::new(),
172    };
173
174    let res = |ident: Ident, owner_id: OwnerId| {
175        if ident.name == name {
176            let def_id = owner_id.to_def_id();
177            Some(Res::Def(tcx.def_kind(def_id), def_id))
178        } else {
179            None
180        }
181    };
182
183    match item_kind {
184        ItemKind::Mod(_ident, module) => module
185            .item_ids
186            .iter()
187            .filter_map(|&item_id| res(tcx.hir_ident(item_id.hir_id()), item_id.owner_id))
188            .collect(),
189        ItemKind::Impl(r#impl) => r#impl
190            .items
191            .iter()
192            .filter_map(|&ImplItemId { owner_id }| {
193                res(tcx.hir_ident(HirId::from(owner_id)), owner_id)
194            })
195            .collect(),
196        ItemKind::Trait(.., trait_item_refs) => trait_item_refs
197            .iter()
198            .filter_map(|&TraitItemId { owner_id }| {
199                res(tcx.hir_ident(HirId::from(owner_id)), owner_id)
200            })
201            .collect(),
202        _ => Vec::new(),
203    }
204}
205
206fn item_children_by_name(tcx: &TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
207    if let Some(local_id) = def_id.as_local() {
208        local_item_children_by_name(tcx, local_id, name)
209    } else {
210        non_local_item_children_by_name(tcx, def_id, name)
211    }
212}
213
214pub fn find_crates(tcx: &TyCtxt<'_>, name: Symbol) -> Vec<Res> {
215    tcx.crates(())
216        .iter()
217        .copied()
218        .filter(move |&num| tcx.crate_name(num) == name)
219        .map(CrateNum::as_def_id)
220        .map(|id| Res::Def(tcx.def_kind(id), id))
221        .collect()
222}