Skip to main content

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