about summary refs log tree commit diff
diff options
context:
space:
mode:
authorAlex Macleod <alex@macleod.io>2025-04-28 17:12:16 +0000
committerAlex Macleod <alex@macleod.io>2025-05-04 15:26:37 +0000
commitb768fbe4bccbfd4c4ecbd4726805facb6769eb4f (patch)
treeb0a1e7ef5ea2bcbd78cc17c70e76a3e7b36867ee
parentea13461967fb97098c5a9f1566e8826dd32f5495 (diff)
downloadrust-b768fbe4bccbfd4c4ecbd4726805facb6769eb4f.tar.gz
rust-b768fbe4bccbfd4c4ecbd4726805facb6769eb4f.zip
Replace str path utils with new `PathLookup` type
-rw-r--r--CONTRIBUTING.md2
-rw-r--r--book/src/development/common_tools_writing_lints.md4
-rw-r--r--book/src/development/trait_checking.md16
-rw-r--r--clippy.toml3
-rw-r--r--clippy_config/src/types.rs87
-rw-r--r--clippy_lints/src/await_holding_invalid.rs13
-rw-r--r--clippy_lints/src/casts/manual_dangling_ptr.rs4
-rw-r--r--clippy_lints/src/derive.rs4
-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.rs10
-rw-r--r--clippy_lints/src/functions/mod.rs4
-rw-r--r--clippy_lints/src/let_underscore.rs12
-rw-r--r--clippy_lints/src/manual_option_as_slice.rs4
-rw-r--r--clippy_lints/src/methods/io_other_error.rs9
-rw-r--r--clippy_lints/src/methods/manual_saturating_arithmetic.rs20
-rw-r--r--clippy_lints/src/methods/needless_character_iteration.rs7
-rw-r--r--clippy_lints/src/methods/open_options.rs20
-rw-r--r--clippy_lints/src/methods/str_splitn.rs4
-rw-r--r--clippy_lints/src/methods/useless_asref.rs9
-rw-r--r--clippy_lints/src/missing_enforced_import_rename.rs10
-rw-r--r--clippy_lints/src/non_std_lazy_statics.rs40
-rw-r--r--clippy_lints/src/regex.rs17
-rw-r--r--clippy_lints/src/serde_api.rs6
-rw-r--r--clippy_lints/src/single_range_in_vec_init.rs4
-rw-r--r--clippy_lints/src/to_digit_is_some.rs20
-rw-r--r--clippy_lints/src/unused_io_amount.rs41
-rw-r--r--clippy_lints/src/utils/author.rs130
-rw-r--r--clippy_lints_internal/src/collapsible_calls.rs6
-rw-r--r--clippy_lints_internal/src/internal_paths.rs17
-rw-r--r--clippy_lints_internal/src/invalid_paths.rs108
-rw-r--r--clippy_lints_internal/src/lib.rs6
-rw-r--r--clippy_lints_internal/src/lint_without_lint_pass.rs9
-rw-r--r--clippy_lints_internal/src/msrv_attr_impl.rs7
-rw-r--r--clippy_lints_internal/src/outer_expn_data_pass.rs6
-rw-r--r--clippy_lints_internal/src/symbols.rs15
-rw-r--r--clippy_lints_internal/src/unnecessary_def_path.rs316
-rw-r--r--clippy_utils/src/lib.rs359
-rw-r--r--clippy_utils/src/paths.rs173
-rw-r--r--clippy_utils/src/sym.rs56
-rw-r--r--clippy_utils/src/ty/mod.rs22
-rw-r--r--clippy_utils/src/ty/type_certainty/mod.rs23
-rw-r--r--tests/ui-internal/auxiliary/paths.rs4
-rw-r--r--tests/ui-internal/invalid_paths.rs30
-rw-r--r--tests/ui-internal/invalid_paths.stderr26
-rw-r--r--tests/ui-internal/unnecessary_def_path.fixed77
-rw-r--r--tests/ui-internal/unnecessary_def_path.rs85
-rw-r--r--tests/ui-internal/unnecessary_def_path.stderr120
-rw-r--r--tests/ui-internal/unnecessary_def_path_hardcoded_path.rs19
-rw-r--r--tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr31
-rw-r--r--tests/ui-toml/toml_disallowed_types/clippy.toml2
-rw-r--r--tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr8
-rw-r--r--tests/ui-toml/toml_invalid_path/clippy.toml9
-rw-r--r--tests/ui-toml/toml_invalid_path/conf_invalid_path.rs5
-rw-r--r--tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr27
-rw-r--r--tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs4
-rw-r--r--tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr8
-rw-r--r--tests/ui/author.stdout2
-rw-r--r--tests/ui/author/blocks.stdout10
-rw-r--r--tests/ui/author/call.stdout3
-rw-r--r--tests/ui/author/if.stdout4
-rw-r--r--tests/ui/author/issue_3849.stdout5
-rw-r--r--tests/ui/author/loop.stdout8
-rw-r--r--tests/ui/author/macro_in_closure.stdout11
-rw-r--r--tests/ui/author/macro_in_loop.stdout11
-rw-r--r--tests/ui/author/matches.stdout6
-rw-r--r--tests/ui/author/struct.stdout9
-rw-r--r--tests/ui/manual_saturating_arithmetic.fixed2
-rw-r--r--tests/ui/manual_saturating_arithmetic.rs2
-rw-r--r--tests/ui/manual_saturating_arithmetic.stderr48
70 files changed, 801 insertions, 1402 deletions
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 3b33f719063..45ba2f078be 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -77,7 +77,7 @@ debugging to find the actual problem behind the issue.
 
 [`T-middle`] issues can be more involved and require verifying types. The [`ty`] module contains a
 lot of methods that are useful, though one of the most useful would be `expr_ty` (gives the type of
-an AST expression). `match_def_path()` in Clippy's `utils` module can also be useful.
+an AST expression).
 
 [`good-first-issue`]: https://github.com/rust-lang/rust-clippy/labels/good-first-issue
 [`S-inactive-closed`]: https://github.com/rust-lang/rust-clippy/pulls?q=is%3Aclosed+label%3AS-inactive-closed
diff --git a/book/src/development/common_tools_writing_lints.md b/book/src/development/common_tools_writing_lints.md
index 2e39f279eae..e23b32039c9 100644
--- a/book/src/development/common_tools_writing_lints.md
+++ b/book/src/development/common_tools_writing_lints.md
@@ -86,7 +86,7 @@ arguments have to be checked separately.
 
 ```rust
 use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
-use clippy_utils::{paths, match_def_path};
+use clippy_utils::paths;
 use rustc_span::symbol::sym;
 use rustc_hir::LangItem;
 
@@ -108,7 +108,7 @@ impl LateLintPass<'_> for MyStructLint {
 
         // 3. Using the type path
         // This method should be avoided if possible
-        if match_def_path(cx, def_id, &paths::RESULT) {
+        if paths::RESULT.matches_ty(cx, ty) {
             // The type is a `core::result::Result`
         }
     }
diff --git a/book/src/development/trait_checking.md b/book/src/development/trait_checking.md
index b7d229ccc19..cc4eb966f59 100644
--- a/book/src/development/trait_checking.md
+++ b/book/src/development/trait_checking.md
@@ -73,22 +73,24 @@ impl LateLintPass<'_> for CheckDropTraitLint {
 ## Using Type Path
 
 If neither diagnostic item nor a language item is available, we can use
-[`clippy_utils::paths`][paths] with the `match_trait_method` to determine trait
-implementation.
+[`clippy_utils::paths`][paths] to determine get a trait's `DefId`.
 
 > **Note**: This approach should be avoided if possible, the best thing to do would be to make a PR to [`rust-lang/rust`][rust] adding a diagnostic item.
 
-Below, we check if the given `expr` implements the `Iterator`'s trait method `cloned` :
+Below, we check if the given `expr` implements [`core::iter::Step`](https://doc.rust-lang.org/std/iter/trait.Step.html):
 
 ```rust
-use clippy_utils::{match_trait_method, paths};
+use clippy_utils::{implements_trait, paths};
 use rustc_hir::Expr;
 use rustc_lint::{LateContext, LateLintPass};
 
-impl LateLintPass<'_> for CheckTokioAsyncReadExtTrait {
+impl LateLintPass<'_> for CheckIterStep {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
-        if match_trait_method(cx, expr, &paths::CORE_ITER_CLONED) {
-            println!("`expr` implements `CORE_ITER_CLONED` trait!");
+        let ty = cx.typeck_results().expr_ty(expr);
+        if let Some(trait_def_id) = paths::ITER_STEP.first(cx)
+            && implements_trait(cx, ty, trait_def_id, &[])
+        {
+            println!("`expr` implements the `core::iter::Step` trait!");
         }
     }
 }
diff --git a/clippy.toml b/clippy.toml
index 0a7724bbe4e..77573105d86 100644
--- a/clippy.toml
+++ b/clippy.toml
@@ -7,14 +7,11 @@ lint-commented-code = true
 [[disallowed-methods]]
 path = "rustc_lint::context::LintContext::lint"
 reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead"
-allow-invalid = true
 
 [[disallowed-methods]]
 path = "rustc_lint::context::LintContext::span_lint"
 reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead"
-allow-invalid = true
 
 [[disallowed-methods]]
 path = "rustc_middle::ty::context::TyCtxt::node_span_lint"
 reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead"
-allow-invalid = true
diff --git a/clippy_config/src/types.rs b/clippy_config/src/types.rs
index 70cf0915bb6..2d51c104da1 100644
--- a/clippy_config/src/types.rs
+++ b/clippy_config/src/types.rs
@@ -1,7 +1,8 @@
+use clippy_utils::PathNS;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::{Applicability, Diag};
 use rustc_hir::PrimTy;
-use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def::DefKind;
 use rustc_hir::def_id::DefIdMap;
 use rustc_middle::ty::TyCtxt;
 use rustc_span::{Span, Symbol};
@@ -133,6 +134,7 @@ impl DisallowedPathEnum {
 pub fn create_disallowed_map<const REPLACEMENT_ALLOWED: bool>(
     tcx: TyCtxt<'_>,
     disallowed_paths: &'static [DisallowedPath<REPLACEMENT_ALLOWED>],
+    ns: PathNS,
     def_kind_predicate: impl Fn(DefKind) -> bool,
     predicate_description: &str,
     allow_prim_tys: bool,
@@ -145,62 +147,47 @@ pub fn create_disallowed_map<const REPLACEMENT_ALLOWED: bool>(
         FxHashMap::default();
     for disallowed_path in disallowed_paths {
         let path = disallowed_path.path();
-        let path_split = path.split("::").collect::<Vec<_>>();
-        let mut resolutions = clippy_utils::def_path_res(tcx, &path_split);
-
-        let mut found_def_id = None;
-        let mut found_prim_ty = false;
-        resolutions.retain(|res| match res {
-            Res::Def(def_kind, def_id) => {
-                found_def_id = Some(*def_id);
-                def_kind_predicate(*def_kind)
-            },
-            Res::PrimTy(_) => {
-                found_prim_ty = true;
-                allow_prim_tys
-            },
-            _ => false,
-        });
+        let sym_path: Vec<Symbol> = path.split("::").map(Symbol::intern).collect();
+        let mut resolutions = clippy_utils::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()
+            && let Some(prim) = PrimTy::from_name(name)
+        {
+            (allow_prim_tys.then_some(prim), true)
+        } else {
+            (None, false)
+        };
+
         if resolutions.is_empty()
+            && prim_ty.is_none()
+            && !disallowed_path.allow_invalid
             // Don't warn about unloaded crates:
             // https://github.com/rust-lang/rust-clippy/pull/14397#issuecomment-2848328221
-            && (path_split.len() < 2
-                || !clippy_utils::find_crates(tcx, Symbol::intern(path_split[0])).is_empty())
+            && (sym_path.len() < 2 || !clippy_utils::find_crates(tcx, sym_path[0]).is_empty())
         {
-            let span = disallowed_path.span();
-
-            if let Some(def_id) = found_def_id {
-                tcx.sess.dcx().span_warn(
-                    span,
-                    format!(
-                        "expected a {predicate_description}, found {} {}",
-                        tcx.def_descr_article(def_id),
-                        tcx.def_descr(def_id)
-                    ),
-                );
+            // 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 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}")
             } else if found_prim_ty {
-                tcx.sess.dcx().span_warn(
-                    span,
-                    format!("expected a {predicate_description}, found a primitive type",),
-                );
-            } else if !disallowed_path.allow_invalid {
-                tcx.sess.dcx().span_warn(
-                    span,
-                    format!("`{path}` does not refer to an existing {predicate_description}"),
-                );
-            }
+                format!("expected a {predicate_description}, found a primitive type")
+            } else {
+                format!("`{path}` does not refer to a reachable {predicate_description}")
+            };
+            tcx.sess
+                .dcx()
+                .struct_span_warn(disallowed_path.span(), message)
+                .with_help("add `allow-invalid = true` to the entry to suppress this warning")
+                .emit();
         }
 
-        for res in resolutions {
-            match res {
-                Res::Def(_, def_id) => {
-                    def_ids.insert(def_id, (path, disallowed_path));
-                },
-                Res::PrimTy(ty) => {
-                    prim_tys.insert(ty, (path, disallowed_path));
-                },
-                _ => unreachable!(),
-            }
+        for def_id in resolutions {
+            def_ids.insert(def_id, (path, disallowed_path));
+        }
+        if let Some(ty) = prim_ty {
+            prim_tys.insert(ty, (path, disallowed_path));
         }
     }
 
diff --git a/clippy_lints/src/await_holding_invalid.rs b/clippy_lints/src/await_holding_invalid.rs
index 52d1d5b4c67..4c47d180d3f 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::{match_def_path, paths};
+use clippy_utils::{PathNS, paths};
 use rustc_hir as hir;
 use rustc_hir::def_id::{DefId, DefIdMap};
 use rustc_lint::{LateContext, LateLintPass};
@@ -182,6 +182,7 @@ impl AwaitHolding {
         let (def_ids, _) = create_disallowed_map(
             tcx,
             &conf.await_holding_invalid_types,
+            PathNS::Type,
             crate::disallowed_types::def_kind_predicate,
             "type",
             false,
@@ -275,12 +276,10 @@ fn emit_invalid_type(
 }
 
 fn is_mutex_guard(cx: &LateContext<'_>, def_id: DefId) -> bool {
-    cx.tcx.is_diagnostic_item(sym::MutexGuard, def_id)
-        || cx.tcx.is_diagnostic_item(sym::RwLockReadGuard, def_id)
-        || cx.tcx.is_diagnostic_item(sym::RwLockWriteGuard, def_id)
-        || match_def_path(cx, def_id, &paths::PARKING_LOT_MUTEX_GUARD)
-        || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_READ_GUARD)
-        || match_def_path(cx, def_id, &paths::PARKING_LOT_RWLOCK_WRITE_GUARD)
+    match cx.tcx.get_diagnostic_name(def_id) {
+        Some(name) => matches!(name, sym::MutexGuard | sym::RwLockReadGuard | sym::RwLockWriteGuard),
+        None => paths::PARKING_LOT_GUARDS.iter().any(|guard| guard.matches(cx, def_id)),
+    }
 }
 
 fn is_refcell_ref(cx: &LateContext<'_>, def_id: DefId) -> bool {
diff --git a/clippy_lints/src/casts/manual_dangling_ptr.rs b/clippy_lints/src/casts/manual_dangling_ptr.rs
index 6dbaa5cb507..61dfc0fc042 100644
--- a/clippy_lints/src/casts/manual_dangling_ptr.rs
+++ b/clippy_lints/src/casts/manual_dangling_ptr.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::SpanRangeExt;
-use clippy_utils::{expr_or_init, match_def_path, path_def_id, paths, std_or_core};
+use clippy_utils::{expr_or_init, path_def_id, paths, std_or_core};
 use rustc_ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, GenericArg, Mutability, QPath, Ty, TyKind};
@@ -54,7 +54,7 @@ fn is_expr_const_aligned(cx: &LateContext<'_>, expr: &Expr<'_>, to: &Ty<'_>) ->
 fn is_align_of_call(cx: &LateContext<'_>, fun: &Expr<'_>, to: &Ty<'_>) -> bool {
     if let ExprKind::Path(QPath::Resolved(_, path)) = fun.kind
         && let Some(fun_id) = path_def_id(cx, fun)
-        && match_def_path(cx, fun_id, &paths::ALIGN_OF)
+        && paths::ALIGN_OF.matches(cx, fun_id)
         && let Some(args) = path.segments.last().and_then(|seg| seg.args)
         && let [GenericArg::Type(generic_ty)] = args.args
     {
diff --git a/clippy_lints/src/derive.rs b/clippy_lints/src/derive.rs
index 06528f875a2..3443b36eb4f 100644
--- a/clippy_lints/src/derive.rs
+++ b/clippy_lints/src/derive.rs
@@ -2,7 +2,7 @@ use std::ops::ControlFlow;
 
 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
-use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, paths};
+use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, paths};
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{FnKind, Visitor, walk_expr, walk_fn, walk_item};
@@ -377,7 +377,7 @@ fn check_unsafe_derive_deserialize<'tcx>(
     }
 
     if let Some(trait_def_id) = trait_ref.trait_def_id()
-        && match_def_path(cx, trait_def_id, &paths::SERDE_DESERIALIZE)
+        && paths::SERDE_DESERIALIZE.matches(cx, trait_def_id)
         && let ty::Adt(def, _) = ty.kind()
         && let Some(local_def_id) = def.did().as_local()
         && let adt_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id)
diff --git a/clippy_lints/src/disallowed_macros.rs b/clippy_lints/src/disallowed_macros.rs
index fa33fef2306..c58cfb022e4 100644
--- a/clippy_lints/src/disallowed_macros.rs
+++ b/clippy_lints/src/disallowed_macros.rs
@@ -1,5 +1,6 @@
 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 rustc_data_structures::fx::FxHashSet;
@@ -75,6 +76,7 @@ impl DisallowedMacros {
         let (disallowed, _) = create_disallowed_map(
             tcx,
             &conf.disallowed_macros,
+            PathNS::Macro,
             |def_kind| matches!(def_kind, DefKind::Macro(_)),
             "macro",
             false,
diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs
index 1382dafa931..4c32a600204 100644
--- a/clippy_lints/src/disallowed_methods.rs
+++ b/clippy_lints/src/disallowed_methods.rs
@@ -1,5 +1,6 @@
 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 rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefIdMap;
@@ -66,6 +67,7 @@ impl DisallowedMethods {
         let (disallowed, _) = create_disallowed_map(
             tcx,
             &conf.disallowed_methods,
+            PathNS::Value,
             |def_kind| {
                 matches!(
                     def_kind,
diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs
index 2bae82648ac..2cf1fd018b8 100644
--- a/clippy_lints/src/disallowed_types.rs
+++ b/clippy_lints/src/disallowed_types.rs
@@ -1,5 +1,6 @@
 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 rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def::{DefKind, Res};
@@ -60,7 +61,14 @@ pub struct DisallowedTypes {
 
 impl DisallowedTypes {
     pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
-        let (def_ids, prim_tys) = create_disallowed_map(tcx, &conf.disallowed_types, def_kind_predicate, "type", true);
+        let (def_ids, prim_tys) = create_disallowed_map(
+            tcx,
+            &conf.disallowed_types,
+            PathNS::Type,
+            def_kind_predicate,
+            "type",
+            true,
+        );
         Self { def_ids, prim_tys }
     }
 
diff --git a/clippy_lints/src/functions/mod.rs b/clippy_lints/src/functions/mod.rs
index 5f3fc5100e7..6be4845e2e7 100644
--- a/clippy_lints/src/functions/mod.rs
+++ b/clippy_lints/src/functions/mod.rs
@@ -9,8 +9,8 @@ mod too_many_arguments;
 mod too_many_lines;
 
 use clippy_config::Conf;
-use clippy_utils::def_path_def_ids;
 use clippy_utils::msrvs::Msrv;
+use clippy_utils::{PathNS, lookup_path_str};
 use rustc_hir as hir;
 use rustc_hir::intravisit;
 use rustc_lint::{LateContext, LateLintPass};
@@ -469,7 +469,7 @@ impl Functions {
             trait_ids: conf
                 .allow_renamed_params_for
                 .iter()
-                .flat_map(|p| def_path_def_ids(tcx, &p.split("::").collect::<Vec<_>>()))
+                .flat_map(|p| lookup_path_str(tcx, PathNS::Type, p))
                 .collect(),
             msrv: conf.msrv,
         }
diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs
index bdbf5b37c5f..916191b2a7b 100644
--- a/clippy_lints/src/let_underscore.rs
+++ b/clippy_lints/src/let_underscore.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type};
+use clippy_utils::ty::{implements_trait, is_must_use_ty};
 use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths};
 use rustc_hir::{LetStmt, LocalSource, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -129,12 +129,6 @@ declare_clippy_lint! {
 
 declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_FUTURE, LET_UNDERSCORE_UNTYPED]);
 
-const SYNC_GUARD_PATHS: [&[&str]; 3] = [
-    &paths::PARKING_LOT_MUTEX_GUARD,
-    &paths::PARKING_LOT_RWLOCK_READ_GUARD,
-    &paths::PARKING_LOT_RWLOCK_WRITE_GUARD,
-];
-
 impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) {
         if matches!(local.source, LocalSource::Normal)
@@ -144,7 +138,9 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
         {
             let init_ty = cx.typeck_results().expr_ty(init);
             let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
-                GenericArgKind::Type(inner_ty) => SYNC_GUARD_PATHS.iter().any(|path| match_type(cx, inner_ty, path)),
+                GenericArgKind::Type(inner_ty) => inner_ty
+                    .ty_adt_def()
+                    .is_some_and(|adt| paths::PARKING_LOT_GUARDS.iter().any(|path| path.matches(cx, adt.did()))),
                 GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
             });
             if contains_sync_guard {
diff --git a/clippy_lints/src/manual_option_as_slice.rs b/clippy_lints/src/manual_option_as_slice.rs
index b365dbf088f..04e00f84103 100644
--- a/clippy_lints/src/manual_option_as_slice.rs
+++ b/clippy_lints/src/manual_option_as_slice.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 use clippy_utils::msrvs::Msrv;
-use clippy_utils::{is_none_arm, msrvs, peel_hir_expr_refs, sym};
+use clippy_utils::{is_none_arm, msrvs, paths, peel_hir_expr_refs, sym};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Arm, Expr, ExprKind, LangItem, Pat, PatKind, QPath, is_range_literal};
@@ -220,5 +220,5 @@ fn is_empty_slice(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 }
 
 fn is_slice_from_ref(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    clippy_utils::is_expr_path_def_path(cx, expr, &["core", "slice", "raw", "from_ref"])
+    paths::SLICE_FROM_REF.matches_path(cx, expr)
 }
diff --git a/clippy_lints/src/methods/io_other_error.rs b/clippy_lints/src/methods/io_other_error.rs
index bdc834bd47a..ec4b9c7ae2e 100644
--- a/clippy_lints/src/methods/io_other_error.rs
+++ b/clippy_lints/src/methods/io_other_error.rs
@@ -1,5 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::msrvs::{self, Msrv};
+use clippy_utils::{expr_or_init, paths};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::LateContext;
@@ -8,13 +9,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, path: &Expr<'_>, args
     if let [error_kind, error] = args
         && !expr.span.from_expansion()
         && !error_kind.span.from_expansion()
-        && clippy_utils::is_expr_path_def_path(cx, path, &clippy_utils::paths::IO_ERROR_NEW)
-        && clippy_utils::is_expr_path_def_path(
-            cx,
-            clippy_utils::expr_or_init(cx, error_kind),
-            &clippy_utils::paths::IO_ERRORKIND_OTHER,
-        )
         && let ExprKind::Path(QPath::TypeRelative(_, new_segment)) = path.kind
+        && paths::IO_ERROR_NEW.matches_path(cx, path)
+        && paths::IO_ERRORKIND_OTHER_CTOR.matches_path(cx, expr_or_init(cx, error_kind))
         && msrv.meets(cx, msrvs::IO_ERROR_OTHER)
     {
         span_lint_and_then(
diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs
index 18978a1d2bc..e2df8ce1513 100644
--- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs
+++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs
@@ -1,9 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{match_def_path, path_def_id};
+use clippy_utils::{path_res, sym};
 use rustc_ast::ast;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
+use rustc_hir::def::Res;
 use rustc_lint::LateContext;
 use rustc_middle::ty::layout::LayoutOf;
 
@@ -79,16 +80,15 @@ fn is_min_or_max(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<MinMax> {
     }
 
     let ty = cx.typeck_results().expr_ty(expr);
-    let ty_str = ty.to_string();
 
-    // `std::T::MAX` `std::T::MIN` constants
-    if let Some(id) = path_def_id(cx, expr) {
-        if match_def_path(cx, id, &["core", &ty_str, "MAX"]) {
-            return Some(MinMax::Max);
-        }
-
-        if match_def_path(cx, id, &["core", &ty_str, "MIN"]) {
-            return Some(MinMax::Min);
+    // `T::MAX` and `T::MIN` constants
+    if let hir::ExprKind::Path(hir::QPath::TypeRelative(base, seg)) = expr.kind
+        && let Res::PrimTy(_) = path_res(cx, base)
+    {
+        match seg.ident.name {
+            sym::MAX => return Some(MinMax::Max),
+            sym::MIN => return Some(MinMax::Min),
+            _ => {},
         }
     }
 
diff --git a/clippy_lints/src/methods/needless_character_iteration.rs b/clippy_lints/src/methods/needless_character_iteration.rs
index f528f7f065c..71c1576cd57 100644
--- a/clippy_lints/src/methods/needless_character_iteration.rs
+++ b/clippy_lints/src/methods/needless_character_iteration.rs
@@ -7,9 +7,8 @@ use rustc_span::Span;
 use super::NEEDLESS_CHARACTER_ITERATION;
 use super::utils::get_last_chain_binding_hir_id;
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::paths::CHAR_IS_ASCII;
 use clippy_utils::source::SpanRangeExt;
-use clippy_utils::{match_def_path, path_to_local_id, peel_blocks, sym};
+use clippy_utils::{is_path_diagnostic_item, path_to_local_id, peel_blocks, sym};
 
 fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> {
     while let ExprKind::AddrOf(_, _, e) = expr.kind {
@@ -76,9 +75,7 @@ fn handle_expr(
             // If we have `!is_ascii`, then only `.any()` should warn. And if the condition is
             // `is_ascii`, then only `.all()` should warn.
             if revert != is_all
-                && let ExprKind::Path(path) = fn_path.kind
-                && let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id()
-                && match_def_path(cx, fn_def_id, &CHAR_IS_ASCII)
+                && is_path_diagnostic_item(cx, fn_path, sym::char_is_ascii)
                 && path_to_local_id(peels_expr_ref(arg), first_param)
                 && let Some(snippet) = before_chars.get_source_text(cx)
             {
diff --git a/clippy_lints/src/methods/open_options.rs b/clippy_lints/src/methods/open_options.rs
index da084871402..bce314e64f0 100644
--- a/clippy_lints/src/methods/open_options.rs
+++ b/clippy_lints/src/methods/open_options.rs
@@ -1,8 +1,8 @@
 use rustc_data_structures::fx::FxHashMap;
 
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
-use clippy_utils::ty::{is_type_diagnostic_item, match_type};
-use clippy_utils::{match_any_def_paths, paths};
+use clippy_utils::paths;
+use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_ast::ast::LitKind;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
@@ -13,7 +13,7 @@ use rustc_span::{Span, sym};
 use super::{NONSENSICAL_OPEN_OPTIONS, SUSPICIOUS_OPEN_OPTIONS};
 
 fn is_open_options(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
-    is_type_diagnostic_item(cx, ty, sym::FsOpenOptions) || match_type(cx, ty, &paths::TOKIO_IO_OPEN_OPTIONS)
+    is_type_diagnostic_item(cx, ty, sym::FsOpenOptions) || paths::TOKIO_IO_OPEN_OPTIONS.matches_ty(cx, ty)
 }
 
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, recv: &'tcx Expr<'_>) {
@@ -126,14 +126,14 @@ fn get_open_options(
         && let ExprKind::Path(path) = callee.kind
         && let Some(did) = cx.qpath_res(&path, callee.hir_id).opt_def_id()
     {
-        let std_file_options = [sym::file_options, sym::open_options_new];
-
-        let tokio_file_options: &[&[&str]] = &[&paths::TOKIO_IO_OPEN_OPTIONS_NEW, &paths::TOKIO_FILE_OPTIONS];
+        let is_std_options = matches!(
+            cx.tcx.get_diagnostic_name(did),
+            Some(sym::file_options | sym::open_options_new)
+        );
 
-        let is_std_options = std_file_options
-            .into_iter()
-            .any(|sym| cx.tcx.is_diagnostic_item(sym, did));
-        is_std_options || match_any_def_paths(cx, did, tokio_file_options).is_some()
+        is_std_options
+            || paths::TOKIO_IO_OPEN_OPTIONS_NEW.matches(cx, did)
+            || paths::TOKIO_FILE_OPTIONS.matches(cx, did)
     } else {
         false
     }
diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs
index d183457da25..c8efb600f57 100644
--- a/clippy_lints/src/methods/str_splitn.rs
+++ b/clippy_lints/src/methods/str_splitn.rs
@@ -4,7 +4,7 @@ use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::usage::local_used_after_expr;
 use clippy_utils::visitors::{Descend, for_each_expr};
-use clippy_utils::{is_diag_item_method, match_def_path, path_to_local_id, paths};
+use clippy_utils::{is_diag_item_method, path_to_local_id, paths};
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
 use rustc_hir::{
@@ -288,7 +288,7 @@ fn parse_iter_usage<'tcx>(
             match (name.ident.as_str(), args) {
                 ("next", []) if cx.tcx.trait_of_item(did) == Some(iter_id) => (IterUsageKind::Nth(0), e.span),
                 ("next_tuple", []) => {
-                    return if match_def_path(cx, did, &paths::ITERTOOLS_NEXT_TUPLE)
+                    return if paths::ITERTOOLS_NEXT_TUPLE.matches(cx, did)
                         && let ty::Adt(adt_def, subs) = cx.typeck_results().expr_ty(e).kind()
                         && cx.tcx.is_diagnostic_item(sym::Option, adt_def.did())
                         && let ty::Tuple(subs) = subs.type_at(0).kind()
diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs
index 0cbf6004be3..17e2620d9dd 100644
--- a/clippy_lints/src/methods/useless_asref.rs
+++ b/clippy_lints/src/methods/useless_asref.rs
@@ -1,9 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{implements_trait, should_call_clone_as_function, walk_ptrs_ty_depth};
-use clippy_utils::{
-    get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, peel_blocks, strip_pat_refs,
-};
+use clippy_utils::{get_parent_expr, is_diag_trait_item, path_to_local_id, peel_blocks, strip_pat_refs};
 use rustc_errors::Applicability;
 use rustc_hir::{self as hir, LangItem};
 use rustc_lint::LateContext;
@@ -81,8 +79,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str,
                 applicability,
             );
         }
-    } else if match_def_path(cx, def_id, &["core", "option", "Option", call_name])
-        || match_def_path(cx, def_id, &["core", "result", "Result", call_name])
+    } else if let Some(impl_id) = cx.tcx.opt_parent(def_id)
+        && let Some(adt) = cx.tcx.type_of(impl_id).instantiate_identity().ty_adt_def()
+        && (cx.tcx.lang_items().option_type() == Some(adt.did()) || cx.tcx.is_diagnostic_item(sym::Result, adt.did()))
     {
         let rcv_ty = cx.typeck_results().expr_ty(recvr).peel_refs();
         let res_ty = cx.typeck_results().expr_ty(expr).peel_refs();
diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs
index 66631a69206..dd0cb01cc33 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::def_path_def_ids;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 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;
@@ -56,8 +56,12 @@ impl ImportRename {
             renames: conf
                 .enforced_import_renames
                 .iter()
-                .map(|x| (x.path.split("::").collect::<Vec<_>>(), Symbol::intern(&x.rename)))
-                .flat_map(|(path, rename)| def_path_def_ids(tcx, &path).map(move |id| (id, rename)))
+                .map(|x| (&x.path, Symbol::intern(&x.rename)))
+                .flat_map(|(path, rename)| {
+                    lookup_path_str(tcx, PathNS::Arbitrary, path)
+                        .into_iter()
+                        .map(move |id| (id, rename))
+                })
                 .collect(),
         }
     }
diff --git a/clippy_lints/src/non_std_lazy_statics.rs b/clippy_lints/src/non_std_lazy_statics.rs
index f6bc9428d65..6f70854767d 100644
--- a/clippy_lints/src/non_std_lazy_statics.rs
+++ b/clippy_lints/src/non_std_lazy_statics.rs
@@ -2,7 +2,7 @@ use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::msrvs::{self, Msrv};
 use clippy_utils::visitors::for_each_expr;
-use clippy_utils::{def_path_def_ids, fn_def_id, is_no_std_crate, path_def_id};
+use clippy_utils::{PathNS, find_crates, fn_def_id, is_no_std_crate, lookup_path_str, path_def_id, paths, sym};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
@@ -62,10 +62,7 @@ static FUNCTION_REPLACEMENTS: &[(&str, Option<&str>)] = &[
 
 pub struct NonStdLazyStatic {
     msrv: Msrv,
-    lazy_static_lazy_static: Vec<DefId>,
-    once_cell_crate: Vec<CrateNum>,
-    once_cell_sync_lazy: Vec<DefId>,
-    once_cell_sync_lazy_new: Vec<DefId>,
+    once_cell_crates: Vec<CrateNum>,
     sugg_map: FxIndexMap<DefId, Option<String>>,
     lazy_type_defs: FxIndexMap<DefId, LazyInfo>,
     uses_other_once_cell_types: bool,
@@ -76,10 +73,7 @@ impl NonStdLazyStatic {
     pub fn new(conf: &'static Conf) -> Self {
         Self {
             msrv: conf.msrv,
-            lazy_static_lazy_static: Vec::new(),
-            once_cell_crate: Vec::new(),
-            once_cell_sync_lazy: Vec::new(),
-            once_cell_sync_lazy_new: Vec::new(),
+            once_cell_crates: Vec::new(),
             sugg_map: FxIndexMap::default(),
             lazy_type_defs: FxIndexMap::default(),
             uses_other_once_cell_types: false,
@@ -95,17 +89,15 @@ fn can_use_lazy_cell(cx: &LateContext<'_>, msrv: Msrv) -> bool {
 
 impl<'hir> LateLintPass<'hir> for NonStdLazyStatic {
     fn check_crate(&mut self, cx: &LateContext<'hir>) {
-        // Fetch def_ids for external paths
-        self.lazy_static_lazy_static = def_path_def_ids(cx.tcx, &["lazy_static", "lazy_static"]).collect();
-        self.once_cell_sync_lazy = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy"]).collect();
-        self.once_cell_sync_lazy_new = def_path_def_ids(cx.tcx, &["once_cell", "sync", "Lazy", "new"]).collect();
-        // And CrateNums for `once_cell` crate
-        self.once_cell_crate = self.once_cell_sync_lazy.iter().map(|d| d.krate).collect();
+        // Add CrateNums for `once_cell` crate
+        self.once_cell_crates = find_crates(cx.tcx, sym::once_cell)
+            .iter()
+            .map(|def_id| def_id.krate)
+            .collect();
 
         // Convert hardcoded fn replacement list into a map with def_id
         for (path, sugg) in FUNCTION_REPLACEMENTS {
-            let path_vec: Vec<&str> = path.split("::").collect();
-            for did in def_path_def_ids(cx.tcx, &path_vec) {
+            for did in lookup_path_str(cx.tcx, PathNS::Value, path) {
                 self.sugg_map.insert(did, sugg.map(ToOwned::to_owned));
             }
         }
@@ -114,7 +106,7 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic {
     fn check_item(&mut self, cx: &LateContext<'hir>, item: &Item<'hir>) {
         if let ItemKind::Static(..) = item.kind
             && let Some(macro_call) = clippy_utils::macros::root_macro_call(item.span)
-            && self.lazy_static_lazy_static.contains(&macro_call.def_id)
+            && paths::LAZY_STATIC.matches(cx, macro_call.def_id)
             && can_use_lazy_cell(cx, self.msrv)
         {
             span_lint(
@@ -130,7 +122,7 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic {
             return;
         }
 
-        if let Some(lazy_info) = LazyInfo::from_item(self, cx, item)
+        if let Some(lazy_info) = LazyInfo::from_item(cx, item)
             && can_use_lazy_cell(cx, self.msrv)
         {
             self.lazy_type_defs.insert(item.owner_id.to_def_id(), lazy_info);
@@ -155,9 +147,9 @@ impl<'hir> LateLintPass<'hir> for NonStdLazyStatic {
         if let rustc_hir::TyKind::Path(qpath) = ty.peel_refs().kind
             && let Some(ty_def_id) = cx.qpath_res(&qpath, ty.hir_id).opt_def_id()
             // Is from `once_cell` crate
-            && self.once_cell_crate.contains(&ty_def_id.krate)
+            && self.once_cell_crates.contains(&ty_def_id.krate)
             // And is NOT `once_cell::sync::Lazy`
-            && !self.once_cell_sync_lazy.contains(&ty_def_id)
+            && !paths::ONCE_CELL_SYNC_LAZY.matches(cx, ty_def_id)
         {
             self.uses_other_once_cell_types = true;
         }
@@ -190,12 +182,12 @@ struct LazyInfo {
 }
 
 impl LazyInfo {
-    fn from_item(state: &NonStdLazyStatic, cx: &LateContext<'_>, item: &Item<'_>) -> Option<Self> {
+    fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Option<Self> {
         // Check if item is a `once_cell:sync::Lazy` static.
         if let ItemKind::Static(_, ty, _, body_id) = item.kind
             && let Some(path_def_id) = path_def_id(cx, ty)
             && let hir::TyKind::Path(hir::QPath::Resolved(_, path)) = ty.kind
-            && state.once_cell_sync_lazy.contains(&path_def_id)
+            && paths::ONCE_CELL_SYNC_LAZY.matches(cx, path_def_id)
         {
             let ty_span_no_args = path_span_without_args(path);
             let body = cx.tcx.hir_body(body_id);
@@ -204,7 +196,7 @@ impl LazyInfo {
             let mut new_fn_calls = FxIndexMap::default();
             for_each_expr::<(), ()>(cx, body, |ex| {
                 if let Some((fn_did, call_span)) = fn_def_id_and_span_from_body(cx, ex, body_id)
-                    && state.once_cell_sync_lazy_new.contains(&fn_did)
+                    && paths::ONCE_CELL_SYNC_LAZY_NEW.matches(cx, fn_did)
                 {
                     new_fn_calls.insert(call_span, fn_did);
                 }
diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs
index 834ff2af0e8..89d945161f6 100644
--- a/clippy_lints/src/regex.rs
+++ b/clippy_lints/src/regex.rs
@@ -2,8 +2,9 @@ use std::fmt::Display;
 
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::paths::PathLookup;
 use clippy_utils::source::SpanRangeExt;
-use clippy_utils::{def_path_res_with_base, find_crates, path_def_id, paths, sym};
+use clippy_utils::{path_def_id, paths};
 use rustc_ast::ast::{LitKind, StrStyle};
 use rustc_hir::def_id::DefIdMap;
 use rustc_hir::{BorrowKind, Expr, ExprKind, OwnerId};
@@ -121,17 +122,9 @@ impl_lint_pass!(Regex => [INVALID_REGEX, TRIVIAL_REGEX, REGEX_CREATION_IN_LOOPS]
 
 impl<'tcx> LateLintPass<'tcx> for Regex {
     fn check_crate(&mut self, cx: &LateContext<'tcx>) {
-        // We don't use `match_def_path` here because that relies on matching the exact path, which changed
-        // between regex 1.8 and 1.9
-        //
-        // `def_path_res_with_base` will resolve through re-exports but is relatively heavy, so we only
-        // perform the operation once and store the results
-        let regex_crates = find_crates(cx.tcx, sym::regex);
-        let mut resolve = |path: &[&str], kind: RegexKind| {
-            for res in def_path_res_with_base(cx.tcx, regex_crates.clone(), &path[1..]) {
-                if let Some(id) = res.opt_def_id() {
-                    self.definitions.insert(id, kind);
-                }
+        let mut resolve = |path: &PathLookup, kind: RegexKind| {
+            for &id in path.get(cx) {
+                self.definitions.insert(id, kind);
             }
         };
 
diff --git a/clippy_lints/src/serde_api.rs b/clippy_lints/src/serde_api.rs
index a8c6518b592..a64b9b22378 100644
--- a/clippy_lints/src/serde_api.rs
+++ b/clippy_lints/src/serde_api.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{get_trait_def_id, paths};
+use clippy_utils::paths;
 use rustc_hir::{Impl, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
@@ -32,9 +32,7 @@ impl<'tcx> LateLintPass<'tcx> for SerdeApi {
         }) = item.kind
         {
             let did = trait_ref.path.res.def_id();
-            if let Some(visit_did) = get_trait_def_id(cx.tcx, &paths::SERDE_DE_VISITOR)
-                && did == visit_did
-            {
+            if paths::SERDE_DE_VISITOR.matches(cx, did) {
                 let mut seen_str = None;
                 let mut seen_string = None;
                 for item in *items {
diff --git a/clippy_lints/src/single_range_in_vec_init.rs b/clippy_lints/src/single_range_in_vec_init.rs
index 2d989b1cf0b..54d09ff9ee4 100644
--- a/clippy_lints/src/single_range_in_vec_init.rs
+++ b/clippy_lints/src/single_range_in_vec_init.rs
@@ -3,7 +3,7 @@ use clippy_utils::higher::VecArgs;
 use clippy_utils::macros::root_macro_call_first_node;
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::implements_trait;
-use clippy_utils::{get_trait_def_id, is_no_std_crate};
+use clippy_utils::{is_no_std_crate, paths};
 use rustc_ast::{LitIntType, LitKind, UintTy};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, LangItem, QPath, StructTailExpr};
@@ -100,7 +100,7 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
             && let Some(start_snippet) = start.span.get_source_text(cx)
             && let Some(end_snippet) = end.span.get_source_text(cx)
         {
-            let should_emit_every_value = if let Some(step_def_id) = get_trait_def_id(cx.tcx, &["core", "iter", "Step"])
+            let should_emit_every_value = if let Some(step_def_id) = paths::ITER_STEP.only(cx)
                 && implements_trait(cx, ty, step_def_id, &[])
             {
                 true
diff --git a/clippy_lints/src/to_digit_is_some.rs b/clippy_lints/src/to_digit_is_some.rs
index bb969bc802f..c8a6a41d6d8 100644
--- a/clippy_lints/src/to_digit_is_some.rs
+++ b/clippy_lints/src/to_digit_is_some.rs
@@ -1,10 +1,9 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{match_def_path, sym};
+use clippy_utils::{paths, sym};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
 use rustc_session::declare_lint_pass;
 
 declare_clippy_lint! {
@@ -40,27 +39,18 @@ impl<'tcx> LateLintPass<'tcx> for ToDigitIsSome {
         if let hir::ExprKind::MethodCall(is_some_path, to_digit_expr, [], _) = &expr.kind
             && is_some_path.ident.name == sym::is_some
         {
-            let match_result = match &to_digit_expr.kind {
+            let match_result = match to_digit_expr.kind {
                 hir::ExprKind::MethodCall(to_digits_path, char_arg, [radix_arg], _) => {
                     if to_digits_path.ident.name == sym::to_digit
-                        && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(char_arg)
-                        && *char_arg_ty.kind() == ty::Char
+                        && cx.typeck_results().expr_ty_adjusted(char_arg).is_char()
                     {
-                        Some((true, *char_arg, radix_arg))
+                        Some((true, char_arg, radix_arg))
                     } else {
                         None
                     }
                 },
                 hir::ExprKind::Call(to_digits_call, [char_arg, radix_arg]) => {
-                    if let hir::ExprKind::Path(to_digits_path) = &to_digits_call.kind
-                        && let to_digits_call_res = cx.qpath_res(to_digits_path, to_digits_call.hir_id)
-                        && let Some(to_digits_def_id) = to_digits_call_res.opt_def_id()
-                        && match_def_path(
-                            cx,
-                            to_digits_def_id,
-                            &["core", "char", "methods", "<impl char>", "to_digit"],
-                        )
-                    {
+                    if paths::CHAR_TO_DIGIT.matches_path(cx, to_digits_call) {
                         Some((false, char_arg, radix_arg))
                     } else {
                         None
diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs
index 2d88c490b1a..e3f28908ff8 100644
--- a/clippy_lints/src/unused_io_amount.rs
+++ b/clippy_lints/src/unused_io_amount.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::macros::{is_panic, root_macro_call_first_node};
-use clippy_utils::{is_res_lang_ctor, is_trait_method, match_def_path, match_trait_method, paths, peel_blocks};
+use clippy_utils::{is_res_lang_ctor, paths, peel_blocks};
 use hir::{ExprKind, HirId, PatKind};
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
@@ -93,14 +93,14 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
                 return;
             }
 
-            let async_paths: [&[&str]; 4] = [
+            let async_paths = [
                 &paths::TOKIO_IO_ASYNCREADEXT,
                 &paths::TOKIO_IO_ASYNCWRITEEXT,
                 &paths::FUTURES_IO_ASYNCREADEXT,
                 &paths::FUTURES_IO_ASYNCWRITEEXT,
             ];
 
-            if async_paths.into_iter().any(|path| match_def_path(cx, trait_id, path)) {
+            if async_paths.into_iter().any(|path| path.matches(cx, trait_id)) {
                 return;
             }
         }
@@ -291,19 +291,28 @@ fn check_io_mode(cx: &LateContext<'_>, call: &hir::Expr<'_>) -> Option<IoOp> {
         },
     };
 
-    match (
-        is_trait_method(cx, call, sym::IoRead),
-        is_trait_method(cx, call, sym::IoWrite),
-        match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCREADEXT)
-            || match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCREADEXT),
-        match_trait_method(cx, call, &paths::TOKIO_IO_ASYNCWRITEEXT)
-            || match_trait_method(cx, call, &paths::FUTURES_IO_ASYNCWRITEEXT),
-    ) {
-        (true, _, _, _) => Some(IoOp::SyncRead(vectorized)),
-        (_, true, _, _) => Some(IoOp::SyncWrite(vectorized)),
-        (_, _, true, _) => Some(IoOp::AsyncRead(vectorized)),
-        (_, _, _, true) => Some(IoOp::AsyncWrite(vectorized)),
-        _ => None,
+    if let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(call.hir_id)
+        && let Some(trait_def_id) = cx.tcx.trait_of_item(method_def_id)
+    {
+        if let Some(diag_name) = cx.tcx.get_diagnostic_name(trait_def_id) {
+            match diag_name {
+                sym::IoRead => Some(IoOp::SyncRead(vectorized)),
+                sym::IoWrite => Some(IoOp::SyncWrite(vectorized)),
+                _ => None,
+            }
+        } else if paths::FUTURES_IO_ASYNCREADEXT.matches(cx, trait_def_id)
+            || paths::TOKIO_IO_ASYNCREADEXT.matches(cx, trait_def_id)
+        {
+            Some(IoOp::AsyncRead(vectorized))
+        } else if paths::TOKIO_IO_ASYNCWRITEEXT.matches(cx, trait_def_id)
+            || paths::FUTURES_IO_ASYNCWRITEEXT.matches(cx, trait_def_id)
+        {
+            Some(IoOp::AsyncWrite(vectorized))
+        } else {
+            None
+        }
+    } else {
+        None
     }
 }
 
diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs
index b7dcd2ffb0e..812c4df4ddd 100644
--- a/clippy_lints/src/utils/author.rs
+++ b/clippy_lints/src/utils/author.rs
@@ -1,16 +1,18 @@
-use clippy_utils::{get_attr, higher};
+use clippy_utils::{MaybePath, get_attr, higher, path_def_id};
+use itertools::Itertools;
 use rustc_ast::LitIntType;
 use rustc_ast::ast::{LitFloatType, LitKind};
 use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::def_id::DefId;
 use rustc_hir::{
     self as hir, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, ExprKind,
-    FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr, TyKind,
+    FnRetTy, HirId, Lit, PatExprKind, PatKind, QPath, StmtKind, StructTailExpr,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_session::declare_lint_pass;
 use rustc_span::symbol::{Ident, Symbol};
 use std::cell::Cell;
-use std::fmt::{Display, Formatter, Write as _};
+use std::fmt::{Display, Formatter};
 
 declare_lint_pass!(
     /// ### What it does
@@ -148,6 +150,15 @@ fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_,
     }
 }
 
+fn paths_static_name(cx: &LateContext<'_>, id: DefId) -> String {
+    cx.get_def_path(id)
+        .iter()
+        .map(Symbol::as_str)
+        .filter(|s| !s.starts_with('<'))
+        .join("_")
+        .to_uppercase()
+}
+
 struct Binding<T> {
     name: String,
     value: T,
@@ -257,11 +268,44 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
         chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str());
     }
 
-    fn qpath(&self, qpath: &Binding<&QPath<'_>>) {
+    fn qpath<'p>(&self, qpath: &Binding<&QPath<'_>>, has_hir_id: &Binding<&impl MaybePath<'p>>) {
         if let QPath::LangItem(lang_item, ..) = *qpath.value {
             chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))");
-        } else if let Ok(path) = path_to_string(qpath.value) {
-            chain!(self, "match_qpath({qpath}, &[{}])", path);
+        } else if let Some(def_id) = self.cx.qpath_res(qpath.value, has_hir_id.value.hir_id()).opt_def_id()
+            && !def_id.is_local()
+        {
+            bind!(self, def_id);
+            chain!(
+                self,
+                "let Some({def_id}) = cx.qpath_res({qpath}, {has_hir_id}.hir_id).opt_def_id()"
+            );
+            if let Some(name) = self.cx.tcx.get_diagnostic_name(def_id.value) {
+                chain!(self, "cx.tcx.is_diagnostic_item(sym::{name}, {def_id})");
+            } else {
+                chain!(
+                    self,
+                    "paths::{}.matches(cx, {def_id}) // Add the path to `clippy_utils::paths` if needed",
+                    paths_static_name(self.cx, def_id.value)
+                );
+            }
+        }
+    }
+
+    fn maybe_path<'p>(&self, path: &Binding<&impl MaybePath<'p>>) {
+        if let Some(id) = path_def_id(self.cx, path.value)
+            && !id.is_local()
+        {
+            if let Some(lang) = self.cx.tcx.lang_items().from_def_id(id) {
+                chain!(self, "is_path_lang_item(cx, {path}, LangItem::{}", lang.name());
+            } else if let Some(name) = self.cx.tcx.get_diagnostic_name(id) {
+                chain!(self, "is_path_diagnostic_item(cx, {path}, sym::{name})");
+            } else {
+                chain!(
+                    self,
+                    "paths::{}.matches_path(cx, {path}) // Add the path to `clippy_utils::paths` if needed",
+                    paths_static_name(self.cx, id)
+                );
+            }
         }
     }
 
@@ -270,7 +314,6 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
             ConstArgKind::Path(ref qpath) => {
                 bind!(self, qpath);
                 chain!(self, "let ConstArgKind::Path(ref {qpath}) = {const_arg}.kind");
-                self.qpath(qpath);
             },
             ConstArgKind::Anon(anon_const) => {
                 bind!(self, anon_const);
@@ -394,12 +437,10 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
                 bind!(self, let_expr);
                 kind!("Let({let_expr})");
                 self.pat(field!(let_expr.pat));
-                // Does what ExprKind::Cast does, only adds a clause for the type
-                // if it's a path
-                if let Some(TyKind::Path(qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) {
-                    bind!(self, qpath);
-                    chain!(self, "let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind");
-                    self.qpath(qpath);
+                if let Some(ty) = let_expr.value.ty {
+                    bind!(self, ty);
+                    chain!(self, "let Some({ty}) = {let_expr}.ty");
+                    self.maybe_path(ty);
                 }
                 self.expr(field!(let_expr.init));
             },
@@ -451,11 +492,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
             ExprKind::Cast(expr, cast_ty) => {
                 bind!(self, expr, cast_ty);
                 kind!("Cast({expr}, {cast_ty})");
-                if let TyKind::Path(ref qpath) = cast_ty.value.kind {
-                    bind!(self, qpath);
-                    chain!(self, "let TyKind::Path(ref {qpath}) = {cast_ty}.kind");
-                    self.qpath(qpath);
-                }
+                self.maybe_path(cast_ty);
                 self.expr(expr);
             },
             ExprKind::Type(expr, _ty) => {
@@ -561,10 +598,8 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
                 self.expr(object);
                 self.expr(index);
             },
-            ExprKind::Path(ref qpath) => {
-                bind!(self, qpath);
-                kind!("Path(ref {qpath})");
-                self.qpath(qpath);
+            ExprKind::Path(_) => {
+                self.maybe_path(expr);
             },
             ExprKind::AddrOf(kind, mutability, inner) => {
                 bind!(self, inner);
@@ -608,7 +643,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
                     StructTailExpr::None | StructTailExpr::DefaultFields(_) => None,
                 });
                 kind!("Struct({qpath}, {fields}, {base})");
-                self.qpath(qpath);
+                self.qpath(qpath, expr);
                 self.slice(fields, |field| {
                     self.ident(field!(field.ident));
                     self.expr(field!(field.expr));
@@ -648,7 +683,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
         self.expr(expr);
     }
 
-    fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>) {
+    fn pat_expr(&self, lit: &Binding<&hir::PatExpr<'_>>, pat: &Binding<&hir::Pat<'_>>) {
         let kind = |kind| chain!(self, "let PatExprKind::{kind} = {lit}.kind");
         macro_rules! kind {
             ($($t:tt)*) => (kind(format_args!($($t)*)));
@@ -657,15 +692,11 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
             PatExprKind::Lit { lit, negated } => {
                 bind!(self, lit);
                 bind!(self, negated);
-                kind!("Lit{{ref {lit}, {negated} }}");
+                kind!("Lit {{ ref {lit}, {negated} }}");
                 self.lit(lit);
             },
             PatExprKind::ConstBlock(_) => kind!("ConstBlock(_)"),
-            PatExprKind::Path(ref qpath) => {
-                bind!(self, qpath);
-                kind!("Path(ref {qpath})");
-                self.qpath(qpath);
-            },
+            PatExprKind::Path(_) => self.maybe_path(pat),
         }
     }
 
@@ -697,7 +728,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
             PatKind::Struct(ref qpath, fields, ignore) => {
                 bind!(self, qpath, fields);
                 kind!("Struct(ref {qpath}, {fields}, {ignore})");
-                self.qpath(qpath);
+                self.qpath(qpath, pat);
                 self.slice(fields, |field| {
                     self.ident(field!(field.ident));
                     self.pat(field!(field.pat));
@@ -711,7 +742,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
             PatKind::TupleStruct(ref qpath, fields, skip_pos) => {
                 bind!(self, qpath, fields);
                 kind!("TupleStruct(ref {qpath}, {fields}, {skip_pos:?})");
-                self.qpath(qpath);
+                self.qpath(qpath, pat);
                 self.slice(fields, |pat| self.pat(pat));
             },
             PatKind::Tuple(fields, skip_pos) => {
@@ -743,13 +774,13 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
             PatKind::Expr(lit_expr) => {
                 bind!(self, lit_expr);
                 kind!("Expr({lit_expr})");
-                self.pat_expr(lit_expr);
+                self.pat_expr(lit_expr, pat);
             },
             PatKind::Range(start, end, end_kind) => {
                 opt_bind!(self, start, end);
                 kind!("Range({start}, {end}, RangeEnd::{end_kind:?})");
-                start.if_some(|e| self.pat_expr(e));
-                end.if_some(|e| self.pat_expr(e));
+                start.if_some(|e| self.pat_expr(e, pat));
+                end.if_some(|e| self.pat_expr(e, pat));
             },
             PatKind::Slice(start, middle, end) => {
                 bind!(self, start, end);
@@ -797,32 +828,3 @@ fn has_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool {
     let attrs = cx.tcx.hir_attrs(hir_id);
     get_attr(cx.sess(), attrs, "author").count() > 0
 }
-
-fn path_to_string(path: &QPath<'_>) -> Result<String, ()> {
-    fn inner(s: &mut String, path: &QPath<'_>) -> Result<(), ()> {
-        match *path {
-            QPath::Resolved(_, path) => {
-                for (i, segment) in path.segments.iter().enumerate() {
-                    if i > 0 {
-                        *s += ", ";
-                    }
-                    write!(s, "{:?}", segment.ident.as_str()).unwrap();
-                }
-            },
-            QPath::TypeRelative(ty, segment) => match &ty.kind {
-                TyKind::Path(inner_path) => {
-                    inner(s, inner_path)?;
-                    *s += ", ";
-                    write!(s, "{:?}", segment.ident.as_str()).unwrap();
-                },
-                other => write!(s, "/* unimplemented: {other:?}*/").unwrap(),
-            },
-            QPath::LangItem(..) => return Err(()),
-        }
-
-        Ok(())
-    }
-    let mut s = String::new();
-    inner(&mut s, path)?;
-    Ok(s)
-}
diff --git a/clippy_lints_internal/src/collapsible_calls.rs b/clippy_lints_internal/src/collapsible_calls.rs
index d7967a0cc02..407deb45db0 100644
--- a/clippy_lints_internal/src/collapsible_calls.rs
+++ b/clippy_lints_internal/src/collapsible_calls.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
-use clippy_utils::{SpanlessEq, is_expr_path_def_path, is_lint_allowed, peel_blocks_with_stmt};
+use clippy_utils::{SpanlessEq, is_lint_allowed, peel_blocks_with_stmt};
 use rustc_errors::Applicability;
 use rustc_hir::{Closure, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -10,6 +10,8 @@ use rustc_span::Span;
 
 use std::borrow::{Borrow, Cow};
 
+use crate::internal_paths;
+
 declare_tool_lint! {
     /// ### What it does
     /// Lints `span_lint_and_then` function calls, where the
@@ -80,7 +82,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
         }
 
         if let ExprKind::Call(func, [call_cx, call_lint, call_sp, call_msg, call_f]) = expr.kind
-            && is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"])
+            && internal_paths::SPAN_LINT_AND_THEN.matches_path(cx, func)
             && let ExprKind::Closure(&Closure { body, .. }) = call_f.kind
             && let body = cx.tcx.hir_body(body)
             && let only_expr = peel_blocks_with_stmt(body.value)
diff --git a/clippy_lints_internal/src/internal_paths.rs b/clippy_lints_internal/src/internal_paths.rs
new file mode 100644
index 00000000000..29ad3b85da1
--- /dev/null
+++ b/clippy_lints_internal/src/internal_paths.rs
@@ -0,0 +1,17 @@
+use clippy_utils::paths::PathLookup;
+use clippy_utils::{PathNS, sym, type_path, value_path};
+
+// Paths inside rustc
+pub static EARLY_LINT_PASS: PathLookup = type_path!(rustc_lint::passes::EarlyLintPass);
+pub static KW_MODULE: PathLookup = type_path!(rustc_span::symbol::kw);
+pub static LINT: PathLookup = type_path!(rustc_lint_defs::Lint);
+pub static SYMBOL: PathLookup = type_path!(rustc_span::symbol::Symbol);
+pub static SYMBOL_AS_STR: PathLookup = value_path!(rustc_span::symbol::Symbol::as_str);
+pub static SYM_MODULE: PathLookup = type_path!(rustc_span::symbol::sym);
+pub static SYNTAX_CONTEXT: PathLookup = type_path!(rustc_span::hygiene::SyntaxContext);
+
+// Paths in clippy itself
+pub static CLIPPY_SYM_MODULE: PathLookup = type_path!(clippy_utils::sym);
+pub static MSRV_STACK: PathLookup = type_path!(clippy_utils::msrvs::MsrvStack);
+pub static PATH_LOOKUP_NEW: PathLookup = value_path!(clippy_utils::paths::PathLookup::new);
+pub static SPAN_LINT_AND_THEN: PathLookup = value_path!(clippy_utils::diagnostics::span_lint_and_then);
diff --git a/clippy_lints_internal/src/invalid_paths.rs b/clippy_lints_internal/src/invalid_paths.rs
deleted file mode 100644
index bee87efa3fc..00000000000
--- a/clippy_lints_internal/src/invalid_paths.rs
+++ /dev/null
@@ -1,108 +0,0 @@
-use clippy_utils::consts::{ConstEvalCtxt, Constant};
-use clippy_utils::def_path_res;
-use clippy_utils::diagnostics::span_lint;
-use rustc_hir as hir;
-use rustc_hir::Item;
-use rustc_hir::def::DefKind;
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_lint_defs::declare_tool_lint;
-use rustc_middle::ty::fast_reject::SimplifiedType;
-use rustc_middle::ty::{self, FloatTy};
-use rustc_session::declare_lint_pass;
-use rustc_span::symbol::Symbol;
-
-declare_tool_lint! {
-    /// ### What it does
-    /// Checks the paths module for invalid paths.
-    ///
-    /// ### Why is this bad?
-    /// It indicates a bug in the code.
-    ///
-    /// ### Example
-    /// None.
-    pub clippy::INVALID_PATHS,
-    Warn,
-    "invalid path",
-    report_in_external_macro: true
-}
-
-declare_lint_pass!(InvalidPaths => [INVALID_PATHS]);
-
-impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
-    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-        let local_def_id = &cx.tcx.parent_module(item.hir_id());
-        let mod_name = &cx.tcx.item_name(local_def_id.to_def_id());
-        if mod_name.as_str() == "paths"
-            && let hir::ItemKind::Const(.., body_id) = item.kind
-            && let Some(Constant::Vec(path)) = ConstEvalCtxt::with_env(
-                cx.tcx,
-                ty::TypingEnv::post_analysis(cx.tcx, item.owner_id),
-                cx.tcx.typeck(item.owner_id),
-            )
-            .eval_simple(cx.tcx.hir_body(body_id).value)
-            && let Some(path) = path
-                .iter()
-                .map(|x| {
-                    if let Constant::Str(s) = x {
-                        Some(s.as_str())
-                    } else {
-                        None
-                    }
-                })
-                .collect::<Option<Vec<&str>>>()
-            && !check_path(cx, &path[..])
-        {
-            span_lint(cx, INVALID_PATHS, item.span, "invalid path");
-        }
-    }
-}
-
-// This is not a complete resolver for paths. It works on all the paths currently used in the paths
-// module.  That's all it does and all it needs to do.
-pub fn check_path(cx: &LateContext<'_>, path: &[&str]) -> bool {
-    if !def_path_res(cx.tcx, path).is_empty() {
-        return true;
-    }
-
-    // Some implementations can't be found by `path_to_res`, particularly inherent
-    // implementations of native types. Check lang items.
-    let path_syms: Vec<_> = path.iter().map(|p| Symbol::intern(p)).collect();
-    let lang_items = cx.tcx.lang_items();
-    // This list isn't complete, but good enough for our current list of paths.
-    let incoherent_impls = [
-        SimplifiedType::Float(FloatTy::F32),
-        SimplifiedType::Float(FloatTy::F64),
-        SimplifiedType::Slice,
-        SimplifiedType::Str,
-        SimplifiedType::Bool,
-        SimplifiedType::Char,
-    ]
-    .iter()
-    .flat_map(|&ty| cx.tcx.incoherent_impls(ty).iter())
-    .copied();
-    for item_def_id in lang_items.iter().map(|(_, def_id)| def_id).chain(incoherent_impls) {
-        let lang_item_path = cx.get_def_path(item_def_id);
-        if path_syms.starts_with(&lang_item_path)
-            && let [item] = &path_syms[lang_item_path.len()..]
-        {
-            if matches!(
-                cx.tcx.def_kind(item_def_id),
-                DefKind::Mod | DefKind::Enum | DefKind::Trait
-            ) {
-                for child in cx.tcx.module_children(item_def_id) {
-                    if child.ident.name == *item {
-                        return true;
-                    }
-                }
-            } else {
-                for child in cx.tcx.associated_item_def_ids(item_def_id) {
-                    if cx.tcx.item_name(*child) == *item {
-                        return true;
-                    }
-                }
-            }
-        }
-    }
-
-    false
-}
diff --git a/clippy_lints_internal/src/lib.rs b/clippy_lints_internal/src/lib.rs
index b02d378619c..da37fd3d827 100644
--- a/clippy_lints_internal/src/lib.rs
+++ b/clippy_lints_internal/src/lib.rs
@@ -32,7 +32,7 @@ extern crate rustc_span;
 
 mod almost_standard_lint_formulation;
 mod collapsible_calls;
-mod invalid_paths;
+mod internal_paths;
 mod lint_without_lint_pass;
 mod msrv_attr_impl;
 mod outer_expn_data_pass;
@@ -46,7 +46,6 @@ use rustc_lint::{Lint, LintStore};
 static LINTS: &[&Lint] = &[
     almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION,
     collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS,
-    invalid_paths::INVALID_PATHS,
     lint_without_lint_pass::DEFAULT_LINT,
     lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE,
     lint_without_lint_pass::LINT_WITHOUT_LINT_PASS,
@@ -66,10 +65,9 @@ pub fn register_lints(store: &mut LintStore) {
     store.register_early_pass(|| Box::new(unsorted_clippy_utils_paths::UnsortedClippyUtilsPaths));
     store.register_early_pass(|| Box::new(produce_ice::ProduceIce));
     store.register_late_pass(|_| Box::new(collapsible_calls::CollapsibleCalls));
-    store.register_late_pass(|_| Box::new(invalid_paths::InvalidPaths));
     store.register_late_pass(|_| Box::<symbols::Symbols>::default());
     store.register_late_pass(|_| Box::<lint_without_lint_pass::LintWithoutLintPass>::default());
-    store.register_late_pass(|_| Box::<unnecessary_def_path::UnnecessaryDefPath>::default());
+    store.register_late_pass(|_| Box::new(unnecessary_def_path::UnnecessaryDefPath));
     store.register_late_pass(|_| Box::new(outer_expn_data_pass::OuterExpnDataPass));
     store.register_late_pass(|_| Box::new(msrv_attr_impl::MsrvAttrImpl));
     store.register_late_pass(|_| Box::new(almost_standard_lint_formulation::AlmostStandardFormulation::new()));
diff --git a/clippy_lints_internal/src/lint_without_lint_pass.rs b/clippy_lints_internal/src/lint_without_lint_pass.rs
index 6a75defcce3..655d8fb8d1b 100644
--- a/clippy_lints_internal/src/lint_without_lint_pass.rs
+++ b/clippy_lints_internal/src/lint_without_lint_pass.rs
@@ -1,6 +1,7 @@
+use crate::internal_paths;
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::is_lint_allowed;
 use clippy_utils::macros::root_macro_call_first_node;
-use clippy_utils::{is_lint_allowed, match_def_path, paths};
 use rustc_ast::ast::LitKind;
 use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
 use rustc_hir as hir;
@@ -209,10 +210,10 @@ pub(super) fn is_lint_ref_type(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
         && let TyKind::Path(ref path) = inner.kind
         && let Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, inner.hir_id)
     {
-        return match_def_path(cx, def_id, &paths::LINT);
+        internal_paths::LINT.matches(cx, def_id)
+    } else {
+        false
     }
-
-    false
 }
 
 fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) {
diff --git a/clippy_lints_internal/src/msrv_attr_impl.rs b/clippy_lints_internal/src/msrv_attr_impl.rs
index dda054546e2..d48d8dc57b2 100644
--- a/clippy_lints_internal/src/msrv_attr_impl.rs
+++ b/clippy_lints_internal/src/msrv_attr_impl.rs
@@ -1,7 +1,6 @@
+use crate::internal_paths;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
-use clippy_utils::ty::match_type;
-use clippy_utils::{match_def_path, paths};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -31,7 +30,7 @@ impl LateLintPass<'_> for MsrvAttrImpl {
                 .tcx
                 .impl_trait_ref(item.owner_id)
                 .map(EarlyBinder::instantiate_identity)
-            && match_def_path(cx, trait_ref.def_id, &paths::EARLY_LINT_PASS)
+            && internal_paths::EARLY_LINT_PASS.matches(cx, trait_ref.def_id)
             && let ty::Adt(self_ty_def, _) = trait_ref.self_ty().kind()
             && self_ty_def.is_struct()
             && self_ty_def.all_fields().any(|f| {
@@ -40,7 +39,7 @@ impl LateLintPass<'_> for MsrvAttrImpl {
                     .instantiate_identity()
                     .walk()
                     .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
-                    .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV_STACK))
+                    .any(|t| internal_paths::MSRV_STACK.matches_ty(cx, t.expect_ty()))
             })
             && !items.iter().any(|item| item.ident.name.as_str() == "check_attributes")
         {
diff --git a/clippy_lints_internal/src/outer_expn_data_pass.rs b/clippy_lints_internal/src/outer_expn_data_pass.rs
index e9441964797..40951443a48 100644
--- a/clippy_lints_internal/src/outer_expn_data_pass.rs
+++ b/clippy_lints_internal/src/outer_expn_data_pass.rs
@@ -1,6 +1,6 @@
+use crate::internal_paths;
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::ty::match_type;
-use clippy_utils::{is_lint_allowed, method_calls, paths};
+use clippy_utils::{is_lint_allowed, method_calls};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
@@ -45,7 +45,7 @@ impl<'tcx> LateLintPass<'tcx> for OuterExpnDataPass {
             && let (self_arg, args) = arg_lists[1]
             && args.is_empty()
             && let self_ty = cx.typeck_results().expr_ty(self_arg).peel_refs()
-            && match_type(cx, self_ty, &paths::SYNTAX_CONTEXT)
+            && internal_paths::SYNTAX_CONTEXT.matches_ty(cx, self_ty)
         {
             span_lint_and_sugg(
                 cx,
diff --git a/clippy_lints_internal/src/symbols.rs b/clippy_lints_internal/src/symbols.rs
index c64e5821916..bf166988a0c 100644
--- a/clippy_lints_internal/src/symbols.rs
+++ b/clippy_lints_internal/src/symbols.rs
@@ -1,6 +1,5 @@
+use crate::internal_paths;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::ty::match_type;
-use clippy_utils::{def_path_def_ids, match_def_path, paths};
 use rustc_ast::LitKind;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::Applicability;
@@ -69,12 +68,12 @@ impl_lint_pass!(Symbols => [INTERNING_LITERALS, SYMBOL_AS_STR]);
 impl<'tcx> LateLintPass<'tcx> for Symbols {
     fn check_crate(&mut self, cx: &LateContext<'_>) {
         let modules = [
-            ("kw", &paths::KW_MODULE[..]),
-            ("sym", &paths::SYM_MODULE),
-            ("sym", &paths::CLIPPY_SYM_MODULE),
+            ("kw", &internal_paths::KW_MODULE),
+            ("sym", &internal_paths::SYM_MODULE),
+            ("sym", &internal_paths::CLIPPY_SYM_MODULE),
         ];
         for (prefix, module) in modules {
-            for def_id in def_path_def_ids(cx.tcx, module) {
+            for def_id in module.get(cx) {
                 // When linting `clippy_utils` itself we can't use `module_children` as it's a local def id. It will
                 // still lint but the suggestion will say to add it to `sym.rs` even if it's already there
                 if def_id.is_local() {
@@ -84,7 +83,7 @@ impl<'tcx> LateLintPass<'tcx> for Symbols {
                 for item in cx.tcx.module_children(def_id) {
                     if let Res::Def(DefKind::Const, item_def_id) = item.res
                         && let ty = cx.tcx.type_of(item_def_id).instantiate_identity()
-                        && match_type(cx, ty, &paths::SYMBOL)
+                        && internal_paths::SYMBOL.matches_ty(cx, ty)
                         && let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(item_def_id)
                         && let Some(value) = value.to_u32().discard_err()
                     {
@@ -160,7 +159,7 @@ fn suggestion(symbols: &mut FxHashMap<u32, (&'static str, Symbol)>, name: Symbol
 fn as_str_span(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Span> {
     if let ExprKind::MethodCall(_, recv, [], _) = expr.kind
         && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
-        && match_def_path(cx, method_def_id, &paths::SYMBOL_AS_STR)
+        && internal_paths::SYMBOL_AS_STR.matches(cx, method_def_id)
     {
         Some(recv.span.shrink_to_hi().to(expr.span.shrink_to_hi()))
     } else {
diff --git a/clippy_lints_internal/src/unnecessary_def_path.rs b/clippy_lints_internal/src/unnecessary_def_path.rs
index 6bdfbed55b0..2e9b8c2d53d 100644
--- a/clippy_lints_internal/src/unnecessary_def_path.rs
+++ b/clippy_lints_internal/src/unnecessary_def_path.rs
@@ -1,23 +1,13 @@
-use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{def_path_def_ids, is_lint_allowed, match_any_def_paths, peel_hir_expr_refs};
-use rustc_ast::ast::LitKind;
-use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
-use rustc_errors::Applicability;
-use rustc_hir::def::{DefKind, Res};
+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 rustc_hir::def_id::DefId;
-use rustc_hir::{Expr, ExprKind, LetStmt, Mutability, Node};
+use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_lint_defs::declare_tool_lint;
+use rustc_lint_defs::{declare_lint_pass, declare_tool_lint};
 use rustc_middle::mir::ConstValue;
-use rustc_middle::mir::interpret::{Allocation, GlobalAlloc};
-use rustc_middle::ty::{self, Ty};
-use rustc_session::impl_lint_pass;
-use rustc_span::Span;
 use rustc_span::symbol::Symbol;
 
-use std::str;
-
 declare_tool_lint! {
     /// ### What it does
     /// Checks for usage of def paths when a diagnostic item or a `LangItem` could be used.
@@ -28,12 +18,14 @@ declare_tool_lint! {
     ///
     /// ### Example
     /// ```rust,ignore
-    /// utils::match_type(cx, ty, &paths::VEC)
+    /// pub static VEC: PathLookup = path!(alloc::vec::Vec);
+    ///
+    /// VEC.contains_ty(cx, ty)
     /// ```
     ///
     /// Use instead:
     /// ```rust,ignore
-    /// utils::is_type_diagnostic_item(cx, ty, sym::Vec)
+    /// is_type_diagnostic_item(cx, ty, sym::Vec)
     /// ```
     pub clippy::UNNECESSARY_DEF_PATH,
     Warn,
@@ -41,259 +33,67 @@ declare_tool_lint! {
     report_in_external_macro: true
 }
 
-impl_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]);
-
-#[derive(Default)]
-pub struct UnnecessaryDefPath {
-    array_def_ids: FxIndexSet<(DefId, Span)>,
-    linted_def_ids: FxHashSet<DefId>,
-}
+declare_lint_pass!(UnnecessaryDefPath => [UNNECESSARY_DEF_PATH]);
 
 impl<'tcx> LateLintPass<'tcx> for UnnecessaryDefPath {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if is_lint_allowed(cx, UNNECESSARY_DEF_PATH, expr.hir_id) {
-            return;
-        }
-
-        match expr.kind {
-            ExprKind::Call(func, args) => self.check_call(cx, func, args, expr.span),
-            ExprKind::Array(elements) => self.check_array(cx, elements, expr.span),
-            _ => {},
-        }
-    }
-
-    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
-        for &(def_id, span) in &self.array_def_ids {
-            if self.linted_def_ids.contains(&def_id) {
-                continue;
-            }
-
-            let (msg, sugg) = if let Some(sym) = cx.tcx.get_diagnostic_name(def_id) {
-                ("diagnostic item", format!("sym::{sym}"))
-            } else if let Some(sym) = get_lang_item_name(cx, def_id) {
-                ("language item", format!("LangItem::{sym}"))
-            } else {
-                continue;
-            };
-
-            span_lint_and_help(
-                cx,
-                UNNECESSARY_DEF_PATH,
-                span,
-                format!("hardcoded path to a {msg}"),
-                None,
-                format!("convert all references to use `{sugg}`"),
-            );
-        }
-    }
-}
-
-impl UnnecessaryDefPath {
-    #[allow(clippy::too_many_lines)]
-    fn check_call(&mut self, cx: &LateContext<'_>, func: &Expr<'_>, args: &[Expr<'_>], span: Span) {
-        enum Item {
-            LangItem(&'static str),
-            DiagnosticItem(Symbol),
-        }
-        static PATHS: &[&[&str]] = &[
-            &["clippy_utils", "match_def_path"],
-            &["clippy_utils", "match_trait_method"],
-            &["clippy_utils", "ty", "match_type"],
-            &["clippy_utils", "is_expr_path_def_path"],
-        ];
-
-        if let [cx_arg, def_arg, args @ ..] = args
-            && let ExprKind::Path(path) = &func.kind
-            && let Some(id) = cx.qpath_res(path, func.hir_id).opt_def_id()
-            && let Some(which_path) = match_any_def_paths(cx, id, PATHS)
-            && let item_arg = if which_path == 4 { &args[1] } else { &args[0] }
-            // Extract the path to the matched type
-            && let Some(segments) = path_to_matched_type(cx, item_arg)
-            && let segments = segments.iter().map(|sym| &**sym).collect::<Vec<_>>()
-            && let Some(def_id) = def_path_def_ids(cx.tcx, &segments[..]).next()
+        if let ExprKind::Call(ctor, [_, path]) = expr.kind
+            && internal_paths::PATH_LOOKUP_NEW.matches_path(cx, ctor)
+            && let ExprKind::Array(segments) = peel_ref_operators(cx, path).kind
+            && let Some(macro_id) = expr.span.ctxt().outer_expn_data().macro_def_id
         {
-            // Check if the target item is a diagnostic item or LangItem.
-            #[rustfmt::skip]
-            let (msg, item) = if let Some(item_name)
-                = cx.tcx.diagnostic_items(def_id.krate).id_to_name.get(&def_id)
-            {
-                (
-                    "use of a def path to a diagnostic item",
-                    Item::DiagnosticItem(*item_name),
-                )
-            } else if let Some(item_name) = get_lang_item_name(cx, def_id) {
-                (
-                    "use of a def path to a `LangItem`",
-                    Item::LangItem(item_name),
-                )
-            } else {
-                return;
-            };
-
-            let has_ctor = match cx.tcx.def_kind(def_id) {
-                DefKind::Struct => {
-                    let variant = cx.tcx.adt_def(def_id).non_enum_variant();
-                    variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public())
-                },
-                DefKind::Variant => {
-                    let variant = cx.tcx.adt_def(cx.tcx.parent(def_id)).variant_with_id(def_id);
-                    variant.ctor.is_some() && variant.fields.iter().all(|f| f.vis.is_public())
-                },
-                _ => false,
+            let ns = match cx.tcx.item_name(macro_id).as_str() {
+                "type_path" => PathNS::Type,
+                "value_path" => PathNS::Value,
+                "macro_path" => PathNS::Macro,
+                _ => unreachable!(),
             };
 
-            let mut app = Applicability::MachineApplicable;
-            let cx_snip = snippet_with_applicability(cx, cx_arg.span, "..", &mut app);
-            let def_snip = snippet_with_applicability(cx, def_arg.span, "..", &mut app);
-            let (sugg, with_note) = match (which_path, item) {
-                // match_def_path
-                (0, Item::DiagnosticItem(item)) => (
-                    format!("{cx_snip}.tcx.is_diagnostic_item(sym::{item}, {def_snip})"),
-                    has_ctor,
-                ),
-                (0, Item::LangItem(item)) => (
-                    format!("{cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some({def_snip})"),
-                    has_ctor,
-                ),
-                // match_trait_method
-                (1, Item::DiagnosticItem(item)) => {
-                    (format!("is_trait_method({cx_snip}, {def_snip}, sym::{item})"), false)
-                },
-                // match_type
-                (2, Item::DiagnosticItem(item)) => (
-                    format!("is_type_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"),
-                    false,
-                ),
-                (2, Item::LangItem(item)) => (
-                    format!("is_type_lang_item({cx_snip}, {def_snip}, LangItem::{item})"),
-                    false,
-                ),
-                // is_expr_path_def_path
-                (3, Item::DiagnosticItem(item)) if has_ctor => (
-                    format!("is_res_diag_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), sym::{item})",),
-                    false,
-                ),
-                (3, Item::LangItem(item)) if has_ctor => (
-                    format!("is_res_lang_ctor({cx_snip}, path_res({cx_snip}, {def_snip}), LangItem::{item})",),
-                    false,
-                ),
-                (3, Item::DiagnosticItem(item)) => (
-                    format!("is_path_diagnostic_item({cx_snip}, {def_snip}, sym::{item})"),
-                    false,
-                ),
-                (3, Item::LangItem(item)) => (
-                    format!(
-                        "path_res({cx_snip}, {def_snip}).opt_def_id()\
-                            .map_or(false, |id| {cx_snip}.tcx.lang_items().get(LangItem::{item}) == Some(id))",
-                    ),
-                    false,
-                ),
-                _ => return,
-            };
-
-            span_lint_and_then(cx, UNNECESSARY_DEF_PATH, span, msg, |diag| {
-                diag.span_suggestion(span, "try", sugg, app);
-                if with_note {
-                    diag.help(
-                        "if this `DefId` came from a constructor expression or pattern then the \
-                                parent `DefId` should be used instead",
+            let path: Vec<Symbol> = segments
+                .iter()
+                .map(|segment| {
+                    if let Some(const_def_id) = path_def_id(cx, segment)
+                        && let Ok(ConstValue::Scalar(value)) = cx.tcx.const_eval_poly(const_def_id)
+                        && let Some(value) = value.to_u32().discard_err()
+                    {
+                        Symbol::new(value)
+                    } else {
+                        panic!("failed to resolve path {:?}", expr.span);
+                    }
+                })
+                .collect();
+
+            for def_id in lookup_path(cx.tcx, ns, &path) {
+                if let Some(name) = cx.tcx.get_diagnostic_name(def_id) {
+                    span_lint_and_then(
+                        cx,
+                        UNNECESSARY_DEF_PATH,
+                        expr.span.source_callsite(),
+                        format!("a diagnostic name exists for this path: sym::{name}"),
+                        |diag| {
+                            diag.help(
+                                "remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead",
+                            );
+                            diag.help("see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils");
+                        },
+                    );
+                } else if let Some(item_name) = get_lang_item_name(cx, def_id) {
+                    span_lint_and_then(
+                        cx,
+                        UNNECESSARY_DEF_PATH,
+                        expr.span.source_callsite(),
+                        format!("a language item exists for this path: LangItem::{item_name}"),
+                        |diag| {
+                            diag.help("remove the `PathLookup` and use utilities such as `cx.tcx.lang_items` instead");
+                            diag.help("see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=lang&filter-crate=clippy_utils");
+                        },
                     );
                 }
-            });
-
-            self.linted_def_ids.insert(def_id);
-        }
-    }
-
-    fn check_array(&mut self, cx: &LateContext<'_>, elements: &[Expr<'_>], span: Span) {
-        let Some(path) = path_from_array(elements) else { return };
-
-        for def_id in def_path_def_ids(cx.tcx, &path.iter().map(AsRef::as_ref).collect::<Vec<_>>()) {
-            self.array_def_ids.insert((def_id, span));
-        }
-    }
-}
-
-fn path_to_matched_type(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Vec<String>> {
-    match peel_hir_expr_refs(expr).0.kind {
-        ExprKind::Path(ref qpath) => match cx.qpath_res(qpath, expr.hir_id) {
-            Res::Local(hir_id) => {
-                if let Node::LetStmt(LetStmt { init: Some(init), .. }) = cx.tcx.parent_hir_node(hir_id) {
-                    path_to_matched_type(cx, init)
-                } else {
-                    None
-                }
-            },
-            Res::Def(DefKind::Static { .. }, def_id) => read_mir_alloc_def_path(
-                cx,
-                cx.tcx.eval_static_initializer(def_id).ok()?.inner(),
-                cx.tcx.type_of(def_id).instantiate_identity(),
-            ),
-            Res::Def(DefKind::Const, def_id) => match cx.tcx.const_eval_poly(def_id).ok()? {
-                ConstValue::Indirect { alloc_id, offset } if offset.bytes() == 0 => {
-                    let alloc = cx.tcx.global_alloc(alloc_id).unwrap_memory();
-                    read_mir_alloc_def_path(cx, alloc.inner(), cx.tcx.type_of(def_id).instantiate_identity())
-                },
-                _ => None,
-            },
-            _ => None,
-        },
-        ExprKind::Array(exprs) => path_from_array(exprs),
-        _ => None,
-    }
-}
-
-fn read_mir_alloc_def_path<'tcx>(cx: &LateContext<'tcx>, alloc: &'tcx Allocation, ty: Ty<'_>) -> Option<Vec<String>> {
-    let (alloc, ty) = if let ty::Ref(_, ty, Mutability::Not) = *ty.kind() {
-        let &alloc = alloc.provenance().ptrs().values().next()?;
-        if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc.alloc_id()) {
-            (alloc.inner(), ty)
-        } else {
-            return None;
+            }
         }
-    } else {
-        (alloc, ty)
-    };
-
-    if let ty::Array(ty, _) | ty::Slice(ty) = *ty.kind()
-        && let ty::Ref(_, ty, Mutability::Not) = *ty.kind()
-        && ty.is_str()
-    {
-        alloc
-            .provenance()
-            .ptrs()
-            .values()
-            .map(|&alloc| {
-                if let GlobalAlloc::Memory(alloc) = cx.tcx.global_alloc(alloc.alloc_id()) {
-                    let alloc = alloc.inner();
-                    str::from_utf8(alloc.inspect_with_uninit_and_ptr_outside_interpreter(0..alloc.len()))
-                        .ok()
-                        .map(ToOwned::to_owned)
-                } else {
-                    None
-                }
-            })
-            .collect()
-    } else {
-        None
     }
 }
 
-fn path_from_array(exprs: &[Expr<'_>]) -> Option<Vec<String>> {
-    exprs
-        .iter()
-        .map(|expr| {
-            if let ExprKind::Lit(lit) = &expr.kind
-                && let LitKind::Str(sym, _) = lit.node
-            {
-                return Some((*sym.as_str()).to_owned());
-            }
-
-            None
-        })
-        .collect()
-}
-
 fn get_lang_item_name(cx: &LateContext<'_>, def_id: DefId) -> Option<&'static str> {
     if let Some((lang_item, _)) = cx.tcx.lang_items().iter().find(|(_, id)| *id == def_id) {
         Some(lang_item.variant_name())
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index 187dfa4dda8..44e72a8d16a 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -96,8 +96,9 @@ 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::{DefKind, Res};
-use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE, LocalDefId, LocalModDefId};
+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::definitions::{DefPath, DefPathData};
 use rustc_hir::hir_id::{HirIdMap, HirIdSet};
 use rustc_hir::intravisit::{FnKind, Visitor, walk_expr};
@@ -105,8 +106,8 @@ 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, PrimTy, QPath,
-    Stmt, StmtKind, TraitFn, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def,
+    Mutability, Node, OwnerId, OwnerNode, Param, Pat, PatExpr, PatExprKind, PatKind, Path, PathSegment, QPath, Stmt,
+    StmtKind, TraitFn, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp, def,
 };
 use rustc_lexer::{TokenKind, tokenize};
 use rustc_lint::{LateContext, Level, Lint, LintContext};
@@ -347,14 +348,6 @@ pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
     }
 }
 
-/// Checks if the method call given in `expr` belongs to the given trait.
-/// This is a deprecated function, consider using [`is_trait_method`].
-pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
-    let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
-    let trt_id = cx.tcx.trait_of_item(def_id);
-    trt_id.is_some_and(|trt_id| match_def_path(cx, trt_id, path))
-}
-
 /// Checks if the given method call expression calls an inherent method.
 pub fn is_inherent_method_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     if let Some(method_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
@@ -438,44 +431,6 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tc
         })
 }
 
-/// THIS METHOD IS DEPRECATED. Matches a `QPath` against a slice of segment string literals.
-///
-/// This method is deprecated and will eventually be removed since it does not match against the
-/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
-/// `QPath::Resolved.1.res.opt_def_id()`.
-///
-/// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
-/// `rustc_hir::QPath`.
-///
-/// # Examples
-/// ```rust,ignore
-/// match_qpath(path, &["std", "rt", "begin_unwind"])
-/// ```
-pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
-    match *path {
-        QPath::Resolved(_, path) => match_path(path, segments),
-        QPath::TypeRelative(ty, segment) => match ty.kind {
-            TyKind::Path(ref inner_path) => {
-                if let [prefix @ .., end] = segments
-                    && match_qpath(inner_path, prefix)
-                {
-                    return segment.ident.name.as_str() == *end;
-                }
-                false
-            },
-            _ => false,
-        },
-        QPath::LangItem(..) => false,
-    }
-}
-
-/// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
-///
-/// Please use `is_path_diagnostic_item` if the target is a diagnostic item.
-pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
-    path_def_id(cx, expr).is_some_and(|id| match_def_path(cx, id, segments))
-}
-
 /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
 /// it matches the given lang item.
 pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool {
@@ -492,34 +447,6 @@ pub fn is_path_diagnostic_item<'tcx>(
     path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id))
 }
 
-/// THIS METHOD IS DEPRECATED. Matches a `Path` against a slice of segment string literals.
-///
-/// This method is deprecated and will eventually be removed since it does not match against the
-/// entire path or resolved `DefId`. Prefer using `match_def_path`. Consider getting a `DefId` from
-/// `QPath::Resolved.1.res.opt_def_id()`.
-///
-/// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
-/// `rustc_hir::Path`.
-///
-/// # Examples
-///
-/// ```rust,ignore
-/// if match_path(&trait_ref.path, &paths::HASH) {
-///     // This is the `std::hash::Hash` trait.
-/// }
-///
-/// if match_path(ty_path, &["rustc", "lint", "Lint"]) {
-///     // This is a `rustc_middle::lint::Lint`.
-/// }
-/// ```
-pub fn match_path(path: &Path<'_>, segments: &[&str]) -> bool {
-    path.segments
-        .iter()
-        .rev()
-        .zip(segments.iter().rev())
-        .all(|(a, b)| a.ident.name.as_str() == *b)
-}
-
 /// If the expression is a path to a local, returns the canonical `HirId` of the local.
 pub fn path_to_local(expr: &Expr<'_>) -> Option<HirId> {
     if let ExprKind::Path(QPath::Resolved(None, path)) = expr.kind
@@ -586,60 +513,57 @@ 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>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<Item = DefId> + 'tcx {
+fn find_primitive_impls(tcx: TyCtxt<'_>, name: Symbol) -> &[DefId] {
     let ty = match name {
-        "bool" => SimplifiedType::Bool,
-        "char" => SimplifiedType::Char,
-        "str" => SimplifiedType::Str,
-        "array" => SimplifiedType::Array,
-        "slice" => SimplifiedType::Slice,
+        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.
-        "const_ptr" => SimplifiedType::Ptr(Mutability::Not),
-        "mut_ptr" => SimplifiedType::Ptr(Mutability::Mut),
-        "isize" => SimplifiedType::Int(IntTy::Isize),
-        "i8" => SimplifiedType::Int(IntTy::I8),
-        "i16" => SimplifiedType::Int(IntTy::I16),
-        "i32" => SimplifiedType::Int(IntTy::I32),
-        "i64" => SimplifiedType::Int(IntTy::I64),
-        "i128" => SimplifiedType::Int(IntTy::I128),
-        "usize" => SimplifiedType::Uint(UintTy::Usize),
-        "u8" => SimplifiedType::Uint(UintTy::U8),
-        "u16" => SimplifiedType::Uint(UintTy::U16),
-        "u32" => SimplifiedType::Uint(UintTy::U32),
-        "u64" => SimplifiedType::Uint(UintTy::U64),
-        "u128" => SimplifiedType::Uint(UintTy::U128),
-        "f32" => SimplifiedType::Float(FloatTy::F32),
-        "f64" => SimplifiedType::Float(FloatTy::F64),
-        _ => {
-            return [].iter().copied();
-        },
+        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).iter().copied()
+    tcx.incoherent_impls(ty)
 }
 
-fn non_local_item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
+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()
-            .filter(|item| item.ident.name == name)
-            .map(|child| child.res.expect_non_local())
-            .collect(),
+        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()
-            .filter(|assoc_def_id| tcx.item_name(*assoc_def_id) == name)
-            .map(|assoc_def_id| Res::Def(tcx.def_kind(assoc_def_id), assoc_def_id))
-            .collect(),
-        _ => Vec::new(),
+            .find(|assoc_def_id| tcx.item_name(*assoc_def_id) == name && ns.matches(tcx.def_kind(assoc_def_id).ns())),
+        _ => None,
     }
 }
 
-fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symbol) -> Vec<Res> {
+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) => {
@@ -647,138 +571,147 @@ fn local_item_children_by_name(tcx: TyCtxt<'_>, local_id: LocalDefId, name: Symb
             &root_mod
         },
         Node::Item(item) => &item.kind,
-        _ => return Vec::new(),
+        _ => return None,
     };
 
     let res = |ident: Ident, owner_id: OwnerId| {
-        if ident.name == name {
-            let def_id = owner_id.to_def_id();
-            Some(Res::Def(tcx.def_kind(def_id), def_id))
+        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()
-            .filter_map(|&item_id| {
-                let ident = tcx.hir_item(item_id).kind.ident()?;
-                res(ident, item_id.owner_id)
-            })
-            .collect(),
+        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()
-            .filter_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id))
-            .collect(),
+            .find_map(|&ImplItemRef { ident, id, .. }| res(ident, id.owner_id)),
         ItemKind::Trait(.., trait_item_refs) => trait_item_refs
             .iter()
-            .filter_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id))
-            .collect(),
-        _ => Vec::new(),
+            .find_map(|&TraitItemRef { ident, id, .. }| res(ident, id.owner_id)),
+        _ => None,
     }
 }
 
-fn item_children_by_name(tcx: TyCtxt<'_>, def_id: DefId, name: Symbol) -> Vec<Res> {
+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_children_by_name(tcx, local_id, name)
+        local_item_child_by_name(tcx, local_id, ns, name)
     } else {
-        non_local_item_children_by_name(tcx, def_id, name)
+        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) -> Vec<Res> {
-    tcx.crates(())
-        .iter()
-        .copied()
-        .filter(move |&num| tcx.crate_name(num) == name)
-        .map(CrateNum::as_def_id)
-        .map(|id| Res::Def(tcx.def_kind(id), id))
-        .collect()
+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`.
 ///
-/// Can return multiple resolutions when there are multiple versions of the same crate, e.g.
-/// `memchr::memchr` could return the functions from both memchr 1.0 and memchr 2.0.
-///
-/// Also returns multiple results when there are multiple paths under the same name e.g. `std::vec`
-/// would have both a [`DefKind::Mod`] and [`DefKind::Macro`].
+/// 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.
-pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec<Res> {
-    let (base, path) = match path {
-        [primitive] => {
-            return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)];
-        },
-        [base, path @ ..] => (base, path),
-        _ => return Vec::new(),
-    };
-
-    let base_sym = Symbol::intern(base);
-
-    let local_crate = if tcx.crate_name(LOCAL_CRATE) == base_sym {
-        Some(LOCAL_CRATE.as_def_id())
-    } else {
-        None
+///
+/// [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 crates = find_primitive_impls(tcx, base)
-        .chain(local_crate)
-        .map(|id| Res::Def(tcx.def_kind(id), id))
-        .chain(find_crates(tcx, base_sym))
-        .collect();
-
-    def_path_res_with_base(tcx, crates, path)
+    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`.
-///
-/// This is lighter than [`def_path_res`], and should be called with [`find_crates`] looking up
-/// items from the same crate repeatedly, although should still be used sparingly.
-pub fn def_path_res_with_base(tcx: TyCtxt<'_>, mut base: Vec<Res>, mut path: &[&str]) -> Vec<Res> {
-    while let [segment, rest @ ..] = path {
-        path = rest;
-        let segment = Symbol::intern(segment);
-
-        base = base
-            .into_iter()
-            .filter_map(|res| res.opt_def_id())
-            .flat_map(|def_id| {
+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(def_id)
+                    .inherent_impls(base)
                     .iter()
-                    .flat_map(|&impl_def_id| item_children_by_name(tcx, impl_def_id, segment));
-
-                let direct_children = item_children_by_name(tcx, def_id, segment);
+                    .filter_map(|&impl_def_id| item_child_by_name(tcx, impl_def_id, ns, segment));
+                out.extend(inherent_impl_children);
 
-                inherent_impl_children.chain(direct_children)
-            })
-            .collect();
+                return;
+            },
+            [segment, ref rest @ ..] => {
+                path = rest;
+                let Some(child) = item_child_by_name(tcx, base, PathNS::Type, segment) else {
+                    return;
+                };
+                base = child;
+            },
+            [] => unreachable!(),
+        }
     }
-
-    base
 }
 
-/// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`].
-pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator<Item = DefId> + use<> {
-    def_path_res(tcx, path).into_iter().filter_map(|res| res.opt_def_id())
-}
-
-/// Convenience function to get the `DefId` of a trait by path.
-/// It could be a trait or trait alias.
+/// Equivalent to a [`lookup_path`] after splitting the input string on `::`
 ///
 /// This function is expensive and should be used sparingly.
-pub fn get_trait_def_id(tcx: TyCtxt<'_>, path: &[&str]) -> Option<DefId> {
-    def_path_res(tcx, path).into_iter().find_map(|res| match res {
-        Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
-        _ => None,
-    })
+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.
@@ -2065,24 +1998,6 @@ pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
         })
 }
 
-/// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
-/// any.
-///
-/// Please use `tcx.get_diagnostic_name` if the targets are all diagnostic items.
-pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]]) -> Option<usize> {
-    let search_path = cx.get_def_path(did);
-    paths
-        .iter()
-        .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
-}
-
-/// Checks if the given `DefId` matches the path.
-pub fn match_def_path(cx: &LateContext<'_>, did: DefId, syms: &[&str]) -> bool {
-    // We should probably move to Symbols in Clippy as well rather than interning every time.
-    let path = cx.get_def_path(did);
-    syms.iter().map(|x| Symbol::intern(x)).eq(path.iter().copied())
-}
-
 /// Checks if the given `DefId` matches the `libc` item.
 pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
     let path = cx.get_def_path(did);
diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs
index 7f64ebd3b64..98423017883 100644
--- a/clippy_utils/src/paths.rs
+++ b/clippy_utils/src/paths.rs
@@ -4,62 +4,125 @@
 //! Whenever possible, please consider diagnostic items over hardcoded paths.
 //! See <https://github.com/rust-lang/rust-clippy/issues/5393> for more information.
 
-// Paths inside rustc
-pub const APPLICABILITY: [&str; 2] = ["rustc_lint_defs", "Applicability"];
-pub const APPLICABILITY_VALUES: [[&str; 3]; 4] = [
-    ["rustc_lint_defs", "Applicability", "Unspecified"],
-    ["rustc_lint_defs", "Applicability", "HasPlaceholders"],
-    ["rustc_lint_defs", "Applicability", "MaybeIncorrect"],
-    ["rustc_lint_defs", "Applicability", "MachineApplicable"],
-];
-pub const DIAG: [&str; 2] = ["rustc_errors", "Diag"];
-pub const EARLY_CONTEXT: [&str; 2] = ["rustc_lint", "EarlyContext"];
-pub const EARLY_LINT_PASS: [&str; 3] = ["rustc_lint", "passes", "EarlyLintPass"];
-pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
-pub const IDENT_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Ident", "as_str"];
-pub const KW_MODULE: [&str; 3] = ["rustc_span", "symbol", "kw"];
-pub const LATE_CONTEXT: [&str; 2] = ["rustc_lint", "LateContext"];
-pub const LINT: [&str; 2] = ["rustc_lint_defs", "Lint"];
-pub const SYMBOL: [&str; 3] = ["rustc_span", "symbol", "Symbol"];
-pub const SYMBOL_AS_STR: [&str; 4] = ["rustc_span", "symbol", "Symbol", "as_str"];
-pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"];
-pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
-pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
+use crate::{MaybePath, PathNS, lookup_path, path_def_id, sym};
+use rustc_hir::def_id::DefId;
+use rustc_lint::LateContext;
+use rustc_middle::ty::Ty;
+use rustc_span::{STDLIB_STABLE_CRATES, Symbol};
+use std::sync::OnceLock;
 
-// Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items.
-pub const CHAR_IS_ASCII: [&str; 5] = ["core", "char", "methods", "<impl char>", "is_ascii"];
-pub const IO_ERROR_NEW: [&str; 5] = ["std", "io", "error", "Error", "new"];
-pub const IO_ERRORKIND_OTHER: [&str; 5] = ["std", "io", "error", "ErrorKind", "Other"];
-pub const ALIGN_OF: [&str; 3] = ["core", "mem", "align_of"];
+/// 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:
+/// - `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])
+///
+/// [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 struct PathLookup {
+    ns: PathNS,
+    path: &'static [Symbol],
+    once: OnceLock<Vec<DefId>>,
+}
+
+impl PathLookup {
+    /// Only exported for tests and `clippy_lints_internal`
+    #[doc(hidden)]
+    pub const fn new(ns: PathNS, path: &'static [Symbol]) -> Self {
+        Self {
+            ns,
+            path,
+            once: OnceLock::new(),
+        }
+    }
+
+    /// Returns the list of [`DefId`]s that the path resolves to
+    pub fn get(&self, cx: &LateContext<'_>) -> &[DefId] {
+        self.once.get_or_init(|| lookup_path(cx.tcx, self.ns, self.path))
+    }
+
+    /// Returns the single [`DefId`] that the path resolves to, this can only be used for paths into
+    /// stdlib crates to avoid the issue of multiple [`DefId`]s being returned
+    ///
+    /// May return [`None`] in `no_std`/`no_core` environments
+    pub fn only(&self, cx: &LateContext<'_>) -> Option<DefId> {
+        let ids = self.get(cx);
+        debug_assert!(STDLIB_STABLE_CRATES.contains(&self.path[0]));
+        debug_assert!(ids.len() <= 1, "{ids:?}");
+        ids.first().copied()
+    }
+
+    /// Checks if the path resolves to the given `def_id`
+    pub fn matches(&self, cx: &LateContext<'_>, def_id: DefId) -> bool {
+        self.get(cx).contains(&def_id)
+    }
+
+    /// Resolves `maybe_path` to a [`DefId`] and checks if the [`PathLookup`] matches it
+    pub fn matches_path<'tcx>(&self, cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> bool {
+        path_def_id(cx, maybe_path).is_some_and(|def_id| self.matches(cx, def_id))
+    }
 
-// Paths in clippy itself
-pub const MSRV_STACK: [&str; 3] = ["clippy_utils", "msrvs", "MsrvStack"];
-pub const CLIPPY_SYM_MODULE: [&str; 2] = ["clippy_utils", "sym"];
+    /// Checks if the path resolves to `ty`'s definition, must be an `Adt`
+    pub fn matches_ty(&self, cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
+        ty.ty_adt_def().is_some_and(|adt| self.matches(cx, adt.did()))
+    }
+}
+
+macro_rules! path_macros {
+    ($($name:ident: $ns:expr,)*) => {
+        $(
+            /// Only exported for tests and `clippy_lints_internal`
+            #[doc(hidden)]
+            #[macro_export]
+            macro_rules! $name {
+                ($$($$seg:ident $$(::)?)*) => {
+                    PathLookup::new($ns, &[$$(sym::$$seg,)*])
+                };
+            }
+        )*
+    };
+}
+
+path_macros! {
+    type_path: PathNS::Type,
+    value_path: PathNS::Value,
+    macro_path: PathNS::Macro,
+}
+
+// Paths in `core`/`alloc`/`std`. This should be avoided and cleaned up by adding diagnostic items.
+pub static ALIGN_OF: PathLookup = value_path!(core::mem::align_of);
+pub static CHAR_TO_DIGIT: PathLookup = value_path!(char::to_digit);
+pub static IO_ERROR_NEW: PathLookup = value_path!(std::io::Error::new);
+pub static IO_ERRORKIND_OTHER_CTOR: PathLookup = value_path!(std::io::ErrorKind::Other);
+pub static ITER_STEP: PathLookup = type_path!(core::iter::Step);
+pub static SLICE_FROM_REF: PathLookup = value_path!(core::slice::from_ref);
 
 // Paths in external crates
-#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
-pub const FUTURES_IO_ASYNCREADEXT: [&str; 3] = ["futures_util", "io", "AsyncReadExt"];
-#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
-pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWriteExt"];
-pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
-pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"];
-pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"];
-pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"];
-pub const REGEX_BUILDER_NEW: [&str; 3] = ["regex", "RegexBuilder", "new"];
-pub const REGEX_BYTES_BUILDER_NEW: [&str; 4] = ["regex", "bytes", "RegexBuilder", "new"];
-pub const REGEX_BYTES_NEW: [&str; 4] = ["regex", "bytes", "Regex", "new"];
-pub const REGEX_BYTES_SET_NEW: [&str; 4] = ["regex", "bytes", "RegexSet", "new"];
-pub const REGEX_NEW: [&str; 3] = ["regex", "Regex", "new"];
-pub const REGEX_SET_NEW: [&str; 3] = ["regex", "RegexSet", "new"];
-pub const SERDE_DESERIALIZE: [&str; 3] = ["serde", "de", "Deserialize"];
-pub const SERDE_DE_VISITOR: [&str; 3] = ["serde", "de", "Visitor"];
-#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
-pub const TOKIO_FILE_OPTIONS: [&str; 5] = ["tokio", "fs", "file", "File", "options"];
-#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
-pub const TOKIO_IO_ASYNCREADEXT: [&str; 5] = ["tokio", "io", "util", "async_read_ext", "AsyncReadExt"];
-#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
-pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_write_ext", "AsyncWriteExt"];
-#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
-pub const TOKIO_IO_OPEN_OPTIONS: [&str; 4] = ["tokio", "fs", "open_options", "OpenOptions"];
-#[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
-pub const TOKIO_IO_OPEN_OPTIONS_NEW: [&str; 5] = ["tokio", "fs", "open_options", "OpenOptions", "new"];
+pub static FUTURES_IO_ASYNCREADEXT: PathLookup = type_path!(futures_util::AsyncReadExt);
+pub static FUTURES_IO_ASYNCWRITEEXT: PathLookup = type_path!(futures_util::AsyncWriteExt);
+pub static ITERTOOLS_NEXT_TUPLE: PathLookup = value_path!(itertools::Itertools::next_tuple);
+pub static PARKING_LOT_GUARDS: [PathLookup; 3] = [
+    type_path!(lock_api::mutex::MutexGuard),
+    type_path!(lock_api::rwlock::RwLockReadGuard),
+    type_path!(lock_api::rwlock::RwLockWriteGuard),
+];
+pub static REGEX_BUILDER_NEW: PathLookup = value_path!(regex::RegexBuilder::new);
+pub static REGEX_BYTES_BUILDER_NEW: PathLookup = value_path!(regex::bytes::RegexBuilder::new);
+pub static REGEX_BYTES_NEW: PathLookup = value_path!(regex::bytes::Regex::new);
+pub static REGEX_BYTES_SET_NEW: PathLookup = value_path!(regex::bytes::RegexSet::new);
+pub static REGEX_NEW: PathLookup = value_path!(regex::Regex::new);
+pub static REGEX_SET_NEW: PathLookup = value_path!(regex::RegexSet::new);
+pub static SERDE_DESERIALIZE: PathLookup = type_path!(serde::de::Deserialize);
+pub static SERDE_DE_VISITOR: PathLookup = type_path!(serde::de::Visitor);
+pub static TOKIO_FILE_OPTIONS: PathLookup = value_path!(tokio::fs::File::options);
+pub static TOKIO_IO_ASYNCREADEXT: PathLookup = type_path!(tokio::io::AsyncReadExt);
+pub static TOKIO_IO_ASYNCWRITEEXT: PathLookup = type_path!(tokio::io::AsyncWriteExt);
+pub static TOKIO_IO_OPEN_OPTIONS: PathLookup = type_path!(tokio::fs::OpenOptions);
+pub static TOKIO_IO_OPEN_OPTIONS_NEW: PathLookup = value_path!(tokio::fs::OpenOptions::new);
+pub static LAZY_STATIC: PathLookup = macro_path!(lazy_static::lazy_static);
+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`
diff --git a/clippy_utils/src/sym.rs b/clippy_utils/src/sym.rs
index 38f077134c0..94b73f37269 100644
--- a/clippy_utils/src/sym.rs
+++ b/clippy_utils/src/sym.rs
@@ -1,6 +1,6 @@
 #![allow(non_upper_case_globals)]
 
-use rustc_span::symbol::{PREDEFINED_SYMBOLS_COUNT, Symbol};
+use rustc_span::symbol::PREDEFINED_SYMBOLS_COUNT;
 
 #[doc(no_inline)]
 pub use rustc_span::sym::*;
@@ -24,33 +24,45 @@ macro_rules! generate {
         ];
 
         $(
-            pub const $name: Symbol = Symbol::new(PREDEFINED_SYMBOLS_COUNT + ${index()});
+            pub const $name: rustc_span::Symbol = rustc_span::Symbol::new(PREDEFINED_SYMBOLS_COUNT + ${index()});
         )*
     };
 }
 
 generate! {
     abs,
+    align_of,
     as_bytes,
     as_deref_mut,
     as_deref,
     as_mut,
+    AsyncReadExt,
+    AsyncWriteExt,
     Binary,
     build_hasher,
+    bytes,
     cargo_clippy: "cargo-clippy",
     Cargo_toml: "Cargo.toml",
     cast,
     chars,
     CLIPPY_ARGS,
     CLIPPY_CONF_DIR,
+    clippy_utils,
     clone_into,
     cloned,
     collect,
+    const_ptr,
     contains,
     copied,
     CRLF: "\r\n",
     Current,
+    de,
+    Deserialize,
+    diagnostics,
+    EarlyLintPass,
     ends_with,
+    error,
+    ErrorKind,
     exp,
     extend,
     finish_non_exhaustive,
@@ -58,48 +70,87 @@ generate! {
     flat_map,
     for_each,
     from_raw,
+    from_ref,
     from_str_radix,
+    fs,
+    futures_util,
     get,
+    hygiene,
     insert,
     int_roundings,
     into_bytes,
     into_owned,
     IntoIter,
+    io,
     is_ascii,
     is_empty,
     is_err,
     is_none,
     is_ok,
     is_some,
+    itertools,
+    Itertools,
+    kw,
     last,
+    lazy_static,
+    Lazy,
     LF: "\n",
+    Lint,
+    lock_api,
     LowerExp,
     LowerHex,
     max,
+    MAX,
+    mem,
     min,
+    MIN,
     mode,
     msrv,
+    msrvs,
+    MsrvStack,
+    mut_ptr,
+    mutex,
+    next_tuple,
     Octal,
+    once_cell,
+    OpenOptions,
     or_default,
+    Other,
     parse,
+    PathLookup,
+    paths,
     push,
     regex,
+    Regex,
+    RegexBuilder,
+    RegexSet,
     reserve,
     resize,
     restriction,
+    rustc_lint_defs,
+    rustc_lint,
+    rustc_span,
     rustfmt_skip,
+    rwlock,
+    serde,
     set_len,
     set_mode,
     set_readonly,
     signum,
+    span_lint_and_then,
     split_whitespace,
     split,
     Start,
+    Step,
+    symbol,
+    Symbol,
+    SyntaxContext,
     take,
     TBD,
     then_some,
     to_digit,
     to_owned,
+    tokio,
     unused_extern_crates,
     unwrap_err,
     unwrap_or_default,
@@ -107,6 +158,7 @@ generate! {
     UpperHex,
     V4,
     V6,
+    Visitor,
     Weak,
     with_capacity,
     wrapping_offset,
diff --git a/clippy_utils/src/ty/mod.rs b/clippy_utils/src/ty/mod.rs
index db723312695..901b66159c9 100644
--- a/clippy_utils/src/ty/mod.rs
+++ b/clippy_utils/src/ty/mod.rs
@@ -32,7 +32,7 @@ use std::assert_matches::debug_assert_matches;
 use std::collections::hash_map::Entry;
 use std::iter;
 
-use crate::{def_path_def_ids, match_def_path, path_res};
+use crate::{PathNS, lookup_path_str, path_res};
 
 mod type_certainty;
 pub use type_certainty::expr_type_is_certain;
@@ -229,9 +229,7 @@ pub fn has_iter_method(cx: &LateContext<'_>, probably_ref_ty: Ty<'_>) -> Option<
 /// Checks whether a type implements a trait.
 /// The function returns false in case the type contains an inference variable.
 ///
-/// See:
-/// * [`get_trait_def_id`](super::get_trait_def_id) to get a trait [`DefId`].
-/// * [Common tools for writing lints] for an example how to use this function and other options.
+/// See [Common tools for writing lints] for an example how to use this function and other options.
 ///
 /// [Common tools for writing lints]: https://github.com/rust-lang/rust-clippy/blob/master/book/src/development/common_tools_writing_lints.md#checking-if-a-type-implements-a-specific-trait
 pub fn implements_trait<'tcx>(
@@ -424,17 +422,6 @@ pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
     matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))
 }
 
-/// Checks if type is struct, enum or union type with the given def path.
-///
-/// If the type is a diagnostic item, use `is_type_diagnostic_item` instead.
-/// If you change the signature, remember to update the internal lint `MatchTypeOnDiagItem`
-pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
-    match ty.kind() {
-        ty::Adt(adt, _) => match_def_path(cx, adt.did(), path),
-        _ => false,
-    }
-}
-
 /// Checks if the drop order for a type matters.
 ///
 /// Some std types implement drop solely to deallocate memory. For these types, and composites
@@ -1131,10 +1118,7 @@ impl<'tcx> InteriorMut<'tcx> {
     pub fn new(tcx: TyCtxt<'tcx>, ignore_interior_mutability: &[String]) -> Self {
         let ignored_def_ids = ignore_interior_mutability
             .iter()
-            .flat_map(|ignored_ty| {
-                let path: Vec<&str> = ignored_ty.split("::").collect();
-                def_path_def_ids(tcx, path.as_slice())
-            })
+            .flat_map(|ignored_ty| lookup_path_str(tcx, PathNS::Type, ignored_ty))
             .collect();
 
         Self {
diff --git a/clippy_utils/src/ty/type_certainty/mod.rs b/clippy_utils/src/ty/type_certainty/mod.rs
index 3398ff8af2f..2b9f0cbf25c 100644
--- a/clippy_utils/src/ty/type_certainty/mod.rs
+++ b/clippy_utils/src/ty/type_certainty/mod.rs
@@ -11,14 +11,14 @@
 //! As a heuristic, `expr_type_is_certain` may produce false negatives, but a false positive should
 //! be considered a bug.
 
-use crate::def_path_res;
+use crate::{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};
 use rustc_hir::{self as hir, AmbigArg, Expr, ExprKind, GenericArgs, HirId, Node, PathSegment, QPath, TyKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, AdtDef, GenericArgKind, Ty};
-use rustc_span::{Span, Symbol};
+use rustc_span::Span;
 
 mod certainty;
 use certainty::{Certainty, Meet, join, meet};
@@ -194,7 +194,7 @@ fn path_segment_certainty(
     path_segment: &PathSegment<'_>,
     resolves_to_type: bool,
 ) -> Certainty {
-    let certainty = match update_res(cx, parent_certainty, path_segment).unwrap_or(path_segment.res) {
+    let certainty = match update_res(cx, parent_certainty, path_segment, resolves_to_type).unwrap_or(path_segment.res) {
         // A definition's type is certain if it refers to something without generics (e.g., a crate or module, or
         // an unparameterized type), or the generics are instantiated with arguments that are certain.
         //
@@ -267,17 +267,24 @@ fn path_segment_certainty(
 
 /// For at least some `QPath::TypeRelative`, the path segment's `res` can be `Res::Err`.
 /// `update_res` tries to fix the resolution when `parent_certainty` is `Certain(Some(..))`.
-fn update_res(cx: &LateContext<'_>, parent_certainty: Certainty, path_segment: &PathSegment<'_>) -> Option<Res> {
+fn update_res(
+    cx: &LateContext<'_>,
+    parent_certainty: Certainty,
+    path_segment: &PathSegment<'_>,
+    resolves_to_type: bool,
+) -> Option<Res> {
     if path_segment.res == Res::Err
         && let Some(def_id) = parent_certainty.to_def_id()
     {
         let mut def_path = cx.get_def_path(def_id);
         def_path.push(path_segment.ident.name);
-        let reses = def_path_res(cx.tcx, &def_path.iter().map(Symbol::as_str).collect::<Vec<_>>());
-        if let [res] = reses.as_slice() { Some(*res) } else { None }
-    } else {
-        None
+        let ns = if resolves_to_type { PathNS::Type } else { PathNS::Value };
+        if let &[id] = lookup_path(cx.tcx, ns, &def_path).as_slice() {
+            return Some(Res::Def(cx.tcx.def_kind(id), id));
+        }
     }
+
+    None
 }
 
 #[allow(clippy::cast_possible_truncation)]
diff --git a/tests/ui-internal/auxiliary/paths.rs b/tests/ui-internal/auxiliary/paths.rs
deleted file mode 100644
index f730f564a09..00000000000
--- a/tests/ui-internal/auxiliary/paths.rs
+++ /dev/null
@@ -1,4 +0,0 @@
-#![allow(clippy::unnecessary_def_path)]
-
-pub static OPTION: [&str; 3] = ["core", "option", "Option"];
-pub const RESULT: &[&str] = &["core", "result", "Result"];
diff --git a/tests/ui-internal/invalid_paths.rs b/tests/ui-internal/invalid_paths.rs
deleted file mode 100644
index 7317abc2185..00000000000
--- a/tests/ui-internal/invalid_paths.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-#![deny(clippy::invalid_paths)]
-#![allow(clippy::missing_clippy_version_attribute, clippy::unnecessary_def_path)]
-
-mod paths {
-    // Good path
-    pub const ANY_TRAIT: [&str; 3] = ["std", "any", "Any"];
-
-    // Path to method on inherent impl of a primitive type
-    pub const F32_EPSILON: [&str; 4] = ["core", "f32", "<impl f32>", "EPSILON"];
-
-    // Path to method on inherent impl
-    pub const ARC_PTR_EQ: [&str; 4] = ["alloc", "sync", "Arc", "ptr_eq"];
-
-    // Path with empty segment
-    pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
-    //~^ invalid_paths
-
-    // Path with bad crate
-    pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
-    //~^ invalid_paths
-
-    // Path with bad module
-    pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
-    //~^ invalid_paths
-
-    // Path to method on an enum inherent impl
-    pub const OPTION_IS_SOME: [&str; 4] = ["core", "option", "Option", "is_some"];
-}
-
-fn main() {}
diff --git a/tests/ui-internal/invalid_paths.stderr b/tests/ui-internal/invalid_paths.stderr
deleted file mode 100644
index 7b7b25ce8d8..00000000000
--- a/tests/ui-internal/invalid_paths.stderr
+++ /dev/null
@@ -1,26 +0,0 @@
-error: invalid path
-  --> tests/ui-internal/invalid_paths.rs:15:5
-   |
-LL |     pub const TRANSMUTE: [&str; 4] = ["core", "intrinsics", "", "transmute"];
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-note: the lint level is defined here
-  --> tests/ui-internal/invalid_paths.rs:1:9
-   |
-LL | #![deny(clippy::invalid_paths)]
-   |         ^^^^^^^^^^^^^^^^^^^^^
-
-error: invalid path
-  --> tests/ui-internal/invalid_paths.rs:19:5
-   |
-LL |     pub const BAD_CRATE_PATH: [&str; 2] = ["bad", "path"];
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: invalid path
-  --> tests/ui-internal/invalid_paths.rs:23:5
-   |
-LL |     pub const BAD_MOD_PATH: [&str; 2] = ["std", "xxx"];
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 3 previous errors
-
diff --git a/tests/ui-internal/unnecessary_def_path.fixed b/tests/ui-internal/unnecessary_def_path.fixed
deleted file mode 100644
index 89902ebe4e5..00000000000
--- a/tests/ui-internal/unnecessary_def_path.fixed
+++ /dev/null
@@ -1,77 +0,0 @@
-//@aux-build:paths.rs
-#![deny(clippy::unnecessary_def_path)]
-#![feature(rustc_private)]
-#![allow(clippy::unnecessary_map_or)]
-
-extern crate clippy_utils;
-extern crate paths;
-extern crate rustc_hir;
-extern crate rustc_lint;
-extern crate rustc_middle;
-extern crate rustc_span;
-
-#[allow(unused)]
-use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type};
-#[allow(unused)]
-use clippy_utils::{
-    is_enum_variant_ctor, is_expr_path_def_path, is_path_diagnostic_item, is_res_lang_ctor, is_trait_method,
-    match_def_path, match_trait_method, path_res,
-};
-
-#[allow(unused)]
-use rustc_hir::LangItem;
-#[allow(unused)]
-use rustc_span::sym;
-
-use rustc_hir::Expr;
-use rustc_hir::def_id::DefId;
-use rustc_lint::LateContext;
-use rustc_middle::ty::Ty;
-
-#[allow(unused, clippy::unnecessary_def_path)]
-static OPTION: [&str; 3] = ["core", "option", "Option"];
-#[allow(unused, clippy::unnecessary_def_path)]
-const RESULT: &[&str] = &["core", "result", "Result"];
-
-fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) {
-    let _ = is_type_diagnostic_item(cx, ty, sym::Option);
-    //~^ unnecessary_def_path
-    let _ = is_type_diagnostic_item(cx, ty, sym::Result);
-    //~^ unnecessary_def_path
-    let _ = is_type_diagnostic_item(cx, ty, sym::Result);
-    //~^ unnecessary_def_path
-
-    #[allow(unused, clippy::unnecessary_def_path)]
-    let rc_path = &["alloc", "rc", "Rc"];
-    let _ = is_type_diagnostic_item(cx, ty, sym::Rc);
-    //~^ unnecessary_def_path
-
-    let _ = is_type_diagnostic_item(cx, ty, sym::Option);
-    //~^ unnecessary_def_path
-    let _ = is_type_diagnostic_item(cx, ty, sym::Result);
-    //~^ unnecessary_def_path
-
-    let _ = is_type_lang_item(cx, ty, LangItem::OwnedBox);
-    //~^ unnecessary_def_path
-    let _ = is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit);
-    //~^ unnecessary_def_path
-
-    let _ = cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did);
-    //~^ unnecessary_def_path
-    let _ = cx.tcx.is_diagnostic_item(sym::Option, did);
-    //~^ unnecessary_def_path
-    let _ = cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did);
-    //~^ unnecessary_def_path
-
-    let _ = is_trait_method(cx, expr, sym::AsRef);
-    //~^ unnecessary_def_path
-
-    let _ = is_path_diagnostic_item(cx, expr, sym::Option);
-    //~^ unnecessary_def_path
-    let _ = path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id));
-    //~^ unnecessary_def_path
-    let _ = is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome);
-    //~^ unnecessary_def_path
-}
-
-fn main() {}
diff --git a/tests/ui-internal/unnecessary_def_path.rs b/tests/ui-internal/unnecessary_def_path.rs
index cfca15267c1..b17c60d0342 100644
--- a/tests/ui-internal/unnecessary_def_path.rs
+++ b/tests/ui-internal/unnecessary_def_path.rs
@@ -1,77 +1,20 @@
-//@aux-build:paths.rs
-#![deny(clippy::unnecessary_def_path)]
 #![feature(rustc_private)]
-#![allow(clippy::unnecessary_map_or)]
 
-extern crate clippy_utils;
-extern crate paths;
-extern crate rustc_hir;
-extern crate rustc_lint;
-extern crate rustc_middle;
-extern crate rustc_span;
+use clippy_utils::paths::PathLookup;
+use clippy_utils::{PathNS, macro_path, sym, type_path, value_path};
 
-#[allow(unused)]
-use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item, match_type};
-#[allow(unused)]
-use clippy_utils::{
-    is_enum_variant_ctor, is_expr_path_def_path, is_path_diagnostic_item, is_res_lang_ctor, is_trait_method,
-    match_def_path, match_trait_method, path_res,
-};
+static OPTION: PathLookup = type_path!(core::option::Option);
+//~^ unnecessary_def_path
+static SOME: PathLookup = type_path!(core::option::Option::Some);
+//~^ unnecessary_def_path
 
-#[allow(unused)]
-use rustc_hir::LangItem;
-#[allow(unused)]
-use rustc_span::sym;
+static RESULT: PathLookup = type_path!(core::result::Result);
+//~^ unnecessary_def_path
+static RESULT_VIA_STD: PathLookup = type_path!(std::result::Result);
+//~^ unnecessary_def_path
 
-use rustc_hir::Expr;
-use rustc_hir::def_id::DefId;
-use rustc_lint::LateContext;
-use rustc_middle::ty::Ty;
+static VEC_NEW: PathLookup = value_path!(alloc::vec::Vec::new);
+//~^ unnecessary_def_path
 
-#[allow(unused, clippy::unnecessary_def_path)]
-static OPTION: [&str; 3] = ["core", "option", "Option"];
-#[allow(unused, clippy::unnecessary_def_path)]
-const RESULT: &[&str] = &["core", "result", "Result"];
-
-fn _f<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, did: DefId, expr: &Expr<'_>) {
-    let _ = match_type(cx, ty, &OPTION);
-    //~^ unnecessary_def_path
-    let _ = match_type(cx, ty, RESULT);
-    //~^ unnecessary_def_path
-    let _ = match_type(cx, ty, &["core", "result", "Result"]);
-    //~^ unnecessary_def_path
-
-    #[allow(unused, clippy::unnecessary_def_path)]
-    let rc_path = &["alloc", "rc", "Rc"];
-    let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
-    //~^ unnecessary_def_path
-
-    let _ = match_type(cx, ty, &paths::OPTION);
-    //~^ unnecessary_def_path
-    let _ = match_type(cx, ty, paths::RESULT);
-    //~^ unnecessary_def_path
-
-    let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]);
-    //~^ unnecessary_def_path
-    let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]);
-    //~^ unnecessary_def_path
-
-    let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]);
-    //~^ unnecessary_def_path
-    let _ = match_def_path(cx, did, &["core", "option", "Option"]);
-    //~^ unnecessary_def_path
-    let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]);
-    //~^ unnecessary_def_path
-
-    let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]);
-    //~^ unnecessary_def_path
-
-    let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]);
-    //~^ unnecessary_def_path
-    let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]);
-    //~^ unnecessary_def_path
-    let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]);
-    //~^ unnecessary_def_path
-}
-
-fn main() {}
+static VEC_MACRO: PathLookup = macro_path!(std::vec);
+//~^ unnecessary_def_path
diff --git a/tests/ui-internal/unnecessary_def_path.stderr b/tests/ui-internal/unnecessary_def_path.stderr
index d7fb4ea551e..4abb1be7406 100644
--- a/tests/ui-internal/unnecessary_def_path.stderr
+++ b/tests/ui-internal/unnecessary_def_path.stderr
@@ -1,100 +1,58 @@
-error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:37:13
+error: a diagnostic name exists for this path: sym::Option
+  --> tests/ui-internal/unnecessary_def_path.rs:6:29
    |
-LL |     let _ = match_type(cx, ty, &OPTION);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)`
+LL | static OPTION: PathLookup = type_path!(core::option::Option);
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-note: the lint level is defined here
-  --> tests/ui-internal/unnecessary_def_path.rs:2:9
-   |
-LL | #![deny(clippy::unnecessary_def_path)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:39:13
-   |
-LL |     let _ = match_type(cx, ty, RESULT);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
+   = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead
+   = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils
+   = note: `-D clippy::unnecessary-def-path` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::unnecessary_def_path)]`
 
-error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:41:13
+error: a language item exists for this path: LangItem::OptionSome
+  --> tests/ui-internal/unnecessary_def_path.rs:8:27
    |
-LL |     let _ = match_type(cx, ty, &["core", "result", "Result"]);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
-
-error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:46:13
+LL | static SOME: PathLookup = type_path!(core::option::Option::Some);
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-LL |     let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Rc)`
+   = help: remove the `PathLookup` and use utilities such as `cx.tcx.lang_items` instead
+   = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=lang&filter-crate=clippy_utils
 
-error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:49:13
+error: a diagnostic name exists for this path: sym::Result
+  --> tests/ui-internal/unnecessary_def_path.rs:11:29
    |
-LL |     let _ = match_type(cx, ty, &paths::OPTION);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)`
-
-error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:51:13
+LL | static RESULT: PathLookup = type_path!(core::result::Result);
+   |                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-LL |     let _ = match_type(cx, ty, paths::RESULT);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
+   = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead
+   = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils
 
-error: use of a def path to a `LangItem`
-  --> tests/ui-internal/unnecessary_def_path.rs:54:13
+error: a diagnostic name exists for this path: sym::Result
+  --> tests/ui-internal/unnecessary_def_path.rs:13:37
    |
-LL |     let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_lang_item(cx, ty, LangItem::OwnedBox)`
-
-error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:56:13
+LL | static RESULT_VIA_STD: PathLookup = type_path!(std::result::Result);
+   |                                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-LL |     let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit)`
+   = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead
+   = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils
 
-error: use of a def path to a `LangItem`
-  --> tests/ui-internal/unnecessary_def_path.rs:59:13
+error: a diagnostic name exists for this path: sym::vec_new
+  --> tests/ui-internal/unnecessary_def_path.rs:16:30
    |
-LL |     let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did)`
-
-error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:61:13
+LL | static VEC_NEW: PathLookup = value_path!(alloc::vec::Vec::new);
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-LL |     let _ = match_def_path(cx, did, &["core", "option", "Option"]);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.is_diagnostic_item(sym::Option, did)`
+   = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead
+   = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils
 
-error: use of a def path to a `LangItem`
-  --> tests/ui-internal/unnecessary_def_path.rs:63:13
-   |
-LL |     let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did)`
+error: a diagnostic name exists for this path: sym::vec_macro
+  --> tests/ui-internal/unnecessary_def_path.rs:19:32
    |
-   = help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead
-
-error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:66:13
-   |
-LL |     let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_trait_method(cx, expr, sym::AsRef)`
-
-error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:69:13
-   |
-LL |     let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_path_diagnostic_item(cx, expr, sym::Option)`
-
-error: use of a def path to a `LangItem`
-  --> tests/ui-internal/unnecessary_def_path.rs:71:13
-   |
-LL |     let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id))`
-
-error: use of a def path to a `LangItem`
-  --> tests/ui-internal/unnecessary_def_path.rs:73:13
+LL | static VEC_MACRO: PathLookup = macro_path!(std::vec);
+   |                                ^^^^^^^^^^^^^^^^^^^^^
    |
-LL |     let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome)`
+   = help: remove the `PathLookup` and use utilities such as `cx.tcx.is_diagnostic_item` instead
+   = help: see also https://doc.rust-lang.org/nightly/nightly-rustc/?search=diag&filter-crate=clippy_utils
 
-error: aborting due to 15 previous errors
+error: aborting due to 6 previous errors
 
diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs b/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs
deleted file mode 100644
index bd7a55114ac..00000000000
--- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.rs
+++ /dev/null
@@ -1,19 +0,0 @@
-#![feature(rustc_private)]
-#![allow(unused)]
-#![deny(clippy::unnecessary_def_path)]
-
-extern crate rustc_hir;
-
-use rustc_hir::LangItem;
-
-fn main() {
-    const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"];
-    //~^ unnecessary_def_path
-    const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"];
-    //~^ unnecessary_def_path
-    const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
-    //~^ unnecessary_def_path
-
-    // Don't lint, not a diagnostic or language item
-    const OPS_MOD: [&str; 2] = ["core", "ops"];
-}
diff --git a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr b/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
deleted file mode 100644
index c49abc516f5..00000000000
--- a/tests/ui-internal/unnecessary_def_path_hardcoded_path.stderr
+++ /dev/null
@@ -1,31 +0,0 @@
-error: hardcoded path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:10:36
-   |
-LL |     const DEREF_TRAIT: [&str; 4] = ["core", "ops", "deref", "Deref"];
-   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: convert all references to use `sym::Deref`
-note: the lint level is defined here
-  --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:3:9
-   |
-LL | #![deny(clippy::unnecessary_def_path)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: hardcoded path to a language item
-  --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:12:40
-   |
-LL |     const DEREF_MUT_TRAIT: [&str; 4] = ["core", "ops", "deref", "DerefMut"];
-   |                                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: convert all references to use `LangItem::DerefMut`
-
-error: hardcoded path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path_hardcoded_path.rs:14:43
-   |
-LL |     const DEREF_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "Deref", "deref"];
-   |                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: convert all references to use `sym::deref_method`
-
-error: aborting due to 3 previous errors
-
diff --git a/tests/ui-toml/toml_disallowed_types/clippy.toml b/tests/ui-toml/toml_disallowed_types/clippy.toml
index 6cb9e2ef954..08e35017f78 100644
--- a/tests/ui-toml/toml_disallowed_types/clippy.toml
+++ b/tests/ui-toml/toml_disallowed_types/clippy.toml
@@ -6,7 +6,7 @@ disallowed-types = [
     "std::thread::Thread",
     "std::time::Instant",
     "std::io::Read",
-    "std::primitive::usize",
+    "usize",
     "bool",
     # can give path and reason with an inline table
     { path = "std::net::Ipv4Addr", reason = "no IPv4 allowed" },
diff --git a/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr b/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr
index 18bc36ca1e3..061cdc7649a 100644
--- a/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr
+++ b/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.stderr
@@ -37,7 +37,7 @@ error: use of a disallowed type `std::io::Read`
 LL | fn trait_obj(_: &dyn std::io::Read) {}
    |                      ^^^^^^^^^^^^^
 
-error: use of a disallowed type `std::primitive::usize`
+error: use of a disallowed type `usize`
   --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:26:33
    |
 LL | fn full_and_single_path_prim(_: usize, _: bool) {}
@@ -49,13 +49,13 @@ error: use of a disallowed type `bool`
 LL | fn full_and_single_path_prim(_: usize, _: bool) {}
    |                                           ^^^^
 
-error: use of a disallowed type `std::primitive::usize`
+error: use of a disallowed type `usize`
   --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:30:28
    |
 LL | fn const_generics<const C: usize>() {}
    |                            ^^^^^
 
-error: use of a disallowed type `std::primitive::usize`
+error: use of a disallowed type `usize`
   --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:33:24
    |
 LL | struct GenArg<const U: usize>([u8; U]);
@@ -123,7 +123,7 @@ error: use of a disallowed type `proc_macro2::Ident`
 LL |     let _ = syn::Ident::new("", todo!());
    |             ^^^^^^^^^^
 
-error: use of a disallowed type `std::primitive::usize`
+error: use of a disallowed type `usize`
   --> tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs:61:12
    |
 LL |     let _: usize = 64_usize;
diff --git a/tests/ui-toml/toml_invalid_path/clippy.toml b/tests/ui-toml/toml_invalid_path/clippy.toml
index 6d0d732a922..997ed47b71c 100644
--- a/tests/ui-toml/toml_invalid_path/clippy.toml
+++ b/tests/ui-toml/toml_invalid_path/clippy.toml
@@ -1,12 +1,15 @@
-[[disallowed-types]]
-path = "std::result::Result::Err"
-
 [[disallowed-macros]]
 path = "bool"
 
 [[disallowed-methods]]
 path = "std::process::current_exe"
 
+[[disallowed-methods]]
+path = ""
+
+[[disallowed-types]]
+path = "std::result::Result::Err"
+
 # negative test
 
 [[disallowed-methods]]
diff --git a/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs b/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs
index c1520382703..ff4eada3900 100644
--- a/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs
+++ b/tests/ui-toml/toml_invalid_path/conf_invalid_path.rs
@@ -1,5 +1,6 @@
 //@error-in-other-file: expected a macro, found a primitive type
-//@error-in-other-file: `std::process::current_exe` does not refer to an existing function
-//@error-in-other-file: expected a type, found a tuple variant
+//@error-in-other-file: `std::process::current_exe` does not refer to a reachable function
+//@error-in-other-file: `` does not refer to a reachable function
+//@error-in-other-file: expected a type, found a variant
 
 fn main() {}
diff --git a/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr b/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr
index 82550108eba..59a427dc99c 100644
--- a/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr
+++ b/tests/ui-toml/toml_invalid_path/conf_invalid_path.stderr
@@ -1,23 +1,38 @@
 warning: expected a macro, found a primitive type
-  --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:4:1
+  --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:1:1
    |
 LL | / [[disallowed-macros]]
 LL | | path = "bool"
    | |_____________^
+   |
+   = help: add `allow-invalid = true` to the entry to suppress this warning
 
-warning: `std::process::current_exe` does not refer to an existing function
-  --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:7:1
+warning: `std::process::current_exe` does not refer to a reachable function
+  --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:4:1
    |
 LL | / [[disallowed-methods]]
 LL | | path = "std::process::current_exe"
    | |__________________________________^
+   |
+   = help: add `allow-invalid = true` to the entry to suppress this warning
 
-warning: expected a type, found a tuple variant
-  --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:1:1
+warning: `` does not refer to a reachable function
+  --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:7:1
+   |
+LL | / [[disallowed-methods]]
+LL | | path = ""
+   | |_________^
+   |
+   = help: add `allow-invalid = true` to the entry to suppress this warning
+
+warning: expected a type, found a variant
+  --> $DIR/tests/ui-toml/toml_invalid_path/clippy.toml:10:1
    |
 LL | / [[disallowed-types]]
 LL | | path = "std::result::Result::Err"
    | |_________________________________^
+   |
+   = help: add `allow-invalid = true` to the entry to suppress this warning
 
-warning: 3 warnings emitted
+warning: 4 warnings emitted
 
diff --git a/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs b/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs
index 2dbc5eca32e..14f15e73311 100644
--- a/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs
+++ b/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.rs
@@ -1,5 +1,5 @@
-//@error-in-other-file: `regex::Regex::new_` does not refer to an existing function
-//@error-in-other-file: `regex::Regex_::new` does not refer to an existing function
+//@error-in-other-file: `regex::Regex::new_` does not refer to a reachable function
+//@error-in-other-file: `regex::Regex_::new` does not refer to a reachable function
 
 extern crate regex;
 
diff --git a/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr b/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr
index 5d28e5fa970..e5fd548b26d 100644
--- a/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr
+++ b/tests/ui-toml/toml_unloaded_crate/conf_unloaded_crate.stderr
@@ -1,16 +1,20 @@
-warning: `regex::Regex::new_` does not refer to an existing function
+warning: `regex::Regex::new_` does not refer to a reachable function
   --> $DIR/tests/ui-toml/toml_unloaded_crate/clippy.toml:3:1
    |
 LL | / [[disallowed-methods]]
 LL | | path = "regex::Regex::new_"
    | |___________________________^
+   |
+   = help: add `allow-invalid = true` to the entry to suppress this warning
 
-warning: `regex::Regex_::new` does not refer to an existing function
+warning: `regex::Regex_::new` does not refer to a reachable function
   --> $DIR/tests/ui-toml/toml_unloaded_crate/clippy.toml:6:1
    |
 LL | / [[disallowed-methods]]
 LL | | path = "regex::Regex_::new"
    | |___________________________^
+   |
+   = help: add `allow-invalid = true` to the entry to suppress this warning
 
 warning: 2 warnings emitted
 
diff --git a/tests/ui/author.stdout b/tests/ui/author.stdout
index eed704e82fe..88a27530238 100644
--- a/tests/ui/author.stdout
+++ b/tests/ui/author.stdout
@@ -1,8 +1,6 @@
 if let StmtKind::Let(local) = stmt.kind
     && let Some(init) = local.init
     && let ExprKind::Cast(expr, cast_ty) = init.kind
-    && let TyKind::Path(ref qpath) = cast_ty.kind
-    && match_qpath(qpath, &["char"])
     && let ExprKind::Lit(ref lit) = expr.kind
     && let LitKind::Int(69, LitIntType::Unsuffixed) = lit.node
     && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind
diff --git a/tests/ui/author/blocks.stdout b/tests/ui/author/blocks.stdout
index 54325f9776c..e453299edbc 100644
--- a/tests/ui/author/blocks.stdout
+++ b/tests/ui/author/blocks.stdout
@@ -14,8 +14,6 @@ if let ExprKind::Block(block, None) = expr.kind
     && name1.as_str() == "_t"
     && let StmtKind::Semi(e) = block.stmts[2].kind
     && let ExprKind::Unary(UnOp::Neg, inner) = e.kind
-    && let ExprKind::Path(ref qpath) = inner.kind
-    && match_qpath(qpath, &["x"])
     && block.expr.is_none()
 {
     // report your lint here
@@ -25,18 +23,14 @@ if let ExprKind::Block(block, None) = expr.kind
     && let StmtKind::Let(local) = block.stmts[0].kind
     && let Some(init) = local.init
     && let ExprKind::Call(func, args) = init.kind
-    && let ExprKind::Path(ref qpath) = func.kind
-    && match_qpath(qpath, &["String", "new"])
+    && is_path_diagnostic_item(cx, func, sym::string_new)
     && args.is_empty()
     && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind
     && name.as_str() == "expr"
     && let Some(trailing_expr) = block.expr
     && let ExprKind::Call(func1, args1) = trailing_expr.kind
-    && let ExprKind::Path(ref qpath1) = func1.kind
-    && match_qpath(qpath1, &["drop"])
+    && is_path_diagnostic_item(cx, func1, sym::mem_drop)
     && args1.len() == 1
-    && let ExprKind::Path(ref qpath2) = args1[0].kind
-    && match_qpath(qpath2, &["expr"])
 {
     // report your lint here
 }
diff --git a/tests/ui/author/call.stdout b/tests/ui/author/call.stdout
index 59d4da490fe..2b179d45112 100644
--- a/tests/ui/author/call.stdout
+++ b/tests/ui/author/call.stdout
@@ -1,8 +1,7 @@
 if let StmtKind::Let(local) = stmt.kind
     && let Some(init) = local.init
     && let ExprKind::Call(func, args) = init.kind
-    && let ExprKind::Path(ref qpath) = func.kind
-    && match_qpath(qpath, &["{{root}}", "std", "cmp", "min"])
+    && is_path_diagnostic_item(cx, func, sym::cmp_min)
     && args.len() == 2
     && let ExprKind::Lit(ref lit) = args[0].kind
     && let LitKind::Int(3, LitIntType::Unsuffixed) = lit.node
diff --git a/tests/ui/author/if.stdout b/tests/ui/author/if.stdout
index 8ffdf886202..da359866bff 100644
--- a/tests/ui/author/if.stdout
+++ b/tests/ui/author/if.stdout
@@ -31,10 +31,8 @@ if let StmtKind::Let(local) = stmt.kind
 if let ExprKind::If(cond, then, Some(else_expr)) = expr.kind
     && let ExprKind::Let(let_expr) = cond.kind
     && let PatKind::Expr(lit_expr) = let_expr.pat.kind
-    && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind
+    && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind
     && let LitKind::Bool(true) = lit.node
-    && let ExprKind::Path(ref qpath) = let_expr.init.kind
-    && match_qpath(qpath, &["a"])
     && let ExprKind::Block(block, None) = then.kind
     && block.stmts.is_empty()
     && block.expr.is_none()
diff --git a/tests/ui/author/issue_3849.stdout b/tests/ui/author/issue_3849.stdout
index a5a8c0304ee..f02ea5bf075 100644
--- a/tests/ui/author/issue_3849.stdout
+++ b/tests/ui/author/issue_3849.stdout
@@ -1,11 +1,8 @@
 if let StmtKind::Let(local) = stmt.kind
     && let Some(init) = local.init
     && let ExprKind::Call(func, args) = init.kind
-    && let ExprKind::Path(ref qpath) = func.kind
-    && match_qpath(qpath, &["std", "mem", "transmute"])
+    && is_path_diagnostic_item(cx, func, sym::transmute)
     && args.len() == 1
-    && let ExprKind::Path(ref qpath1) = args[0].kind
-    && match_qpath(qpath1, &["ZPTR"])
     && let PatKind::Wild = local.pat.kind
 {
     // report your lint here
diff --git a/tests/ui/author/loop.stdout b/tests/ui/author/loop.stdout
index c94eb171f52..79794cec926 100644
--- a/tests/ui/author/loop.stdout
+++ b/tests/ui/author/loop.stdout
@@ -14,8 +14,6 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo
     && block.stmts.len() == 1
     && let StmtKind::Let(local) = block.stmts[0].kind
     && let Some(init) = local.init
-    && let ExprKind::Path(ref qpath1) = init.kind
-    && match_qpath(qpath1, &["y"])
     && let PatKind::Binding(BindingMode::NONE, _, name1, None) = local.pat.kind
     && name1.as_str() == "z"
     && block.expr.is_none()
@@ -64,8 +62,6 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo
     // report your lint here
 }
 if let Some(higher::While { condition: condition, body: body }) = higher::While::hir(expr)
-    && let ExprKind::Path(ref qpath) = condition.kind
-    && match_qpath(qpath, &["a"])
     && let ExprKind::Block(block, None) = body.kind
     && block.stmts.len() == 1
     && let StmtKind::Semi(e) = block.stmts[0].kind
@@ -77,10 +73,8 @@ if let Some(higher::While { condition: condition, body: body }) = higher::While:
 }
 if let Some(higher::WhileLet { let_pat: let_pat, let_expr: let_expr, if_then: if_then }) = higher::WhileLet::hir(expr)
     && let PatKind::Expr(lit_expr) = let_pat.kind
-    && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind
+    && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind
     && let LitKind::Bool(true) = lit.node
-    && let ExprKind::Path(ref qpath) = let_expr.kind
-    && match_qpath(qpath, &["a"])
     && let ExprKind::Block(block, None) = if_then.kind
     && block.stmts.len() == 1
     && let StmtKind::Semi(e) = block.stmts[0].kind
diff --git a/tests/ui/author/macro_in_closure.stdout b/tests/ui/author/macro_in_closure.stdout
index 3186d0cbc27..5b347aef14f 100644
--- a/tests/ui/author/macro_in_closure.stdout
+++ b/tests/ui/author/macro_in_closure.stdout
@@ -7,12 +7,10 @@ if let StmtKind::Let(local) = stmt.kind
     && block.stmts.len() == 1
     && let StmtKind::Semi(e) = block.stmts[0].kind
     && let ExprKind::Call(func, args) = e.kind
-    && let ExprKind::Path(ref qpath) = func.kind
-    && match_qpath(qpath, &["$crate", "io", "_print"])
+    && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed
     && args.len() == 1
     && let ExprKind::Call(func1, args1) = args[0].kind
-    && let ExprKind::Path(ref qpath1) = func1.kind
-    && match_qpath(qpath1, &["format_arguments", "new_v1"])
+    && paths::CORE_FMT_ARGUMENTS_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
     && args1.len() == 2
     && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind
     && let ExprKind::Array(elements) = inner.kind
@@ -27,12 +25,9 @@ if let StmtKind::Let(local) = stmt.kind
     && let ExprKind::Array(elements1) = inner1.kind
     && elements1.len() == 1
     && let ExprKind::Call(func2, args2) = elements1[0].kind
-    && let ExprKind::Path(ref qpath2) = func2.kind
-    && match_qpath(qpath2, &["format_argument", "new_display"])
+    && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
     && args2.len() == 1
     && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind
-    && let ExprKind::Path(ref qpath3) = inner2.kind
-    && match_qpath(qpath3, &["x"])
     && block.expr.is_none()
     && let PatKind::Binding(BindingMode::NONE, _, name, None) = local.pat.kind
     && name.as_str() == "print_text"
diff --git a/tests/ui/author/macro_in_loop.stdout b/tests/ui/author/macro_in_loop.stdout
index 3f9be297c33..75dabd57bfe 100644
--- a/tests/ui/author/macro_in_loop.stdout
+++ b/tests/ui/author/macro_in_loop.stdout
@@ -17,12 +17,10 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo
     && block1.stmts.len() == 1
     && let StmtKind::Semi(e1) = block1.stmts[0].kind
     && let ExprKind::Call(func, args) = e1.kind
-    && let ExprKind::Path(ref qpath1) = func.kind
-    && match_qpath(qpath1, &["$crate", "io", "_print"])
+    && paths::STD_IO_STDIO__PRINT.matches_path(cx, func) // Add the path to `clippy_utils::paths` if needed
     && args.len() == 1
     && let ExprKind::Call(func1, args1) = args[0].kind
-    && let ExprKind::Path(ref qpath2) = func1.kind
-    && match_qpath(qpath2, &["format_arguments", "new_v1"])
+    && paths::CORE_FMT_ARGUMENTS_NEW_V1.matches_path(cx, func1) // Add the path to `clippy_utils::paths` if needed
     && args1.len() == 2
     && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) = args1[0].kind
     && let ExprKind::Array(elements) = inner.kind
@@ -37,12 +35,9 @@ if let Some(higher::ForLoop { pat: pat, arg: arg, body: body, .. }) = higher::Fo
     && let ExprKind::Array(elements1) = inner1.kind
     && elements1.len() == 1
     && let ExprKind::Call(func2, args2) = elements1[0].kind
-    && let ExprKind::Path(ref qpath3) = func2.kind
-    && match_qpath(qpath3, &["format_argument", "new_display"])
+    && paths::CORE_FMT_RT_ARGUMENT_NEW_DISPLAY.matches_path(cx, func2) // Add the path to `clippy_utils::paths` if needed
     && args2.len() == 1
     && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner2) = args2[0].kind
-    && let ExprKind::Path(ref qpath4) = inner2.kind
-    && match_qpath(qpath4, &["i"])
     && block1.expr.is_none()
     && block.expr.is_none()
 {
diff --git a/tests/ui/author/matches.stdout b/tests/ui/author/matches.stdout
index acb3b140dfa..9752d7a9f99 100644
--- a/tests/ui/author/matches.stdout
+++ b/tests/ui/author/matches.stdout
@@ -5,13 +5,13 @@ if let StmtKind::Let(local) = stmt.kind
     && let LitKind::Int(42, LitIntType::Unsuffixed) = lit.node
     && arms.len() == 3
     && let PatKind::Expr(lit_expr) = arms[0].pat.kind
-    && let PatExprKind::Lit{ref lit1, negated } = lit_expr.kind
+    && let PatExprKind::Lit { ref lit1, negated } = lit_expr.kind
     && let LitKind::Int(16, LitIntType::Unsuffixed) = lit1.node
     && arms[0].guard.is_none()
     && let ExprKind::Lit(ref lit2) = arms[0].body.kind
     && let LitKind::Int(5, LitIntType::Unsuffixed) = lit2.node
     && let PatKind::Expr(lit_expr1) = arms[1].pat.kind
-    && let PatExprKind::Lit{ref lit3, negated1 } = lit_expr1.kind
+    && let PatExprKind::Lit { ref lit3, negated1 } = lit_expr1.kind
     && let LitKind::Int(17, LitIntType::Unsuffixed) = lit3.node
     && arms[1].guard.is_none()
     && let ExprKind::Block(block, None) = arms[1].body.kind
@@ -23,8 +23,6 @@ if let StmtKind::Let(local) = stmt.kind
     && let PatKind::Binding(BindingMode::NONE, _, name, None) = local1.pat.kind
     && name.as_str() == "x"
     && let Some(trailing_expr) = block.expr
-    && let ExprKind::Path(ref qpath) = trailing_expr.kind
-    && match_qpath(qpath, &["x"])
     && let PatKind::Wild = arms[2].pat.kind
     && arms[2].guard.is_none()
     && let ExprKind::Lit(ref lit5) = arms[2].body.kind
diff --git a/tests/ui/author/struct.stdout b/tests/ui/author/struct.stdout
index b66bbccb3cf..1e8fbafd30c 100644
--- a/tests/ui/author/struct.stdout
+++ b/tests/ui/author/struct.stdout
@@ -1,5 +1,4 @@
 if let ExprKind::Struct(qpath, fields, None) = expr.kind
-    && match_qpath(qpath, &["Test"])
     && fields.len() == 1
     && fields[0].ident.as_str() == "field"
     && let ExprKind::If(cond, then, Some(else_expr)) = fields[0].expr.kind
@@ -20,11 +19,10 @@ if let ExprKind::Struct(qpath, fields, None) = expr.kind
     // report your lint here
 }
 if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind
-    && match_qpath(qpath, &["Test"])
     && fields.len() == 1
     && fields[0].ident.as_str() == "field"
     && let PatKind::Expr(lit_expr) = fields[0].pat.kind
-    && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind
+    && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind
     && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node
     && arm.guard.is_none()
     && let ExprKind::Block(block, None) = arm.body.kind
@@ -34,10 +32,9 @@ if let PatKind::Struct(ref qpath, fields, false) = arm.pat.kind
     // report your lint here
 }
 if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind
-    && match_qpath(qpath, &["TestTuple"])
     && fields.len() == 1
     && let PatKind::Expr(lit_expr) = fields[0].kind
-    && let PatExprKind::Lit{ref lit, negated } = lit_expr.kind
+    && let PatExprKind::Lit { ref lit, negated } = lit_expr.kind
     && let LitKind::Int(1, LitIntType::Unsuffixed) = lit.node
     && arm.guard.is_none()
     && let ExprKind::Block(block, None) = arm.body.kind
@@ -48,8 +45,6 @@ if let PatKind::TupleStruct(ref qpath, fields, None) = arm.pat.kind
 }
 if let ExprKind::MethodCall(method_name, receiver, args, _) = expr.kind
     && method_name.ident.as_str() == "test"
-    && let ExprKind::Path(ref qpath) = receiver.kind
-    && match_qpath(qpath, &["test_method_call"])
     && args.is_empty()
 {
     // report your lint here
diff --git a/tests/ui/manual_saturating_arithmetic.fixed b/tests/ui/manual_saturating_arithmetic.fixed
index 3f73d6e5a1a..304be05f6c4 100644
--- a/tests/ui/manual_saturating_arithmetic.fixed
+++ b/tests/ui/manual_saturating_arithmetic.fixed
@@ -1,7 +1,5 @@
 #![allow(clippy::legacy_numeric_constants, unused_imports)]
 
-use std::{i32, i128, u32, u128};
-
 fn main() {
     let _ = 1u32.saturating_add(1);
     //~^ manual_saturating_arithmetic
diff --git a/tests/ui/manual_saturating_arithmetic.rs b/tests/ui/manual_saturating_arithmetic.rs
index 98246a5cd96..c2b570e974a 100644
--- a/tests/ui/manual_saturating_arithmetic.rs
+++ b/tests/ui/manual_saturating_arithmetic.rs
@@ -1,7 +1,5 @@
 #![allow(clippy::legacy_numeric_constants, unused_imports)]
 
-use std::{i32, i128, u32, u128};
-
 fn main() {
     let _ = 1u32.checked_add(1).unwrap_or(u32::max_value());
     //~^ manual_saturating_arithmetic
diff --git a/tests/ui/manual_saturating_arithmetic.stderr b/tests/ui/manual_saturating_arithmetic.stderr
index 9d133d8a073..2f006a3ae17 100644
--- a/tests/ui/manual_saturating_arithmetic.stderr
+++ b/tests/ui/manual_saturating_arithmetic.stderr
@@ -1,5 +1,5 @@
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:6:13
+  --> tests/ui/manual_saturating_arithmetic.rs:4:13
    |
 LL |     let _ = 1u32.checked_add(1).unwrap_or(u32::max_value());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)`
@@ -8,19 +8,19 @@ LL |     let _ = 1u32.checked_add(1).unwrap_or(u32::max_value());
    = help: to override `-D warnings` add `#[allow(clippy::manual_saturating_arithmetic)]`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:8:13
+  --> tests/ui/manual_saturating_arithmetic.rs:6:13
    |
 LL |     let _ = 1u32.checked_add(1).unwrap_or(u32::MAX);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u32.saturating_add(1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:10:13
+  --> tests/ui/manual_saturating_arithmetic.rs:8:13
    |
 LL |     let _ = 1u8.checked_add(1).unwrap_or(255);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1u8.saturating_add(1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:12:13
+  --> tests/ui/manual_saturating_arithmetic.rs:10:13
    |
 LL |       let _ = 1u128
    |  _____________^
@@ -30,49 +30,49 @@ LL | |         .unwrap_or(340_282_366_920_938_463_463_374_607_431_768_211_455);
    | |_______________________________________________________________________^ help: consider using `saturating_add`: `1u128.saturating_add(1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:18:13
+  --> tests/ui/manual_saturating_arithmetic.rs:16:13
    |
 LL |     let _ = 1u32.checked_mul(1).unwrap_or(u32::MAX);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_mul`: `1u32.saturating_mul(1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:21:13
+  --> tests/ui/manual_saturating_arithmetic.rs:19:13
    |
 LL |     let _ = 1u32.checked_sub(1).unwrap_or(u32::min_value());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:23:13
+  --> tests/ui/manual_saturating_arithmetic.rs:21:13
    |
 LL |     let _ = 1u32.checked_sub(1).unwrap_or(u32::MIN);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u32.saturating_sub(1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:25:13
+  --> tests/ui/manual_saturating_arithmetic.rs:23:13
    |
 LL |     let _ = 1u8.checked_sub(1).unwrap_or(0);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1u8.saturating_sub(1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:30:13
+  --> tests/ui/manual_saturating_arithmetic.rs:28:13
    |
 LL |     let _ = 1i32.checked_add(1).unwrap_or(i32::max_value());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:32:13
+  --> tests/ui/manual_saturating_arithmetic.rs:30:13
    |
 LL |     let _ = 1i32.checked_add(1).unwrap_or(i32::MAX);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:34:13
+  --> tests/ui/manual_saturating_arithmetic.rs:32:13
    |
 LL |     let _ = 1i8.checked_add(1).unwrap_or(127);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:36:13
+  --> tests/ui/manual_saturating_arithmetic.rs:34:13
    |
 LL |       let _ = 1i128
    |  _____________^
@@ -82,25 +82,25 @@ LL | |         .unwrap_or(170_141_183_460_469_231_731_687_303_715_884_105_727);
    | |_______________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:40:13
+  --> tests/ui/manual_saturating_arithmetic.rs:38:13
    |
 LL |     let _ = 1i32.checked_add(-1).unwrap_or(i32::min_value());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:42:13
+  --> tests/ui/manual_saturating_arithmetic.rs:40:13
    |
 LL |     let _ = 1i32.checked_add(-1).unwrap_or(i32::MIN);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i32.saturating_add(-1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:44:13
+  --> tests/ui/manual_saturating_arithmetic.rs:42:13
    |
 LL |     let _ = 1i8.checked_add(-1).unwrap_or(-128);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_add`: `1i8.saturating_add(-1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:46:13
+  --> tests/ui/manual_saturating_arithmetic.rs:44:13
    |
 LL |       let _ = 1i128
    |  _____________^
@@ -110,25 +110,25 @@ LL | |         .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
    | |________________________________________________________________________^ help: consider using `saturating_add`: `1i128.saturating_add(-1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:54:13
+  --> tests/ui/manual_saturating_arithmetic.rs:52:13
    |
 LL |     let _ = 1i32.checked_sub(1).unwrap_or(i32::min_value());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:56:13
+  --> tests/ui/manual_saturating_arithmetic.rs:54:13
    |
 LL |     let _ = 1i32.checked_sub(1).unwrap_or(i32::MIN);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:58:13
+  --> tests/ui/manual_saturating_arithmetic.rs:56:13
    |
 LL |     let _ = 1i8.checked_sub(1).unwrap_or(-128);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:60:13
+  --> tests/ui/manual_saturating_arithmetic.rs:58:13
    |
 LL |       let _ = 1i128
    |  _____________^
@@ -138,25 +138,25 @@ LL | |         .unwrap_or(-170_141_183_460_469_231_731_687_303_715_884_105_728);
    | |________________________________________________________________________^ help: consider using `saturating_sub`: `1i128.saturating_sub(1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:64:13
+  --> tests/ui/manual_saturating_arithmetic.rs:62:13
    |
 LL |     let _ = 1i32.checked_sub(-1).unwrap_or(i32::max_value());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:66:13
+  --> tests/ui/manual_saturating_arithmetic.rs:64:13
    |
 LL |     let _ = 1i32.checked_sub(-1).unwrap_or(i32::MAX);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i32.saturating_sub(-1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:68:13
+  --> tests/ui/manual_saturating_arithmetic.rs:66:13
    |
 LL |     let _ = 1i8.checked_sub(-1).unwrap_or(127);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `saturating_sub`: `1i8.saturating_sub(-1)`
 
 error: manual saturating arithmetic
-  --> tests/ui/manual_saturating_arithmetic.rs:70:13
+  --> tests/ui/manual_saturating_arithmetic.rs:68:13
    |
 LL |       let _ = 1i128
    |  _____________^