diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs index 2d51c104da1a..2cb5493f1a98 100644 --- a/clippy_config/src/types.rs +++ b/clippy_config/src/types.rs @@ -1,4 +1,4 @@ -use clippy_utils::PathNS; +use clippy_utils::paths::{PathNS, find_crates, lookup_path}; use rustc_data_structures::fx::FxHashMap; use rustc_errors::{Applicability, Diag}; use rustc_hir::PrimTy; @@ -148,7 +148,7 @@ pub fn create_disallowed_map( for disallowed_path in disallowed_paths { let path = disallowed_path.path(); let sym_path: Vec = path.split("::").map(Symbol::intern).collect(); - let mut resolutions = clippy_utils::lookup_path(tcx, ns, &sym_path); + let mut resolutions = lookup_path(tcx, ns, &sym_path); resolutions.retain(|&def_id| def_kind_predicate(tcx.def_kind(def_id))); let (prim_ty, found_prim_ty) = if let &[name] = sym_path.as_slice() @@ -164,10 +164,10 @@ pub fn create_disallowed_map( && !disallowed_path.allow_invalid // Don't warn about unloaded crates: // https://github.com/rust-lang/rust-clippy/pull/14397#issuecomment-2848328221 - && (sym_path.len() < 2 || !clippy_utils::find_crates(tcx, sym_path[0]).is_empty()) + && (sym_path.len() < 2 || !find_crates(tcx, sym_path[0]).is_empty()) { // Relookup the path in an arbitrary namespace to get a good `expected, found` message - let found_def_ids = clippy_utils::lookup_path(tcx, PathNS::Arbitrary, &sym_path); + let found_def_ids = lookup_path(tcx, PathNS::Arbitrary, &sym_path); let message = if let Some(&def_id) = found_def_ids.first() { let (article, description) = tcx.article_and_description(def_id); format!("expected a {predicate_description}, found {article} {description}") diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs index 4c47d180d3f5..31cc004f6855 100644 --- a/clippy_lints/src/await_holding_invalid.rs +++ b/clippy_lints/src/await_holding_invalid.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPathWithoutReplacement, create_disallowed_map}; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{PathNS, paths}; +use clippy_utils::paths::{self, PathNS}; use rustc_hir as hir; use rustc_hir::def_id::{DefId, DefIdMap}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs index c58cfb022e49..25b7099c855d 100644 --- a/clippy_lints/src/disallowed_macros.rs +++ b/clippy_lints/src/disallowed_macros.rs @@ -1,8 +1,8 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; -use clippy_utils::PathNS; use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then}; use clippy_utils::macros::macro_backtrace; +use clippy_utils::paths::PathNS; use rustc_data_structures::fx::FxHashSet; use rustc_hir::def::DefKind; use rustc_hir::def_id::DefIdMap; diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs index 4c32a6002041..fb970e17f38f 100644 --- a/clippy_lints/src/disallowed_methods.rs +++ b/clippy_lints/src/disallowed_methods.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; -use clippy_utils::PathNS; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths::PathNS; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::DefIdMap; use rustc_hir::{Expr, ExprKind}; diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs index 2cf1fd018b83..d0b2f0c8407f 100644 --- a/clippy_lints/src/disallowed_types.rs +++ b/clippy_lints/src/disallowed_types.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_config::types::{DisallowedPath, create_disallowed_map}; -use clippy_utils::PathNS; use clippy_utils::diagnostics::span_lint_and_then; +use clippy_utils::paths::PathNS; use rustc_data_structures::fx::FxHashMap; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefIdMap; diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs index 6be4845e2e76..d0d02a382d15 100644 --- a/clippy_lints/src/functions/mod.rs +++ b/clippy_lints/src/functions/mod.rs @@ -10,7 +10,7 @@ mod too_many_lines; use clippy_config::Conf; use clippy_utils::msrvs::Msrv; -use clippy_utils::{PathNS, lookup_path_str}; +use clippy_utils::paths::{PathNS, lookup_path_str}; use rustc_hir as hir; use rustc_hir::intravisit; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs index dd0cb01cc338..a1e621cc9f6b 100644 --- a/clippy_lints/src/missing_enforced_import_rename.rs +++ b/clippy_lints/src/missing_enforced_import_rename.rs @@ -1,7 +1,7 @@ use clippy_config::Conf; use clippy_utils::diagnostics::span_lint_and_sugg; +use clippy_utils::paths::{PathNS, lookup_path_str}; use clippy_utils::source::SpanRangeExt; -use clippy_utils::{PathNS, lookup_path_str}; use rustc_errors::Applicability; use rustc_hir::def::Res; use rustc_hir::def_id::DefIdMap; diff --git a/clippy_lints/src/non_std_lazy_statics.rs b/clippy_lints/src/non_std_lazy_statics.rs index 6f70854767d2..370ded99a4d0 100644 --- a/clippy_lints/src/non_std_lazy_statics.rs +++ b/clippy_lints/src/non_std_lazy_statics.rs @@ -1,8 +1,9 @@ use clippy_config::Conf; use clippy_utils::diagnostics::{span_lint, span_lint_and_then}; use clippy_utils::msrvs::{self, Msrv}; +use clippy_utils::paths::{self, PathNS, find_crates, lookup_path_str}; use clippy_utils::visitors::for_each_expr; -use clippy_utils::{PathNS, find_crates, fn_def_id, is_no_std_crate, lookup_path_str, path_def_id, paths, sym}; +use clippy_utils::{fn_def_id, is_no_std_crate, path_def_id, sym}; use rustc_data_structures::fx::FxIndexMap; use rustc_errors::Applicability; use rustc_hir::def::{DefKind, Res}; diff --git a/clippy_lints_internal/src/internal_paths.rs b/clippy_lints_internal/src/internal_paths.rs index 29ad3b85da11..dc1e30ab2bdd 100644 --- a/clippy_lints_internal/src/internal_paths.rs +++ b/clippy_lints_internal/src/internal_paths.rs @@ -1,5 +1,5 @@ -use clippy_utils::paths::PathLookup; -use clippy_utils::{PathNS, sym, type_path, value_path}; +use clippy_utils::paths::{PathLookup, PathNS}; +use clippy_utils::{sym, type_path, value_path}; // Paths inside rustc pub static EARLY_LINT_PASS: PathLookup = type_path!(rustc_lint::passes::EarlyLintPass); diff --git a/clippy_lints_internal/src/unnecessary_def_path.rs b/clippy_lints_internal/src/unnecessary_def_path.rs index 2e9b8c2d53d6..8877f1faf0ee 100644 --- a/clippy_lints_internal/src/unnecessary_def_path.rs +++ b/clippy_lints_internal/src/unnecessary_def_path.rs @@ -1,6 +1,7 @@ use crate::internal_paths; use clippy_utils::diagnostics::span_lint_and_then; -use clippy_utils::{PathNS, lookup_path, path_def_id, peel_ref_operators}; +use clippy_utils::paths::{PathNS, lookup_path}; +use clippy_utils::{path_def_id, peel_ref_operators}; use rustc_hir::def_id::DefId; use rustc_hir::{Expr, ExprKind}; use rustc_lint::{LateContext, LateLintPass}; diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs index 44e72a8d16a5..bbd0c262c246 100644 --- a/clippy_utils/src/lib.rs +++ b/clippy_utils/src/lib.rs @@ -96,30 +96,29 @@ use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::packed::Pu128; use rustc_data_structures::unhash::UnhashMap; use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk}; -use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS}; -use rustc_hir::def::{DefKind, Namespace, Res}; -use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId, LocalModDefId}; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::{DefId, LocalDefId, LocalModDefId}; use rustc_hir::definitions::{DefPath, DefPathData}; use rustc_hir::hir_id::{HirIdMap, HirIdSet}; use rustc_hir::intravisit::{FnKind, Visitor, walk_expr}; use rustc_hir::{ - self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, ConstContext, - CoroutineDesugaring, CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, - GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, - Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, - StmtKind, TraitFn, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def, + self as hir, Arm, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstArgKind, CoroutineDesugaring, + CoroutineKind, Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArg, GenericArgs, HirId, Impl, + ImplItem, ImplItemKind, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, + Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt, StmtKind, TraitFn, TraitItem, + TraitItemKind, TraitRef, TyKind, UnOp, def, }; use rustc_lexer::{TokenKind, tokenize}; use rustc_lint::{LateContext, Level, Lint, LintContext}; +use rustc_middle::hir::nested_filter; use rustc_middle::hir::place::PlaceBase; use rustc_middle::lint::LevelAndSource; use rustc_middle::mir::{AggregateKind, Operand, RETURN_PLACE, Rvalue, StatementKind, TerminatorKind}; use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow}; -use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::layout::IntegerExt; use rustc_middle::ty::{ - self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgKind, GenericArgsRef, IntTy, Ty, - TyCtxt, TypeFlags, TypeVisitableExt, UintTy, UpvarCapture, + self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, GenericArgKind, GenericArgsRef, IntTy, Ty, TyCtxt, + TypeFlags, TypeVisitableExt, UintTy, UpvarCapture, }; use rustc_span::hygiene::{ExpnKind, MacroKind}; use rustc_span::source_map::SourceMap; @@ -132,7 +131,6 @@ use crate::consts::{ConstEvalCtxt, Constant, mir_to_const}; use crate::higher::Range; use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type}; use crate::visitors::for_each_expr_without_closures; -use rustc_middle::hir::nested_filter; #[macro_export] macro_rules! extract_msrv_attr { @@ -240,7 +238,7 @@ pub fn is_in_const_context(cx: &LateContext<'_>) -> bool { /// * const blocks (or inline consts) /// * associated constants pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool { - use ConstContext::{Const, ConstFn, Static}; + use rustc_hir::ConstContext::{Const, ConstFn, Static}; let Some(ctx) = tcx.hir_body_const_context(tcx.hir_enclosing_body_owner(hir_id)) else { return false; }; @@ -513,207 +511,6 @@ pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx> path_res(cx, maybe_path).opt_def_id() } -fn find_primitive_impls(tcx: TyCtxt<'_>, name: Symbol) -> &[DefId] { - let ty = match name { - sym::bool => SimplifiedType::Bool, - sym::char => SimplifiedType::Char, - sym::str => SimplifiedType::Str, - sym::array => SimplifiedType::Array, - sym::slice => SimplifiedType::Slice, - // FIXME: rustdoc documents these two using just `pointer`. - // - // Maybe this is something we should do here too. - sym::const_ptr => SimplifiedType::Ptr(Mutability::Not), - sym::mut_ptr => SimplifiedType::Ptr(Mutability::Mut), - sym::isize => SimplifiedType::Int(IntTy::Isize), - sym::i8 => SimplifiedType::Int(IntTy::I8), - sym::i16 => SimplifiedType::Int(IntTy::I16), - sym::i32 => SimplifiedType::Int(IntTy::I32), - sym::i64 => SimplifiedType::Int(IntTy::I64), - sym::i128 => SimplifiedType::Int(IntTy::I128), - sym::usize => SimplifiedType::Uint(UintTy::Usize), - sym::u8 => SimplifiedType::Uint(UintTy::U8), - sym::u16 => SimplifiedType::Uint(UintTy::U16), - sym::u32 => SimplifiedType::Uint(UintTy::U32), - sym::u64 => SimplifiedType::Uint(UintTy::U64), - sym::u128 => SimplifiedType::Uint(UintTy::U128), - sym::f32 => SimplifiedType::Float(FloatTy::F32), - sym::f64 => SimplifiedType::Float(FloatTy::F64), - _ => return &[], - }; - - tcx.incoherent_impls(ty) -} - -fn non_local_item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option { - match tcx.def_kind(def_id) { - DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx.module_children(def_id).iter().find_map(|child| { - if child.ident.name == name && ns.matches(child.res.ns()) { - child.res.opt_def_id() - } else { - None - } - }), - DefKind::Impl { .. } => tcx - .associated_item_def_ids(def_id) - .iter() - .copied() - .find(|assoc_def_id| tcx.item_name(*assoc_def_id) == name && ns.matches(tcx.def_kind(assoc_def_id).ns())), - _ => None, - } -} - -fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, name: Symbol) -> Option { - let root_mod; - let item_kind = match tcx.hir_node_by_def_id(local_id) { - Node::Crate(r#mod) => { - root_mod = ItemKind::Mod(Ident::dummy(), r#mod); - &root_mod - }, - Node::Item(item) => &item.kind, - _ => return None, - }; - - let res = |ident: Ident, owner_id: OwnerId| { - if ident.name == name && ns.matches(tcx.def_kind(owner_id).ns()) { - Some(owner_id.to_def_id()) - } else { - None - } - }; - - match item_kind { - ItemKind::Mod(_, r#mod) => r#mod.item_ids.iter().find_map(|&item_id| { - let ident = tcx.hir_item(item_id).kind.ident()?; - res(ident, item_id.owner_id) - }), - ItemKind::Impl(r#impl) => r#impl - .items - .iter() - .find_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)), - ItemKind::Trait(.., trait_item_refs) => trait_item_refs - .iter() - .find_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)), - _ => None, - } -} - -fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option { - if let Some(local_id) = def_id.as_local() { - local_item_child_by_name(tcx, local_id, ns, name) - } else { - non_local_item_child_by_name(tcx, def_id, ns, name) - } -} - -/// Finds the crates called `name`, may be multiple due to multiple major versions. -pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> &'static [DefId] { - static BY_NAME: OnceLock>> = OnceLock::new(); - let map = BY_NAME.get_or_init(|| { - let mut map = FxHashMap::default(); - map.insert(tcx.crate_name(LOCAL_CRATE), vec![LOCAL_CRATE.as_def_id()]); - for &num in tcx.crates(()) { - map.entry(tcx.crate_name(num)).or_default().push(num.as_def_id()); - } - map - }); - match map.get(&name) { - Some(def_ids) => def_ids, - None => &[], - } -} - -/// Specifies whether to resolve a path in the [`TypeNS`], [`ValueNS`], [`MacroNS`] or in an -/// arbitrary namespace -#[derive(Clone, Copy, PartialEq, Debug)] -pub enum PathNS { - Type, - Value, - Macro, - - /// Resolves to the name in the first available namespace, e.g. for `std::vec` this would return - /// either the macro or the module but **not** both - /// - /// Must only be used when the specific resolution is unimportant such as in - /// `missing_enforced_import_renames` - Arbitrary, -} - -impl PathNS { - fn matches(self, ns: Option) -> bool { - let required = match self { - PathNS::Type => TypeNS, - PathNS::Value => ValueNS, - PathNS::Macro => MacroNS, - PathNS::Arbitrary => return true, - }; - - ns == Some(required) - } -} - -/// Resolves a def path like `std::vec::Vec`. -/// -/// Typically it will return one [`DefId`] or none, but in some situations there can be multiple: -/// - `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0 -/// - `alloc::boxed::Box::downcast` would return a function for each of the different inherent impls -/// ([1], [2], [3]) -/// -/// This function is expensive and should be used sparingly. -/// -/// [1]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast -/// [2]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-1 -/// [3]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-2 -pub fn lookup_path(tcx: TyCtxt<'_>, ns: PathNS, path: &[Symbol]) -> Vec { - let (root, rest) = match *path { - [] | [_] => return Vec::new(), - [root, ref rest @ ..] => (root, rest), - }; - - let mut out = Vec::new(); - for &base in find_crates(tcx, root).iter().chain(find_primitive_impls(tcx, root)) { - lookup_path_with_base(tcx, base, ns, rest, &mut out); - } - out -} - -/// Resolves a def path like `vec::Vec` with the base `std`. -fn lookup_path_with_base(tcx: TyCtxt<'_>, mut base: DefId, ns: PathNS, mut path: &[Symbol], out: &mut Vec) { - loop { - match *path { - [segment] => { - out.extend(item_child_by_name(tcx, base, ns, segment)); - - // When the current def_id is e.g. `struct S`, check the impl items in - // `impl S { ... }` - let inherent_impl_children = tcx - .inherent_impls(base) - .iter() - .filter_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, ns, segment)); - out.extend(inherent_impl_children); - - return; - }, - [segment, ref rest @ ..] => { - path = rest; - let Some(child) = item_child_by_name(tcx, base, PathNS::Type, segment) else { - return; - }; - base = child; - }, - [] => unreachable!(), - } - } -} - -/// Equivalent to a [`lookup_path`] after splitting the input string on `::` -/// -/// This function is expensive and should be used sparingly. -pub fn lookup_path_str(tcx: TyCtxt<'_>, ns: PathNS, path: &str) -> Vec { - let path: Vec = path.split("::").map(Symbol::intern).collect(); - lookup_path(tcx, ns, &path) -} - /// Gets the `hir::TraitRef` of the trait the given method is implemented for. /// /// Use this if you want to find the `TraitRef` of the `Add` trait in this example: diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs index 984230178830..795fb502c9cc 100644 --- a/clippy_utils/src/paths.rs +++ b/clippy_utils/src/paths.rs @@ -4,13 +4,48 @@ //! Whenever possible, please consider diagnostic items over hardcoded paths. //! See for more information. -use crate::{MaybePath, PathNS, lookup_path, path_def_id, sym}; -use rustc_hir::def_id::DefId; +use crate::{MaybePath, path_def_id, sym}; +use rustc_ast::Mutability; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::def::Namespace::{MacroNS, TypeNS, ValueNS}; +use rustc_hir::def::{DefKind, Namespace}; +use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId}; +use rustc_hir::{ImplItemRef, ItemKind, Node, OwnerId, TraitItemRef}; use rustc_lint::LateContext; -use rustc_middle::ty::Ty; -use rustc_span::{STDLIB_STABLE_CRATES, Symbol}; +use rustc_middle::ty::fast_reject::SimplifiedType; +use rustc_middle::ty::{FloatTy, IntTy, Ty, TyCtxt, UintTy}; +use rustc_span::{Ident, STDLIB_STABLE_CRATES, Symbol}; use std::sync::OnceLock; +/// Specifies whether to resolve a path in the [`TypeNS`], [`ValueNS`], [`MacroNS`] or in an +/// arbitrary namespace +#[derive(Clone, Copy, PartialEq, Debug)] +pub enum PathNS { + Type, + Value, + Macro, + + /// Resolves to the name in the first available namespace, e.g. for `std::vec` this would return + /// either the macro or the module but **not** both + /// + /// Must only be used when the specific resolution is unimportant such as in + /// `missing_enforced_import_renames` + Arbitrary, +} + +impl PathNS { + fn matches(self, ns: Option) -> bool { + let required = match self { + PathNS::Type => TypeNS, + PathNS::Value => ValueNS, + PathNS::Macro => MacroNS, + PathNS::Arbitrary => return true, + }; + + ns == Some(required) + } +} + /// Lazily resolves a path into a list of [`DefId`]s using [`lookup_path`]. /// /// Typically it will contain one [`DefId`] or none, but in some situations there can be multiple: @@ -126,3 +161,175 @@ pub static ONCE_CELL_SYNC_LAZY: PathLookup = type_path!(once_cell::sync::Lazy); pub static ONCE_CELL_SYNC_LAZY_NEW: PathLookup = value_path!(once_cell::sync::Lazy::new); // Paths for internal lints go in `clippy_lints_internal/src/internal_paths.rs` + +/// Equivalent to a [`lookup_path`] after splitting the input string on `::` +/// +/// This function is expensive and should be used sparingly. +pub fn lookup_path_str(tcx: TyCtxt<'_>, ns: PathNS, path: &str) -> Vec { + let path: Vec = path.split("::").map(Symbol::intern).collect(); + lookup_path(tcx, ns, &path) +} + +/// Resolves a def path like `std::vec::Vec`. +/// +/// Typically it will return one [`DefId`] or none, but in some situations there can be multiple: +/// - `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0 +/// - `alloc::boxed::Box::downcast` would return a function for each of the different inherent impls +/// ([1], [2], [3]) +/// +/// This function is expensive and should be used sparingly. +/// +/// [1]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast +/// [2]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-1 +/// [3]: https://doc.rust-lang.org/std/boxed/struct.Box.html#method.downcast-2 +pub fn lookup_path(tcx: TyCtxt<'_>, ns: PathNS, path: &[Symbol]) -> Vec { + let (root, rest) = match *path { + [] | [_] => return Vec::new(), + [root, ref rest @ ..] => (root, rest), + }; + + let mut out = Vec::new(); + for &base in find_crates(tcx, root).iter().chain(find_primitive_impls(tcx, root)) { + lookup_with_base(tcx, base, ns, rest, &mut out); + } + out +} + +/// Finds the crates called `name`, may be multiple due to multiple major versions. +pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> &'static [DefId] { + static BY_NAME: OnceLock>> = OnceLock::new(); + let map = BY_NAME.get_or_init(|| { + let mut map = FxHashMap::default(); + map.insert(tcx.crate_name(LOCAL_CRATE), vec![LOCAL_CRATE.as_def_id()]); + for &num in tcx.crates(()) { + map.entry(tcx.crate_name(num)).or_default().push(num.as_def_id()); + } + map + }); + match map.get(&name) { + Some(def_ids) => def_ids, + None => &[], + } +} + +fn find_primitive_impls(tcx: TyCtxt<'_>, name: Symbol) -> &[DefId] { + let ty = match name { + sym::bool => SimplifiedType::Bool, + sym::char => SimplifiedType::Char, + sym::str => SimplifiedType::Str, + sym::array => SimplifiedType::Array, + sym::slice => SimplifiedType::Slice, + // FIXME: rustdoc documents these two using just `pointer`. + // + // Maybe this is something we should do here too. + sym::const_ptr => SimplifiedType::Ptr(Mutability::Not), + sym::mut_ptr => SimplifiedType::Ptr(Mutability::Mut), + sym::isize => SimplifiedType::Int(IntTy::Isize), + sym::i8 => SimplifiedType::Int(IntTy::I8), + sym::i16 => SimplifiedType::Int(IntTy::I16), + sym::i32 => SimplifiedType::Int(IntTy::I32), + sym::i64 => SimplifiedType::Int(IntTy::I64), + sym::i128 => SimplifiedType::Int(IntTy::I128), + sym::usize => SimplifiedType::Uint(UintTy::Usize), + sym::u8 => SimplifiedType::Uint(UintTy::U8), + sym::u16 => SimplifiedType::Uint(UintTy::U16), + sym::u32 => SimplifiedType::Uint(UintTy::U32), + sym::u64 => SimplifiedType::Uint(UintTy::U64), + sym::u128 => SimplifiedType::Uint(UintTy::U128), + sym::f32 => SimplifiedType::Float(FloatTy::F32), + sym::f64 => SimplifiedType::Float(FloatTy::F64), + _ => return &[], + }; + + tcx.incoherent_impls(ty) +} + +/// Resolves a def path like `vec::Vec` with the base `std`. +fn lookup_with_base(tcx: TyCtxt<'_>, mut base: DefId, ns: PathNS, mut path: &[Symbol], out: &mut Vec) { + loop { + match *path { + [segment] => { + out.extend(item_child_by_name(tcx, base, ns, segment)); + + // When the current def_id is e.g. `struct S`, check the impl items in + // `impl S { ... }` + let inherent_impl_children = tcx + .inherent_impls(base) + .iter() + .filter_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, ns, segment)); + out.extend(inherent_impl_children); + + return; + }, + [segment, ref rest @ ..] => { + path = rest; + let Some(child) = item_child_by_name(tcx, base, PathNS::Type, segment) else { + return; + }; + base = child; + }, + [] => unreachable!(), + } + } +} + +fn item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option { + if let Some(local_id) = def_id.as_local() { + local_item_child_by_name(tcx, local_id, ns, name) + } else { + non_local_item_child_by_name(tcx, def_id, ns, name) + } +} + +fn local_item_child_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, ns: PathNS, name: Symbol) -> Option { + let root_mod; + let item_kind = match tcx.hir_node_by_def_id(local_id) { + Node::Crate(r#mod) => { + root_mod = ItemKind::Mod(Ident::dummy(), r#mod); + &root_mod + }, + Node::Item(item) => &item.kind, + _ => return None, + }; + + let res = |ident: Ident, owner_id: OwnerId| { + if ident.name == name && ns.matches(tcx.def_kind(owner_id).ns()) { + Some(owner_id.to_def_id()) + } else { + None + } + }; + + match item_kind { + ItemKind::Mod(_, r#mod) => r#mod.item_ids.iter().find_map(|&item_id| { + let ident = tcx.hir_item(item_id).kind.ident()?; + res(ident, item_id.owner_id) + }), + ItemKind::Impl(r#impl) => r#impl + .items + .iter() + .find_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)), + ItemKind::Trait(.., trait_item_refs) => trait_item_refs + .iter() + .find_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)), + _ => None, + } +} + +fn non_local_item_child_by_name(tcx: TyCtxt<'_>, def_id: DefId, ns: PathNS, name: Symbol) -> Option { + match tcx.def_kind(def_id) { + DefKind::Mod | DefKind::Enum | DefKind::Trait => tcx.module_children(def_id).iter().find_map(|child| { + if child.ident.name == name && ns.matches(child.res.ns()) { + child.res.opt_def_id() + } else { + None + } + }), + DefKind::Impl { .. } => tcx + .associated_item_def_ids(def_id) + .iter() + .copied() + .find(|assoc_def_id| tcx.item_name(*assoc_def_id) == name && ns.matches(tcx.def_kind(assoc_def_id).ns())), + _ => None, + } +} diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs index 901b66159c9e..2683892448dd 100644 --- a/clippy_utils/src/ty/mod.rs +++ b/clippy_utils/src/ty/mod.rs @@ -32,7 +32,8 @@ use std::assert_matches::debug_assert_matches; use std::collections::hash_map::Entry; use std::iter; -use crate::{PathNS, lookup_path_str, path_res}; +use crate::path_res; +use crate::paths::{PathNS, lookup_path_str}; mod type_certainty; pub use type_certainty::expr_type_is_certain; diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs index 2b9f0cbf25ce..6e3586623277 100644 --- a/clippy_utils/src/ty/type_certainty/mod.rs +++ b/clippy_utils/src/ty/type_certainty/mod.rs @@ -11,7 +11,7 @@ //! As a heuristic, `expr_type_is_certain` may produce false negatives, but a false positive should //! be considered a bug. -use crate::{PathNS, lookup_path}; +use crate::paths::{PathNS, lookup_path}; use rustc_hir::def::{DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::{InferKind, Visitor, VisitorExt, walk_qpath, walk_ty}; diff --git a/tests/ui-internal/unnecessary_def_path.rs b/tests/ui-internal/unnecessary_def_path.rs index b17c60d03426..5cd3254188d9 100644 --- a/tests/ui-internal/unnecessary_def_path.rs +++ b/tests/ui-internal/unnecessary_def_path.rs @@ -1,7 +1,7 @@ #![feature(rustc_private)] -use clippy_utils::paths::PathLookup; -use clippy_utils::{PathNS, macro_path, sym, type_path, value_path}; +use clippy_utils::paths::{PathLookup, PathNS}; +use clippy_utils::{macro_path, sym, type_path, value_path}; static OPTION: PathLookup = type_path!(core::option::Option); //~^ unnecessary_def_path