charon_driver/translate/
resolve_path.rs

1//! Machinery to resolve a string path into a `DefId`. Based on `clippy_utils::def_path_res`.
2use std::sync::Arc;
3
4use anyhow::bail;
5use charon_lib::name_matcher::NamePattern;
6use hax::{BaseState, SInto};
7use itertools::Itertools;
8use rustc_ast::Mutability;
9use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
10use rustc_middle::ty::{self, FloatTy, IntTy, TyCtxt, UintTy, fast_reject::SimplifiedType};
11use rustc_span::symbol::Symbol;
12
13fn find_primitive_impls<'tcx>(
14    tcx: TyCtxt<'tcx>,
15    name: &str,
16) -> impl Iterator<Item = DefId> + use<'tcx> {
17    let ty = match name {
18        "bool" => SimplifiedType::Bool,
19        "char" => SimplifiedType::Char,
20        "str" => SimplifiedType::Str,
21        "array" => SimplifiedType::Array,
22        "slice" => SimplifiedType::Slice,
23        // FIXME: rustdoc documents these two using just `pointer`.
24        //
25        // Maybe this is something we should do here too.
26        "const_ptr" => SimplifiedType::Ptr(Mutability::Not),
27        "mut_ptr" => SimplifiedType::Ptr(Mutability::Mut),
28        "isize" => SimplifiedType::Int(IntTy::Isize),
29        "i8" => SimplifiedType::Int(IntTy::I8),
30        "i16" => SimplifiedType::Int(IntTy::I16),
31        "i32" => SimplifiedType::Int(IntTy::I32),
32        "i64" => SimplifiedType::Int(IntTy::I64),
33        "i128" => SimplifiedType::Int(IntTy::I128),
34        "usize" => SimplifiedType::Uint(UintTy::Usize),
35        "u8" => SimplifiedType::Uint(UintTy::U8),
36        "u16" => SimplifiedType::Uint(UintTy::U16),
37        "u32" => SimplifiedType::Uint(UintTy::U32),
38        "u64" => SimplifiedType::Uint(UintTy::U64),
39        "u128" => SimplifiedType::Uint(UintTy::U128),
40        "f32" => SimplifiedType::Float(FloatTy::F32),
41        "f64" => SimplifiedType::Float(FloatTy::F64),
42        _ => {
43            return [].iter().copied();
44        }
45    };
46    tcx.incoherent_impls(ty).iter().copied()
47}
48
49/// Find the items corresponding to a given pattern. This mostly ignores generics.
50///
51/// Can return multiple resolutions when there are multiple versions of the same crate, e.g.
52/// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0.
53///
54/// Also returns multiple results when there are multiple paths under the same name e.g. `std::vec`
55/// would have both a [`hax::DefKind::Mod`] and [`hax::DefKind::Macro`].
56///
57/// This function is expensive and should be used sparingly.
58///
59/// If the path does not correspond to an existing item, return the first subpath that doesn't
60/// correspond to an item.
61pub fn def_path_def_ids<'a, 'tcx>(
62    s: &impl BaseState<'tcx>,
63    pat: &'a NamePattern,
64) -> anyhow::Result<Vec<DefId>> {
65    use charon_lib::name_matcher::{PatElem, PatTy};
66    let tcx = s.base().tcx;
67    let mut items: Vec<DefId> = vec![];
68    for (i, elem) in pat.elems.iter().enumerate() {
69        if i == 0 {
70            match elem {
71                PatElem::Ident { name: elem, .. } => {
72                    let segment = Symbol::intern(elem);
73                    items = tcx
74                        .crates(())
75                        .iter()
76                        .copied()
77                        .chain([LOCAL_CRATE])
78                        // Also consider "crate" to be a valid name for the local crate.
79                        .filter(move |&num| {
80                            tcx.crate_name(num) == segment
81                                || (num == LOCAL_CRATE && elem == "crate")
82                        })
83                        .map(CrateNum::as_def_id)
84                        .collect_vec();
85                    items.extend(find_primitive_impls(tcx, elem));
86                }
87                PatElem::Glob => {
88                    items = tcx
89                        .crates(())
90                        .iter()
91                        .copied()
92                        .chain([LOCAL_CRATE])
93                        .map(CrateNum::as_def_id)
94                        .collect_vec();
95                }
96                PatElem::Impl(impl_pat) => match impl_pat.elems.as_slice() {
97                    [
98                        ..,
99                        PatElem::Ident {
100                            generics,
101                            is_trait: true,
102                            ..
103                        },
104                    ] => match generics.as_slice() {
105                        [] => bail!("malformed trait impl pattern"),
106                        [PatTy::Pat(self_pat)] => {
107                            let impls = def_path_def_ids(s, impl_pat)?
108                                .into_iter()
109                                .flat_map(|trait_def_id| tcx.all_impls(trait_def_id));
110                            match self_pat.elems.as_slice() {
111                                [PatElem::Glob] => {
112                                    items = impls.collect_vec();
113                                }
114                                _ => {
115                                    let self_ty_def_ids = def_path_def_ids(s, self_pat)?;
116                                    items = impls
117                                        .filter(|impl_def_id| {
118                                            let impl_self_ty = tcx
119                                                .impl_trait_ref(impl_def_id)
120                                                .skip_binder()
121                                                .self_ty();
122                                            if let ty::Adt(adt_def, _) = impl_self_ty.kind() {
123                                                self_ty_def_ids.contains(&adt_def.did())
124                                            } else {
125                                                false
126                                            }
127                                        })
128                                        .collect_vec();
129                                }
130                            }
131                        }
132                        [_] => bail!("`--start-from` only supports implementations on named types"),
133                        [_, _, ..] => bail!("`--start-from` does not support trait generics"),
134                    },
135                    [
136                        ..,
137                        PatElem::Ident {
138                            is_trait: false, ..
139                        },
140                    ] => bail!("`--start-from` does not support inherent impls"),
141                    _ => bail!("`--start-from` does not support this impl pattern"),
142                },
143            }
144        } else {
145            let nameable_children = items.iter().copied().flat_map(|def_id| {
146                let hax_def: Arc<hax::FullDef> = def_id.sinto(s).full_def(s);
147                hax_def.nameable_children(s)
148            });
149            match elem {
150                PatElem::Ident { name: elem, .. } => {
151                    items = nameable_children
152                        .filter(|(child_name, _)| child_name.as_str() == elem)
153                        .filter_map(|(_, def_id)| def_id.as_rust_def_id())
154                        .collect();
155                }
156                PatElem::Glob => {
157                    items = nameable_children
158                        .filter_map(|(_, def_id)| def_id.as_rust_def_id())
159                        .collect();
160                }
161                PatElem::Impl(_) => bail!(
162                    "`--start-from` only supports impl patterns if they're the first element of the path"
163                ),
164            }
165        }
166        if items.is_empty() {
167            let prefix = NamePattern {
168                elems: pat.elems[..=i].to_vec(),
169            };
170            if i == 0 {
171                bail!(
172                    "path `{prefix}` does not correspond to any item; did you mean `crate::{prefix}`?"
173                )
174            } else {
175                bail!("path `{prefix}` does not correspond to any item")
176            }
177        }
178    }
179    Ok(items)
180}