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