about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Macleod <alex@macleod.io>2025-05-04 17:07:20 +0000
committerAlex Macleod <alex@macleod.io>2025-05-04 17:13:02 +0000
commitf23772ce8cbfe78010fdb27da9dedcdf35180e9f (patch)
tree981e3582a062d96391c522999c0ee81fe11decd9
parentb768fbe4bccbfd4c4ecbd4726805facb6769eb4f (diff)
downloadrust-f23772ce8cbfe78010fdb27da9dedcdf35180e9f.tar.gz
rust-f23772ce8cbfe78010fdb27da9dedcdf35180e9f.zip
Move `lookup_path` and similar into `clippy_utils::paths`
-rw-r--r--clippy_config/src/types.rs8
-rw-r--r--clippy_lints/src/await_holding_invalid.rs2
-rw-r--r--clippy_lints/src/disallowed_macros.rs2
-rw-r--r--clippy_lints/src/disallowed_methods.rs2
-rw-r--r--clippy_lints/src/disallowed_types.rs2
-rw-r--r--clippy_lints/src/functions/mod.rs2
-rw-r--r--clippy_lints/src/missing_enforced_import_rename.rs2
-rw-r--r--clippy_lints/src/non_std_lazy_statics.rs3
-rw-r--r--clippy_lints_internal/src/internal_paths.rs4
-rw-r--r--clippy_lints_internal/src/unnecessary_def_path.rs3
-rw-r--r--clippy_utils/src/lib.rs225
-rw-r--r--clippy_utils/src/paths.rs215
-rw-r--r--clippy_utils/src/ty/mod.rs3
-rw-r--r--clippy_utils/src/ty/type_certainty/mod.rs2
-rw-r--r--tests/ui-internal/unnecessary_def_path.rs4
15 files changed, 243 insertions, 236 deletions
diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs
index 2d51c104da1..2cb5493f1a9 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<const REPLACEMENT_ALLOWED: bool>(
     for disallowed_path in disallowed_paths {
         let path = disallowed_path.path();
         let sym_path: Vec<Symbol> = 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<const REPLACEMENT_ALLOWED: bool>(
             && !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 4c47d180d3f..31cc004f685 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 c58cfb022e4..25b7099c855 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 4c32a600204..fb970e17f38 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 2cf1fd018b8..d0b2f0c8407 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 6be4845e2e7..d0d02a382d1 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 dd0cb01cc33..a1e621cc9f6 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 6f70854767d..370ded99a4d 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 29ad3b85da1..dc1e30ab2bd 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 2e9b8c2d53d..8877f1faf0e 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 44e72a8d16a..bbd0c262c24 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<DefId> {
-    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<DefId> {
-    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<DefId> {
-    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<FxHashMap<Symbol, Vec<DefId>>> = 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<Namespace>) -> 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<DefId> {
-    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<DefId>) {
-    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<DefId> {
-    let path: Vec<Symbol> = 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 98423017883..795fb502c9c 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 <https://github.com/rust-lang/rust-clippy/issues/5393> 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<Namespace>) -> 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<DefId> {
+    let path: Vec<Symbol> = 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<DefId> {
+    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<FxHashMap<Symbol, Vec<DefId>>> = 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<DefId>) {
+    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<DefId> {
+    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<DefId> {
+    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<DefId> {
+    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 901b66159c9..2683892448d 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 2b9f0cbf25c..6e358662327 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 b17c60d0342..5cd3254188d 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