1use crate::{MaybePath, path_def_id, sym};
8use rustc_ast::Mutability;
9use rustc_data_structures::fx::FxHashMap;
10use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS};
11use rustc_hir::def::{DefKind, Namespace, Res};
12use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
13use rustc_hir::{ImplItemRef, ItemKind, Node, OwnerId, TraitItemRef, UseKind};
14use rustc_lint::LateContext;
15use rustc_middle::ty::fast_reject::SimplifiedType;
16use rustc_middle::ty::{FloatTy, IntTy, Ty, TyCtxt, UintTy};
17use rustc_span::{Ident, STDLIB_STABLE_CRATES, Symbol};
18use std::sync::OnceLock;
19
20#[derive(Clone, Copy, PartialEq, Debug)]
23pub enum PathNS {
24 Type,
25 Value,
26 Macro,
27
28 Arbitrary,
34}
35
36impl PathNS {
37 fn matches(self, ns: Option<Namespace>) -> bool {
38 let required = match self {
39 PathNS::Type => TypeNS,
40 PathNS::Value => ValueNS,
41 PathNS::Macro => MacroNS,
42 PathNS::Arbitrary => return true,
43 };
44
45 ns == Some(required)
46 }
47}
48
49pub struct PathLookup {
60 ns: PathNS,
61 path: &'static [Symbol],
62 once: OnceLock<Vec<DefId>>,
63}
64
65impl PathLookup {
66 #[doc(hidden)]
68 pub const fn new(ns: PathNS, path: &'static [Symbol]) -> Self {
69 Self {
70 ns,
71 path,
72 once: OnceLock::new(),
73 }
74 }
75
76 pub fn get(&self, cx: &LateContext<'_>) -> &[DefId] {
78 self.once.get_or_init(|| lookup_path(cx.tcx, self.ns, self.path))
79 }
80
81 pub fn only(&self, cx: &LateContext<'_>) -> Option<DefId> {
86 let ids = self.get(cx);
87 debug_assert!(STDLIB_STABLE_CRATES.contains(&self.path[0]));
88 debug_assert!(ids.len() <= 1, "{ids:?}");
89 ids.first().copied()
90 }
91
92 pub fn matches(&self, cx: &LateContext<'_>, def_id: DefId) -> bool {
94 self.get(cx).contains(&def_id)
95 }
96
97 pub fn matches_path<'tcx>(&self, cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> bool {
99 path_def_id(cx, maybe_path).is_some_and(|def_id| self.matches(cx, def_id))
100 }
101
102 pub fn matches_ty(&self, cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
104 ty.ty_adt_def().is_some_and(|adt| self.matches(cx, adt.did()))
105 }
106}
107
108macro_rules! path_macros {
109 ($($name:ident: $ns:expr,)*) => {
110 $(
111 #[doc(hidden)]
113 #[macro_export]
114 macro_rules! $name {
115 ($$($$seg:ident $$(::)?)*) => {
116 PathLookup::new($ns, &[$$(sym::$$seg,)*])
117 };
118 }
119 )*
120 };
121}
122
123path_macros! {
124 type_path: PathNS::Type,
125 value_path: PathNS::Value,
126 macro_path: PathNS::Macro,
127}
128
129pub static FUTURES_IO_ASYNCREADEXT: PathLookup = type_path!(futures_util::AsyncReadExt);
131pub static FUTURES_IO_ASYNCWRITEEXT: PathLookup = type_path!(futures_util::AsyncWriteExt);
132pub static ITERTOOLS_NEXT_TUPLE: PathLookup = value_path!(itertools::Itertools::next_tuple);
133pub static PARKING_LOT_GUARDS: [PathLookup; 3] = [
134 type_path!(lock_api::mutex::MutexGuard),
135 type_path!(lock_api::rwlock::RwLockReadGuard),
136 type_path!(lock_api::rwlock::RwLockWriteGuard),
137];
138pub static REGEX_BUILDER_NEW: PathLookup = value_path!(regex::RegexBuilder::new);
139pub static REGEX_BYTES_BUILDER_NEW: PathLookup = value_path!(regex::bytes::RegexBuilder::new);
140pub static REGEX_BYTES_NEW: PathLookup = value_path!(regex::bytes::Regex::new);
141pub static REGEX_BYTES_SET_NEW: PathLookup = value_path!(regex::bytes::RegexSet::new);
142pub static REGEX_NEW: PathLookup = value_path!(regex::Regex::new);
143pub static REGEX_SET_NEW: PathLookup = value_path!(regex::RegexSet::new);
144pub static SERDE_DESERIALIZE: PathLookup = type_path!(serde::de::Deserialize);
145pub static SERDE_DE_VISITOR: PathLookup = type_path!(serde::de::Visitor);
146pub static TOKIO_FILE_OPTIONS: PathLookup = value_path!(tokio::fs::File::options);
147pub static TOKIO_IO_ASYNCREADEXT: PathLookup = type_path!(tokio::io::AsyncReadExt);
148pub static TOKIO_IO_ASYNCWRITEEXT: PathLookup = type_path!(tokio::io::AsyncWriteExt);
149pub static TOKIO_IO_OPEN_OPTIONS: PathLookup = type_path!(tokio::fs::OpenOptions);
150pub static TOKIO_IO_OPEN_OPTIONS_NEW: PathLookup = value_path!(tokio::fs::OpenOptions::new);
151pub static LAZY_STATIC: PathLookup = macro_path!(lazy_static::lazy_static);
152pub static ONCE_CELL_SYNC_LAZY: PathLookup = type_path!(once_cell::sync::Lazy);
153pub static ONCE_CELL_SYNC_LAZY_NEW: PathLookup = value_path!(once_cell::sync::Lazy::new);
154
155pub fn lookup_path_str(tcx: TyCtxt<'_>, ns: PathNS, path: &str) -> Vec<DefId> {
161 let path: Vec<Symbol> = path.split("::").map(Symbol::intern).collect();
162 lookup_path(tcx, ns, &path)
163}
164
165pub fn lookup_path(tcx: TyCtxt<'_>, ns: PathNS, path: &[Symbol]) -> Vec<DefId> {
178 let (root, rest) = match *path {
179 [] | [_] => return Vec::new(),
180 [root, ref rest @ ..] => (root, rest),
181 };
182
183 let mut out = Vec::new();
184 for &base in find_crates(tcx, root).iter().chain(find_primitive_impls(tcx, root)) {
185 lookup_with_base(tcx, base, ns, rest, &mut out);
186 }
187 out
188}
189
190pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> &'static [DefId] {
192 static BY_NAME: OnceLock<FxHashMap<Symbol, Vec<DefId>>> = OnceLock::new();
193 let map = BY_NAME.get_or_init(|| {
194 let mut map = FxHashMap::default();
195 map.insert(tcx.crate_name(LOCAL_CRATE), vec![LOCAL_CRATE.as_def_id()]);
196 for &num in tcx.crates(()) {
197 map.entry(tcx.crate_name(num)).or_default().push(num.as_def_id());
198 }
199 map
200 });
201 match map.get(&name) {
202 Some(def_ids) => def_ids,
203 None => &[],
204 }
205}
206
207fn find_primitive_impls(tcx: TyCtxt<'_>, name: Symbol) -> &[DefId] {
208 let ty = match name {
209 sym::bool => SimplifiedType::Bool,
210 sym::char => SimplifiedType::Char,
211 sym::str => SimplifiedType::Str,
212 sym::array => SimplifiedType::Array,
213 sym::slice => SimplifiedType::Slice,
214 sym::const_ptr => SimplifiedType::Ptr(Mutability::Not),
218 sym::mut_ptr => SimplifiedType::Ptr(Mutability::Mut),
219 sym::isize => SimplifiedType::Int(IntTy::Isize),
220 sym::i8 => SimplifiedType::Int(IntTy::I8),
221 sym::i16 => SimplifiedType::Int(IntTy::I16),
222 sym::i32 => SimplifiedType::Int(IntTy::I32),
223 sym::i64 => SimplifiedType::Int(IntTy::I64),
224 sym::i128 => SimplifiedType::Int(IntTy::I128),
225 sym::usize => SimplifiedType::Uint(UintTy::Usize),
226 sym::u8 => SimplifiedType::Uint(UintTy::U8),
227 sym::u16 => SimplifiedType::Uint(UintTy::U16),
228 sym::u32 => SimplifiedType::Uint(UintTy::U32),
229 sym::u64 => SimplifiedType::Uint(UintTy::U64),
230 sym::u128 => SimplifiedType::Uint(UintTy::U128),
231 sym::f32 => SimplifiedType::Float(FloatTy::F32),
232 sym::f64 => SimplifiedType::Float(FloatTy::F64),
233 _ => return &[],
234 };
235
236 tcx.incoherent_impls(ty)
237}
238
239fn lookup_with_base(tcx: TyCtxt<'_>, mut base: DefId, ns: PathNS, mut path: &[Symbol], out: &mut Vec<DefId>) {
241 loop {
242 match *path {
243 [segment] => {
244 out.extend(item_child_by_name(tcx, base, ns, segment));
245
246 let inherent_impl_children = tcx
249 .inherent_impls(base)
250 .iter()
251 .filter_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, ns, segment));
252 out.extend(inherent_impl_children);
253
254 return;
255 },
256 [segment, ref rest @ ..] => {
257 path = rest;
258 let Some(child) = item_child_by_name(tcx, base, PathNS::Type, segment) else {
259 return;
260 };
261 base = child;
262 },
263 [] => unreachable!(),
264 }
265 }
266}
267
268fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option<DefId> {
269 if let Some(local_id) = def_id.as_local() {
270 local_item_child_by_name(tcx, local_id, ns, name)
271 } else {
272 non_local_item_child_by_name(tcx, def_id, ns, name)
273 }
274}
275
276fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, name: Symbol) -> Option<DefId> {
277 let root_mod;
278 let item_kind = match tcx.hir_node_by_def_id(local_id) {
279 Node::Crate(r#mod) => {
280 root_mod = ItemKind::Mod(Ident::dummy(), r#mod);
281 &root_mod
282 },
283 Node::Item(item) => &item.kind,
284 _ => return None,
285 };
286
287 let res = |ident: Ident, owner_id: OwnerId| {
288 if ident.name == name && ns.matches(tcx.def_kind(owner_id).ns()) {
289 Some(owner_id.to_def_id())
290 } else {
291 None
292 }
293 };
294
295 match item_kind {
296 ItemKind::Mod(_, r#mod) => r#mod.item_ids.iter().find_map(|&item_id| {
297 let item = tcx.hir_item(item_id);
298 if let ItemKind::Use(path, UseKind::Single(ident)) = item.kind {
299 if ident.name == name {
300 let opt_def_id = |ns: Option<Res>| ns.and_then(|res| res.opt_def_id());
301 match ns {
302 PathNS::Type => opt_def_id(path.res.type_ns),
303 PathNS::Value => opt_def_id(path.res.value_ns),
304 PathNS::Macro => opt_def_id(path.res.macro_ns),
305 PathNS::Arbitrary => unreachable!(),
306 }
307 } else {
308 None
309 }
310 } else {
311 res(item.kind.ident()?, item_id.owner_id)
312 }
313 }),
314 ItemKind::Impl(r#impl) => r#impl
315 .items
316 .iter()
317 .find_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)),
318 ItemKind::Trait(.., trait_item_refs) => trait_item_refs
319 .iter()
320 .find_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)),
321 _ => None,
322 }
323}
324
325fn non_local_item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option<DefId> {
326 match tcx.def_kind(def_id) {
327 DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx.module_children(def_id).iter().find_map(|child| {
328 if child.ident.name == name && ns.matches(child.res.ns()) {
329 child.res.opt_def_id()
330 } else {
331 None
332 }
333 }),
334 DefKind::Impl { .. } => tcx
335 .associated_item_def_ids(def_id)
336 .iter()
337 .copied()
338 .find(|assoc_def_id| tcx.item_name(*assoc_def_id) == name && ns.matches(tcx.def_kind(assoc_def_id).ns())),
339 _ => None,
340 }
341}