about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--clippy_lints/src/blocks_in_if_conditions.rs6
-rw-r--r--clippy_lints/src/disallowed_methods.rs2
-rw-r--r--clippy_lints/src/disallowed_types.rs2
-rw-r--r--clippy_lints/src/formatting.rs14
-rw-r--r--clippy_lints/src/implicit_hasher.rs3
-rw-r--r--clippy_lints/src/infinite_iter.rs12
-rw-r--r--clippy_lints/src/loops/single_element_loop.rs24
-rw-r--r--clippy_lints/src/matches.rs62
-rw-r--r--clippy_lints/src/methods/chars_cmp.rs15
-rw-r--r--clippy_lints/src/methods/manual_saturating_arithmetic.rs8
-rw-r--r--clippy_lints/src/methods/option_map_or_none.rs13
-rw-r--r--clippy_lints/src/methods/option_map_unwrap_or.rs3
-rw-r--r--clippy_lints/src/misc.rs7
-rw-r--r--clippy_lints/src/missing_enforced_import_rename.rs2
-rw-r--r--clippy_lints/src/ptr.rs8
-rw-r--r--clippy_lints/src/ranges.rs26
-rw-r--r--clippy_lints/src/swap.rs4
-rw-r--r--clippy_lints/src/transmute/unsound_collection_transmute.rs30
-rw-r--r--clippy_lints/src/types/box_collection.rs27
-rw-r--r--clippy_lints/src/types/option_option.rs30
-rw-r--r--clippy_lints/src/types/rc_buffer.rs37
-rw-r--r--clippy_lints/src/types/rc_mutex.rs6
-rw-r--r--clippy_lints/src/types/redundant_allocation.rs21
-rw-r--r--clippy_lints/src/unwrap.rs7
-rw-r--r--clippy_lints/src/utils/internal_lints.rs10
-rw-r--r--clippy_utils/src/hir_utils.rs3
-rw-r--r--clippy_utils/src/lib.rs164
-rw-r--r--clippy_utils/src/ty.rs4
-rw-r--r--doc/common_tools_writing_lints.md8
29 files changed, 249 insertions, 309 deletions
diff --git a/clippy_lints/src/blocks_in_if_conditions.rs b/clippy_lints/src/blocks_in_if_conditions.rs
index c4956bacf43..4c4dd85d518 100644
--- a/clippy_lints/src/blocks_in_if_conditions.rs
+++ b/clippy_lints/src/blocks_in_if_conditions.rs
@@ -1,8 +1,8 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
+use clippy_utils::get_parent_expr;
 use clippy_utils::higher;
 use clippy_utils::source::snippet_block_with_applicability;
 use clippy_utils::ty::implements_trait;
-use clippy_utils::{differing_macro_contexts, get_parent_expr};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, Visitor};
@@ -97,7 +97,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
                         if let Some(ex) = &block.expr {
                             // don't dig into the expression here, just suggest that they remove
                             // the block
-                            if expr.span.from_expansion() || differing_macro_contexts(expr.span, ex.span) {
+                            if expr.span.from_expansion() || ex.span.from_expansion() {
                                 return;
                             }
                             let mut applicability = Applicability::MachineApplicable;
@@ -122,7 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for BlocksInIfConditions {
                         }
                     } else {
                         let span = block.expr.as_ref().map_or_else(|| block.stmts[0].span, |e| e.span);
-                        if span.from_expansion() || differing_macro_contexts(expr.span, span) {
+                        if span.from_expansion() || expr.span.from_expansion() {
                             return;
                         }
                         // move block higher
diff --git a/clippy_lints/src/disallowed_methods.rs b/clippy_lints/src/disallowed_methods.rs
index 73c00d97020..4c12202c84a 100644
--- a/clippy_lints/src/disallowed_methods.rs
+++ b/clippy_lints/src/disallowed_methods.rs
@@ -77,7 +77,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
     fn check_crate(&mut self, cx: &LateContext<'_>) {
         for (index, conf) in self.conf_disallowed.iter().enumerate() {
             let segs: Vec<_> = conf.path().split("::").collect();
-            if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &segs) {
+            if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &segs) {
                 self.disallowed.insert(id, index);
             }
         }
diff --git a/clippy_lints/src/disallowed_types.rs b/clippy_lints/src/disallowed_types.rs
index ea4b49b46fe..14f89edce61 100644
--- a/clippy_lints/src/disallowed_types.rs
+++ b/clippy_lints/src/disallowed_types.rs
@@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedTypes {
                 ),
             };
             let segs: Vec<_> = path.split("::").collect();
-            match clippy_utils::path_to_res(cx, &segs) {
+            match clippy_utils::def_path_res(cx, &segs) {
                 Res::Def(_, id) => {
                     self.def_ids.insert(id, reason);
                 },
diff --git a/clippy_lints/src/formatting.rs b/clippy_lints/src/formatting.rs
index ae18f8081bc..57964b8d48e 100644
--- a/clippy_lints/src/formatting.rs
+++ b/clippy_lints/src/formatting.rs
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note};
-use clippy_utils::differing_macro_contexts;
 use clippy_utils::source::snippet_opt;
 use if_chain::if_chain;
 use rustc_ast::ast::{BinOpKind, Block, Expr, ExprKind, StmtKind, UnOp};
@@ -135,7 +134,7 @@ impl EarlyLintPass for Formatting {
 /// Implementation of the `SUSPICIOUS_ASSIGNMENT_FORMATTING` lint.
 fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
     if let ExprKind::Assign(ref lhs, ref rhs, _) = expr.kind {
-        if !differing_macro_contexts(lhs.span, rhs.span) && !lhs.span.from_expansion() {
+        if !lhs.span.from_expansion() && !rhs.span.from_expansion() {
             let eq_span = lhs.span.between(rhs.span);
             if let ExprKind::Unary(op, ref sub_rhs) = rhs.kind {
                 if let Some(eq_snippet) = snippet_opt(cx, eq_span) {
@@ -165,7 +164,7 @@ fn check_assign(cx: &EarlyContext<'_>, expr: &Expr) {
 fn check_unop(cx: &EarlyContext<'_>, expr: &Expr) {
     if_chain! {
         if let ExprKind::Binary(ref binop, ref lhs, ref rhs) = expr.kind;
-        if !differing_macro_contexts(lhs.span, rhs.span) && !lhs.span.from_expansion();
+        if !lhs.span.from_expansion() && !rhs.span.from_expansion();
         // span between BinOp LHS and RHS
         let binop_span = lhs.span.between(rhs.span);
         // if RHS is an UnOp
@@ -206,8 +205,8 @@ fn check_else(cx: &EarlyContext<'_>, expr: &Expr) {
     if_chain! {
         if let ExprKind::If(_, then, Some(else_)) = &expr.kind;
         if is_block(else_) || is_if(else_);
-        if !differing_macro_contexts(then.span, else_.span);
-        if !then.span.from_expansion() && !in_external_macro(cx.sess(), expr.span);
+        if !then.span.from_expansion() && !else_.span.from_expansion();
+        if !in_external_macro(cx.sess(), expr.span);
 
         // workaround for rust-lang/rust#43081
         if expr.span.lo().0 != 0 && expr.span.hi().0 != 0;
@@ -268,7 +267,7 @@ fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
         for element in array {
             if_chain! {
                 if let ExprKind::Binary(ref op, ref lhs, _) = element.kind;
-                if has_unary_equivalent(op.node) && !differing_macro_contexts(lhs.span, op.span);
+                if has_unary_equivalent(op.node) && lhs.span.ctxt() == op.span.ctxt();
                 let space_span = lhs.span.between(op.span);
                 if let Some(space_snippet) = snippet_opt(cx, space_span);
                 let lint_span = lhs.span.with_lo(lhs.span.hi());
@@ -291,8 +290,7 @@ fn check_array(cx: &EarlyContext<'_>, expr: &Expr) {
 
 fn check_missing_else(cx: &EarlyContext<'_>, first: &Expr, second: &Expr) {
     if_chain! {
-        if !differing_macro_contexts(first.span, second.span);
-        if !first.span.from_expansion();
+        if !first.span.from_expansion() && !second.span.from_expansion();
         if let ExprKind::If(cond_expr, ..) = &first.kind;
         if is_block(second) || is_if(second);
 
diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs
index eed25e9bc0e..3fb7e5dfd6c 100644
--- a/clippy_lints/src/implicit_hasher.rs
+++ b/clippy_lints/src/implicit_hasher.rs
@@ -17,7 +17,6 @@ use rustc_typeck::hir_ty_to_ty;
 use if_chain::if_chain;
 
 use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
-use clippy_utils::differing_macro_contexts;
 use clippy_utils::source::{snippet, snippet_opt};
 use clippy_utils::ty::is_type_diagnostic_item;
 
@@ -123,7 +122,7 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
                 vis.visit_ty(impl_.self_ty);
 
                 for target in &vis.found {
-                    if differing_macro_contexts(item.span, target.span()) {
+                    if item.span.ctxt() != target.span().ctxt() {
                         return;
                     }
 
diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs
index 3008e86ef8b..b6badef02f5 100644
--- a/clippy_lints/src/infinite_iter.rs
+++ b/clippy_lints/src/infinite_iter.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
-use clippy_utils::{get_trait_def_id, higher, is_qpath_def_path, paths};
+use clippy_utils::{get_trait_def_id, higher, match_def_path, path_def_id, paths};
 use rustc_hir::{BorrowKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -167,13 +167,9 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
         },
         ExprKind::Block(block, _) => block.expr.as_ref().map_or(Finite, |e| is_infinite(cx, e)),
         ExprKind::Box(e) | ExprKind::AddrOf(BorrowKind::Ref, _, e) => is_infinite(cx, e),
-        ExprKind::Call(path, _) => {
-            if let ExprKind::Path(ref qpath) = path.kind {
-                is_qpath_def_path(cx, qpath, path.hir_id, &paths::ITER_REPEAT).into()
-            } else {
-                Finite
-            }
-        },
+        ExprKind::Call(path, _) => path_def_id(cx, path)
+            .map_or(false, |id| match_def_path(cx, id, &paths::ITER_REPEAT))
+            .into(),
         ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(),
         _ => Finite,
     }
diff --git a/clippy_lints/src/loops/single_element_loop.rs b/clippy_lints/src/loops/single_element_loop.rs
index 15f419e4410..36ecd83f7d6 100644
--- a/clippy_lints/src/loops/single_element_loop.rs
+++ b/clippy_lints/src/loops/single_element_loop.rs
@@ -1,10 +1,9 @@
 use super::SINGLE_ELEMENT_LOOP;
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::single_segment_path;
-use clippy_utils::source::{indent_of, snippet};
+use clippy_utils::source::{indent_of, snippet_with_applicability};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
-use rustc_hir::{BorrowKind, Expr, ExprKind, Pat, PatKind};
+use rustc_hir::{BorrowKind, Expr, ExprKind, Pat};
 use rustc_lint::LateContext;
 
 pub(super) fn check<'tcx>(
@@ -16,24 +15,21 @@ pub(super) fn check<'tcx>(
 ) {
     let arg_expr = match arg.kind {
         ExprKind::AddrOf(BorrowKind::Ref, _, ref_arg) => ref_arg,
-        ExprKind::MethodCall(method, args, _) if args.len() == 1 && method.ident.name == rustc_span::sym::iter => {
-            &args[0]
-        },
+        ExprKind::MethodCall(method, [arg], _) if method.ident.name == rustc_span::sym::iter => arg,
         _ => return,
     };
     if_chain! {
-        if let PatKind::Binding(.., target, _) = pat.kind;
         if let ExprKind::Array([arg_expression]) = arg_expr.kind;
-        if let ExprKind::Path(ref list_item) = arg_expression.kind;
-        if let Some(list_item_name) = single_segment_path(list_item).map(|ps| ps.ident.name);
         if let ExprKind::Block(block, _) = body.kind;
         if !block.stmts.is_empty();
-
         then {
-            let mut block_str = snippet(cx, block.span, "..").into_owned();
+            let mut applicability = Applicability::MachineApplicable;
+            let pat_snip = snippet_with_applicability(cx, pat.span, "..", &mut applicability);
+            let arg_snip = snippet_with_applicability(cx, arg_expression.span, "..", &mut applicability);
+            let mut block_str = snippet_with_applicability(cx, block.span, "..", &mut applicability).into_owned();
             block_str.remove(0);
             block_str.pop();
-
+            let indent = " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0));
 
             span_lint_and_sugg(
                 cx,
@@ -41,8 +37,8 @@ pub(super) fn check<'tcx>(
                 expr.span,
                 "for loop over a single element",
                 "try",
-                format!("{{\n{}let {} = &{};{}}}", " ".repeat(indent_of(cx, block.stmts[0].span).unwrap_or(0)), target.name, list_item_name, block_str),
-                Applicability::MachineApplicable
+                format!("{{\n{}let {} = &{};{}}}", indent, pat_snip, arg_snip, block_str),
+                applicability,
             )
         }
     }
diff --git a/clippy_lints/src/matches.rs b/clippy_lints/src/matches.rs
index e0cbadeb645..67fb918d20c 100644
--- a/clippy_lints/src/matches.rs
+++ b/clippy_lints/src/matches.rs
@@ -1860,22 +1860,22 @@ where
 mod redundant_pattern_match {
     use super::REDUNDANT_PATTERN_MATCHING;
     use clippy_utils::diagnostics::span_lint_and_then;
-    use clippy_utils::higher;
     use clippy_utils::source::snippet;
     use clippy_utils::sugg::Sugg;
     use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, is_type_lang_item, match_type};
-    use clippy_utils::{is_lang_ctor, is_qpath_def_path, is_trait_method, paths};
+    use clippy_utils::{higher, match_def_path};
+    use clippy_utils::{is_lang_ctor, is_trait_method, paths};
     use if_chain::if_chain;
     use rustc_ast::ast::LitKind;
     use rustc_data_structures::fx::FxHashSet;
     use rustc_errors::Applicability;
-    use rustc_hir::LangItem::{OptionNone, OptionSome, PollPending, PollReady, ResultErr, ResultOk};
+    use rustc_hir::LangItem::{OptionNone, PollPending};
     use rustc_hir::{
         intravisit::{walk_expr, Visitor},
         Arm, Block, Expr, ExprKind, LangItem, MatchSource, Node, Pat, PatKind, QPath, UnOp,
     };
     use rustc_lint::LateContext;
-    use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
+    use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty};
     use rustc_span::sym;
 
     pub fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
@@ -2051,28 +2051,31 @@ mod redundant_pattern_match {
         has_else: bool,
     ) {
         // also look inside refs
-        let mut kind = &let_pat.kind;
         // if we have &None for example, peel it so we can detect "if let None = x"
-        if let PatKind::Ref(inner, _mutability) = kind {
-            kind = &inner.kind;
-        }
+        let check_pat = match let_pat.kind {
+            PatKind::Ref(inner, _mutability) => inner,
+            _ => let_pat,
+        };
         let op_ty = cx.typeck_results().expr_ty(let_expr);
         // Determine which function should be used, and the type contained by the corresponding
         // variant.
-        let (good_method, inner_ty) = match kind {
-            PatKind::TupleStruct(ref path, [sub_pat], _) => {
+        let (good_method, inner_ty) = match check_pat.kind {
+            PatKind::TupleStruct(ref qpath, [sub_pat], _) => {
                 if let PatKind::Wild = sub_pat.kind {
-                    if is_lang_ctor(cx, path, ResultOk) {
+                    let res = cx.typeck_results().qpath_res(qpath, check_pat.hir_id);
+                    let Some(id) = res.opt_def_id().and_then(|ctor_id| cx.tcx.parent(ctor_id)) else { return };
+                    let lang_items = cx.tcx.lang_items();
+                    if Some(id) == lang_items.result_ok_variant() {
                         ("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))
-                    } else if is_lang_ctor(cx, path, ResultErr) {
+                    } else if Some(id) == lang_items.result_err_variant() {
                         ("is_err()", try_get_generic_ty(op_ty, 1).unwrap_or(op_ty))
-                    } else if is_lang_ctor(cx, path, OptionSome) {
+                    } else if Some(id) == lang_items.option_some_variant() {
                         ("is_some()", op_ty)
-                    } else if is_lang_ctor(cx, path, PollReady) {
+                    } else if Some(id) == lang_items.poll_ready_variant() {
                         ("is_ready()", op_ty)
-                    } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V4) {
+                    } else if match_def_path(cx, id, &paths::IPADDR_V4) {
                         ("is_ipv4()", op_ty)
-                    } else if is_qpath_def_path(cx, path, sub_pat.hir_id, &paths::IPADDR_V6) {
+                    } else if match_def_path(cx, id, &paths::IPADDR_V6) {
                         ("is_ipv6()", op_ty)
                     } else {
                         return;
@@ -2272,17 +2275,22 @@ mod redundant_pattern_match {
         should_be_left: &'a str,
         should_be_right: &'a str,
     ) -> Option<&'a str> {
-        let body_node_pair = if is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_left)
-            && is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_right)
-        {
-            (&(*arms[0].body).kind, &(*arms[1].body).kind)
-        } else if is_qpath_def_path(cx, path_right, arms[1].pat.hir_id, expected_left)
-            && is_qpath_def_path(cx, path_left, arms[0].pat.hir_id, expected_right)
-        {
-            (&(*arms[1].body).kind, &(*arms[0].body).kind)
-        } else {
-            return None;
-        };
+        let left_id = cx
+            .typeck_results()
+            .qpath_res(path_left, arms[0].pat.hir_id)
+            .opt_def_id()?;
+        let right_id = cx
+            .typeck_results()
+            .qpath_res(path_right, arms[1].pat.hir_id)
+            .opt_def_id()?;
+        let body_node_pair =
+            if match_def_path(cx, left_id, expected_left) && match_def_path(cx, right_id, expected_right) {
+                (&(*arms[0].body).kind, &(*arms[1].body).kind)
+            } else if match_def_path(cx, right_id, expected_left) && match_def_path(cx, right_id, expected_right) {
+                (&(*arms[1].body).kind, &(*arms[0].body).kind)
+            } else {
+                return None;
+            };
 
         match body_node_pair {
             (ExprKind::Lit(ref lit_left), ExprKind::Lit(ref lit_right)) => match (&lit_left.node, &lit_right.node) {
diff --git a/clippy_lints/src/methods/chars_cmp.rs b/clippy_lints/src/methods/chars_cmp.rs
index 514c4118765..2cf2c5641bf 100644
--- a/clippy_lints/src/methods/chars_cmp.rs
+++ b/clippy_lints/src/methods/chars_cmp.rs
@@ -1,13 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{method_chain_args, single_segment_path};
+use clippy_utils::{method_chain_args, path_def_id};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 use rustc_lint::Lint;
-use rustc_middle::ty;
-use rustc_span::sym;
+use rustc_middle::ty::{self, DefIdTree};
 
 /// Wrapper fn for `CHARS_NEXT_CMP` and `CHARS_LAST_CMP` lints.
 pub(super) fn check(
@@ -19,11 +18,9 @@ pub(super) fn check(
 ) -> bool {
     if_chain! {
         if let Some(args) = method_chain_args(info.chain, chain_methods);
-        if let hir::ExprKind::Call(fun, arg_char) = info.other.kind;
-        if arg_char.len() == 1;
-        if let hir::ExprKind::Path(ref qpath) = fun.kind;
-        if let Some(segment) = single_segment_path(qpath);
-        if segment.ident.name == sym::Some;
+        if let hir::ExprKind::Call(fun, [arg_char]) = info.other.kind;
+        if let Some(id) = path_def_id(cx, fun).and_then(|ctor_id| cx.tcx.parent(ctor_id));
+        if Some(id) == cx.tcx.lang_items().option_some_variant();
         then {
             let mut applicability = Applicability::MachineApplicable;
             let self_ty = cx.typeck_results().expr_ty_adjusted(&args[0][0]).peel_refs();
@@ -42,7 +39,7 @@ pub(super) fn check(
                         if info.eq { "" } else { "!" },
                         snippet_with_applicability(cx, args[0][0].span, "..", &mut applicability),
                         suggest,
-                        snippet_with_applicability(cx, arg_char[0].span, "..", &mut applicability)),
+                        snippet_with_applicability(cx, arg_char.span, "..", &mut applicability)),
                 applicability,
             );
 
diff --git a/clippy_lints/src/methods/manual_saturating_arithmetic.rs b/clippy_lints/src/methods/manual_saturating_arithmetic.rs
index 4307cbf0050..0fe510beaa0 100644
--- a/clippy_lints/src/methods/manual_saturating_arithmetic.rs
+++ b/clippy_lints/src/methods/manual_saturating_arithmetic.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_qpath_def_path;
 use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{match_def_path, path_def_id};
 use if_chain::if_chain;
 use rustc_ast::ast;
 use rustc_errors::Applicability;
@@ -93,12 +93,12 @@ fn is_min_or_max<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>) -> Option<M
     let ty_str = ty.to_string();
 
     // `std::T::MAX` `std::T::MIN` constants
-    if let hir::ExprKind::Path(path) = &expr.kind {
-        if is_qpath_def_path(cx, path, expr.hir_id, &["core", &ty_str, "MAX"][..]) {
+    if let Some(id) = path_def_id(cx, expr) {
+        if match_def_path(cx, id, &["core", &ty_str, "MAX"]) {
             return Some(MinMax::Max);
         }
 
-        if is_qpath_def_path(cx, path, expr.hir_id, &["core", &ty_str, "MIN"][..]) {
+        if match_def_path(cx, id, &["core", &ty_str, "MIN"]) {
             return Some(MinMax::Min);
         }
     }
diff --git a/clippy_lints/src/methods/option_map_or_none.rs b/clippy_lints/src/methods/option_map_or_none.rs
index 5e5c1038e82..bdf8cea1207 100644
--- a/clippy_lints/src/methods/option_map_or_none.rs
+++ b/clippy_lints/src/methods/option_map_or_none.rs
@@ -1,11 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{is_lang_ctor, single_segment_path};
+use clippy_utils::{is_lang_ctor, path_def_id};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_lint::LateContext;
+use rustc_middle::ty::DefIdTree;
 use rustc_span::symbol::sym;
 
 use super::OPTION_MAP_OR_NONE;
@@ -76,13 +77,11 @@ pub(super) fn check<'tcx>(
         if let hir::ExprKind::Closure(_, _, id, span, _) = map_arg.kind;
             let arg_snippet = snippet(cx, span, "..");
             let body = cx.tcx.hir().body(id);
-                if let Some((func, arg_char)) = reduce_unit_expression(cx, &body.value);
-                if arg_char.len() == 1;
-                if let hir::ExprKind::Path(ref qpath) = func.kind;
-                if let Some(segment) = single_segment_path(qpath);
-                if segment.ident.name == sym::Some;
+                if let Some((func, [arg_char])) = reduce_unit_expression(cx, &body.value);
+                if let Some(id) = path_def_id(cx, func).and_then(|ctor_id| cx.tcx.parent(ctor_id));
+                if Some(id) == cx.tcx.lang_items().option_some_variant();
                 then {
-                    let func_snippet = snippet(cx, arg_char[0].span, "..");
+                    let func_snippet = snippet(cx, arg_char.span, "..");
                     let msg = "called `map_or(None, ..)` on an `Option` value. This can be done more directly by calling \
                        `map(..)` instead";
                     return span_lint_and_sugg(
diff --git a/clippy_lints/src/methods/option_map_unwrap_or.rs b/clippy_lints/src/methods/option_map_unwrap_or.rs
index 9c6f4211031..6c641af59f9 100644
--- a/clippy_lints/src/methods/option_map_unwrap_or.rs
+++ b/clippy_lints/src/methods/option_map_unwrap_or.rs
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::differing_macro_contexts;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::is_copy;
 use clippy_utils::ty::is_type_diagnostic_item;
@@ -48,7 +47,7 @@ pub(super) fn check<'tcx>(
             }
         }
 
-        if differing_macro_contexts(unwrap_arg.span, map_span) {
+        if unwrap_arg.span.ctxt() != map_span.ctxt() {
             return;
         }
 
diff --git a/clippy_lints/src/misc.rs b/clippy_lints/src/misc.rs
index 3918bdbdf43..ac82dd306a5 100644
--- a/clippy_lints/src/misc.rs
+++ b/clippy_lints/src/misc.rs
@@ -20,8 +20,8 @@ use rustc_span::symbol::sym;
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{
-    expr_path_res, get_item_name, get_parent_expr, in_constant, is_diag_trait_item, is_integer_const, iter_input_pats,
-    last_path_segment, match_any_def_paths, paths, unsext, SpanlessEq,
+    get_item_name, get_parent_expr, in_constant, is_diag_trait_item, is_integer_const, iter_input_pats,
+    last_path_segment, match_any_def_paths, path_def_id, paths, unsext, SpanlessEq,
 };
 
 declare_clippy_lint! {
@@ -583,8 +583,7 @@ fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left:
             )
         },
         ExprKind::Call(path, [arg]) => {
-            if expr_path_res(cx, path)
-                .opt_def_id()
+            if path_def_id(cx, path)
                 .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM]))
                 .is_some()
             {
diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs
index 566e15ab2a6..3d0a2382283 100644
--- a/clippy_lints/src/missing_enforced_import_rename.rs
+++ b/clippy_lints/src/missing_enforced_import_rename.rs
@@ -58,7 +58,7 @@ impl_lint_pass!(ImportRename => [MISSING_ENFORCED_IMPORT_RENAMES]);
 impl LateLintPass<'_> for ImportRename {
     fn check_crate(&mut self, cx: &LateContext<'_>) {
         for Rename { path, rename } in &self.conf_renames {
-            if let Res::Def(_, id) = clippy_utils::path_to_res(cx, &path.split("::").collect::<Vec<_>>()) {
+            if let Res::Def(_, id) = clippy_utils::def_path_res(cx, &path.split("::").collect::<Vec<_>>()) {
                 self.renames.insert(id, Symbol::intern(rename));
             }
         }
diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs
index 5c13f660c6b..e0ce1b7db00 100644
--- a/clippy_lints/src/ptr.rs
+++ b/clippy_lints/src/ptr.rs
@@ -3,9 +3,7 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::expr_sig;
-use clippy_utils::{
-    expr_path_res, get_expr_use_or_unification_node, is_lint_allowed, match_any_diagnostic_items, path_to_local, paths,
-};
+use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local, paths};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
@@ -673,8 +671,8 @@ fn get_rptr_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutabil
 
 fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     if let ExprKind::Call(pathexp, []) = expr.kind {
-        expr_path_res(cx, pathexp).opt_def_id().map_or(false, |id| {
-            match_any_diagnostic_items(cx, id, &[sym::ptr_null, sym::ptr_null_mut]).is_some()
+        path_def_id(cx, pathexp).map_or(false, |id| {
+            matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut))
         })
     } else {
         false
diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs
index 8065ed3ffc5..e213c208794 100644
--- a/clippy_lints/src/ranges.rs
+++ b/clippy_lints/src/ranges.rs
@@ -2,19 +2,18 @@ use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, single_segment_path};
+use clippy_utils::{get_parent_expr, in_constant, is_integer_const, meets_msrv, msrvs, path_to_local};
 use clippy_utils::{higher, SpanlessEq};
 use if_chain::if_chain;
 use rustc_ast::ast::RangeLimits;
 use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, QPath};
+use rustc_hir::{BinOpKind, Expr, ExprKind, HirId, PathSegment, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
 use rustc_semver::RustcVersion;
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::source_map::{Span, Spanned};
 use rustc_span::sym;
-use rustc_span::symbol::Ident;
 use std::cmp::Ordering;
 
 declare_clippy_lint! {
@@ -220,12 +219,12 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'
         _ => return,
     };
     // value, name, order (higher/lower), inclusiveness
-    if let (Some((lval, lname, name_span, lval_span, lord, linc)), Some((rval, rname, _, rval_span, rord, rinc))) =
+    if let (Some((lval, lid, name_span, lval_span, lord, linc)), Some((rval, rid, _, rval_span, rord, rinc))) =
         (check_range_bounds(cx, l), check_range_bounds(cx, r))
     {
         // we only lint comparisons on the same name and with different
         // direction
-        if lname != rname || lord == rord {
+        if lid != rid || lord == rord {
             return;
         }
         let ord = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(l), &lval, &rval);
@@ -293,7 +292,7 @@ fn check_possible_range_contains(cx: &LateContext<'_>, op: BinOpKind, l: &Expr<'
     }
 }
 
-fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, Ident, Span, Span, Ordering, bool)> {
+fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant, HirId, Span, Span, Ordering, bool)> {
     if let ExprKind::Binary(ref op, l, r) = ex.kind {
         let (inclusive, ordering) = match op.node {
             BinOpKind::Gt => (false, Ordering::Greater),
@@ -302,11 +301,11 @@ fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant,
             BinOpKind::Le => (true, Ordering::Less),
             _ => return None,
         };
-        if let Some(id) = match_ident(l) {
+        if let Some(id) = path_to_local(l) {
             if let Some((c, _)) = constant(cx, cx.typeck_results(), r) {
                 return Some((c, id, l.span, r.span, ordering, inclusive));
             }
-        } else if let Some(id) = match_ident(r) {
+        } else if let Some(id) = path_to_local(r) {
             if let Some((c, _)) = constant(cx, cx.typeck_results(), l) {
                 return Some((c, id, r.span, l.span, ordering.reverse(), inclusive));
             }
@@ -315,17 +314,6 @@ fn check_range_bounds(cx: &LateContext<'_>, ex: &Expr<'_>) -> Option<(Constant,
     None
 }
 
-fn match_ident(e: &Expr<'_>) -> Option<Ident> {
-    if let ExprKind::Path(ref qpath) = e.kind {
-        if let Some(seg) = single_segment_path(qpath) {
-            if seg.args.is_none() {
-                return Some(seg.ident);
-            }
-        }
-    }
-    None
-}
-
 fn check_range_zip_with_len(cx: &LateContext<'_>, path: &PathSegment<'_>, args: &[Expr<'_>], span: Span) {
     if_chain! {
         if path.ident.as_str() == "zip";
diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs
index 4c10b12437d..1885f3ca414 100644
--- a/clippy_lints/src/swap.rs
+++ b/clippy_lints/src/swap.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{can_mut_borrow_both, differing_macro_contexts, eq_expr_value, std_or_core};
+use clippy_utils::{can_mut_borrow_both, eq_expr_value, std_or_core};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Block, Expr, ExprKind, PatKind, QPath, Stmt, StmtKind};
@@ -172,7 +172,7 @@ fn check_suspicious_swap(cx: &LateContext<'_>, block: &Block<'_>) {
         if_chain! {
             if let StmtKind::Semi(first) = w[0].kind;
             if let StmtKind::Semi(second) = w[1].kind;
-            if !differing_macro_contexts(first.span, second.span);
+            if first.span.ctxt() == second.span.ctxt();
             if let ExprKind::Assign(lhs0, rhs0, _) = first.kind;
             if let ExprKind::Assign(lhs1, rhs1, _) = second.kind;
             if eq_expr_value(cx, lhs0, rhs1);
diff --git a/clippy_lints/src/transmute/unsound_collection_transmute.rs b/clippy_lints/src/transmute/unsound_collection_transmute.rs
index 2ce8d4031d7..2d67401a15f 100644
--- a/clippy_lints/src/transmute/unsound_collection_transmute.rs
+++ b/clippy_lints/src/transmute/unsound_collection_transmute.rs
@@ -1,29 +1,31 @@
 use super::utils::is_layout_incompatible;
 use super::UNSOUND_COLLECTION_TRANSMUTE;
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::match_any_diagnostic_items;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
-use rustc_span::symbol::{sym, Symbol};
-
-// used to check for UNSOUND_COLLECTION_TRANSMUTE
-static COLLECTIONS: &[Symbol] = &[
-    sym::Vec,
-    sym::VecDeque,
-    sym::BinaryHeap,
-    sym::BTreeSet,
-    sym::BTreeMap,
-    sym::HashSet,
-    sym::HashMap,
-];
+use rustc_span::symbol::sym;
 
 /// Checks for `unsound_collection_transmute` lint.
 /// Returns `true` if it's triggered, otherwise returns `false`.
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, from_ty: Ty<'tcx>, to_ty: Ty<'tcx>) -> bool {
     match (&from_ty.kind(), &to_ty.kind()) {
         (ty::Adt(from_adt, from_substs), ty::Adt(to_adt, to_substs)) => {
-            if from_adt.did != to_adt.did || match_any_diagnostic_items(cx, to_adt.did, COLLECTIONS).is_none() {
+            if from_adt.did != to_adt.did {
+                return false;
+            }
+            if !matches!(
+                cx.tcx.get_diagnostic_name(to_adt.did),
+                Some(
+                    sym::BTreeMap
+                        | sym::BTreeSet
+                        | sym::BinaryHeap
+                        | sym::HashMap
+                        | sym::HashSet
+                        | sym::Vec
+                        | sym::VecDeque
+                )
+            ) {
                 return false;
             }
             if from_substs
diff --git a/clippy_lints/src/types/box_collection.rs b/clippy_lints/src/types/box_collection.rs
index 538c10a5b20..21a9558ec07 100644
--- a/clippy_lints/src/types/box_collection.rs
+++ b/clippy_lints/src/types/box_collection.rs
@@ -1,8 +1,8 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::is_ty_param_diagnostic_item;
+use clippy_utils::{path_def_id, qpath_generic_tys};
 use rustc_hir::{self as hir, def_id::DefId, QPath};
 use rustc_lint::LateContext;
-use rustc_span::symbol::sym;
+use rustc_span::{sym, Symbol};
 
 use super::BOX_COLLECTION;
 
@@ -11,10 +11,9 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
         if Some(def_id) == cx.tcx.lang_items().owned_box();
         if let Some(item_type) = get_std_collection(cx, qpath);
         then {
-            let generic = if item_type == "String" {
-                ""
-            } else {
-                "<..>"
+            let generic = match item_type {
+                sym::String => "",
+                _ => "<..>",
             };
             span_lint_and_help(
                 cx,
@@ -37,14 +36,10 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
     }
 }
 
-fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> {
-    if is_ty_param_diagnostic_item(cx, qpath, sym::Vec).is_some() {
-        Some("Vec")
-    } else if is_ty_param_diagnostic_item(cx, qpath, sym::String).is_some() {
-        Some("String")
-    } else if is_ty_param_diagnostic_item(cx, qpath, sym::HashMap).is_some() {
-        Some("HashMap")
-    } else {
-        None
-    }
+fn get_std_collection(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<Symbol> {
+    let param = qpath_generic_tys(qpath).next()?;
+    let id = path_def_id(cx, param)?;
+    cx.tcx
+        .get_diagnostic_name(id)
+        .filter(|&name| matches!(name, sym::HashMap | sym::String | sym::Vec))
 }
diff --git a/clippy_lints/src/types/option_option.rs b/clippy_lints/src/types/option_option.rs
index 903e62995c6..8767e3c30a6 100644
--- a/clippy_lints/src/types/option_option.rs
+++ b/clippy_lints/src/types/option_option.rs
@@ -1,5 +1,6 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::is_ty_param_diagnostic_item;
+use clippy_utils::{path_def_id, qpath_generic_tys};
+use if_chain::if_chain;
 use rustc_hir::{self as hir, def_id::DefId, QPath};
 use rustc_lint::LateContext;
 use rustc_span::symbol::sym;
@@ -7,16 +8,21 @@ use rustc_span::symbol::sym;
 use super::OPTION_OPTION;
 
 pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
-    if cx.tcx.is_diagnostic_item(sym::Option, def_id) && is_ty_param_diagnostic_item(cx, qpath, sym::Option).is_some() {
-        span_lint(
-            cx,
-            OPTION_OPTION,
-            hir_ty.span,
-            "consider using `Option<T>` instead of `Option<Option<T>>` or a custom \
-                                 enum if you need to distinguish all 3 cases",
-        );
-        true
-    } else {
-        false
+    if_chain! {
+        if cx.tcx.is_diagnostic_item(sym::Option, def_id);
+        if let Some(arg) = qpath_generic_tys(qpath).next();
+        if path_def_id(cx, arg) == Some(def_id);
+        then {
+            span_lint(
+                cx,
+                OPTION_OPTION,
+                hir_ty.span,
+                "consider using `Option<T>` instead of `Option<Option<T>>` or a custom \
+                                     enum if you need to distinguish all 3 cases",
+            );
+            true
+        } else {
+            false
+        }
     }
 }
diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs
index 31c4abdfc95..4d72a29e8c7 100644
--- a/clippy_lints/src/types/rc_buffer.rs
+++ b/clippy_lints/src/types/rc_buffer.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item};
+use clippy_utils::{path_def_id, qpath_generic_tys};
 use rustc_errors::Applicability;
 use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind};
 use rustc_lint::LateContext;
@@ -20,12 +20,17 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
                 format!("Rc<{}>", alternate),
                 Applicability::MachineApplicable,
             );
-        } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Vec) {
+        } else {
+            let Some(ty) = qpath_generic_tys(qpath).next() else { return false };
+            let Some(id) = path_def_id(cx, ty) else { return false };
+            if !cx.tcx.is_diagnostic_item(sym::Vec, id) {
+                return false;
+            }
             let qpath = match &ty.kind {
                 TyKind::Path(qpath) => qpath,
                 _ => return false,
             };
-            let inner_span = match get_qpath_generic_tys(qpath).next() {
+            let inner_span = match qpath_generic_tys(qpath).next() {
                 Some(ty) => ty.span,
                 None => return false,
             };
@@ -55,12 +60,16 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
                 format!("Arc<{}>", alternate),
                 Applicability::MachineApplicable,
             );
-        } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Vec) {
+        } else if let Some(ty) = qpath_generic_tys(qpath).next() {
+            let Some(id) = path_def_id(cx, ty) else { return false };
+            if !cx.tcx.is_diagnostic_item(sym::Vec, id) {
+                return false;
+            }
             let qpath = match &ty.kind {
                 TyKind::Path(qpath) => qpath,
                 _ => return false,
             };
-            let inner_span = match get_qpath_generic_tys(qpath).next() {
+            let inner_span = match qpath_generic_tys(qpath).next() {
                 Some(ty) => ty.span,
                 None => return false,
             };
@@ -85,13 +94,13 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
 }
 
 fn match_buffer_type(cx: &LateContext<'_>, qpath: &QPath<'_>) -> Option<&'static str> {
-    if is_ty_param_diagnostic_item(cx, qpath, sym::String).is_some() {
-        Some("str")
-    } else if is_ty_param_diagnostic_item(cx, qpath, sym::OsString).is_some() {
-        Some("std::ffi::OsStr")
-    } else if is_ty_param_diagnostic_item(cx, qpath, sym::PathBuf).is_some() {
-        Some("std::path::Path")
-    } else {
-        None
-    }
+    let ty = qpath_generic_tys(qpath).next()?;
+    let id = path_def_id(cx, ty)?;
+    let path = match cx.tcx.get_diagnostic_name(id)? {
+        sym::String => "str",
+        sym::OsString => "std::ffi::OsStr",
+        sym::PathBuf => "std::path::Path",
+        _ => return None,
+    };
+    Some(path)
 }
diff --git a/clippy_lints/src/types/rc_mutex.rs b/clippy_lints/src/types/rc_mutex.rs
index d54608a07bb..a75972cf3dd 100644
--- a/clippy_lints/src/types/rc_mutex.rs
+++ b/clippy_lints/src/types/rc_mutex.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::is_ty_param_diagnostic_item;
+use clippy_utils::{path_def_id, qpath_generic_tys};
 use if_chain::if_chain;
 use rustc_hir::{self as hir, def_id::DefId, QPath};
 use rustc_lint::LateContext;
@@ -10,7 +10,9 @@ use super::RC_MUTEX;
 pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_>, def_id: DefId) -> bool {
     if_chain! {
         if cx.tcx.is_diagnostic_item(sym::Rc, def_id) ;
-        if let Some(_) = is_ty_param_diagnostic_item(cx, qpath, sym::Mutex) ;
+        if let Some(arg) = qpath_generic_tys(qpath).next();
+        if let Some(id) = path_def_id(cx, arg);
+        if cx.tcx.is_diagnostic_item(sym::Mutex, id);
         then {
             span_lint_and_help(
                 cx,
diff --git a/clippy_lints/src/types/redundant_allocation.rs b/clippy_lints/src/types/redundant_allocation.rs
index ac7bdd6a1eb..10d2ae2eb1d 100644
--- a/clippy_lints/src/types/redundant_allocation.rs
+++ b/clippy_lints/src/types/redundant_allocation.rs
@@ -1,8 +1,8 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::{snippet, snippet_with_applicability};
-use clippy_utils::{get_qpath_generic_tys, is_ty_param_diagnostic_item, is_ty_param_lang_item};
+use clippy_utils::{path_def_id, qpath_generic_tys};
 use rustc_errors::Applicability;
-use rustc_hir::{self as hir, def_id::DefId, LangItem, QPath, TyKind};
+use rustc_hir::{self as hir, def_id::DefId, QPath, TyKind};
 use rustc_lint::LateContext;
 use rustc_span::symbol::sym;
 
@@ -39,21 +39,20 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
         return true;
     }
 
-    let (inner_sym, ty) = if let Some(ty) = is_ty_param_lang_item(cx, qpath, LangItem::OwnedBox) {
-        ("Box", ty)
-    } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Rc) {
-        ("Rc", ty)
-    } else if let Some(ty) = is_ty_param_diagnostic_item(cx, qpath, sym::Arc) {
-        ("Arc", ty)
-    } else {
-        return false;
+    let Some(ty) = qpath_generic_tys(qpath).next() else { return false };
+    let Some(id) = path_def_id(cx, ty) else { return false };
+    let (inner_sym, ty) = match cx.tcx.get_diagnostic_name(id) {
+        Some(sym::Arc) => ("Arc", ty),
+        Some(sym::Rc) => ("Rc", ty),
+        _ if Some(id) == cx.tcx.lang_items().owned_box() => ("Box", ty),
+        _ => return false,
     };
 
     let inner_qpath = match &ty.kind {
         TyKind::Path(inner_qpath) => inner_qpath,
         _ => return false,
     };
-    let inner_span = match get_qpath_generic_tys(inner_qpath).next() {
+    let inner_span = match qpath_generic_tys(inner_qpath).next() {
         Some(ty) => {
             // Box<Box<dyn T>> is smaller than Box<dyn T> because of wide pointers
             if matches!(ty.kind, TyKind::TraitObject(..)) {
diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs
index e9840487013..9b9e25326f9 100644
--- a/clippy_lints/src/unwrap.rs
+++ b/clippy_lints/src/unwrap.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{differing_macro_contexts, path_to_local, usage::is_potentially_mutated};
+use clippy_utils::{path_to_local, usage::is_potentially_mutated};
 use if_chain::if_chain;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor};
@@ -238,8 +238,9 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
                 if let Some(unwrappable) = self.unwrappables.iter()
                     .find(|u| u.local_id == id);
                 // Span contexts should not differ with the conditional branch
-                if !differing_macro_contexts(unwrappable.branch.span, expr.span);
-                if !differing_macro_contexts(unwrappable.branch.span, unwrappable.check.span);
+                let span_ctxt = expr.span.ctxt();
+                if unwrappable.branch.span.ctxt() == span_ctxt;
+                if unwrappable.check.span.ctxt() == span_ctxt;
                 then {
                     if call_to_unwrap == unwrappable.safe_to_unwrap {
                         let is_entire_condition = unwrappable.is_entire_condition;
diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs
index f170ff69154..dc0f515bfe5 100644
--- a/clippy_lints/src/utils/internal_lints.rs
+++ b/clippy_lints/src/utils/internal_lints.rs
@@ -4,8 +4,8 @@ use clippy_utils::macros::root_macro_call_first_node;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::match_type;
 use clippy_utils::{
-    higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path, method_calls,
-    path_to_res, paths, peel_blocks_with_stmt, SpanlessEq,
+    def_path_res, higher, is_else_clause, is_expn_of, is_expr_path_def_path, is_lint_allowed, match_def_path,
+    method_calls, paths, peel_blocks_with_stmt, SpanlessEq,
 };
 use if_chain::if_chain;
 use rustc_ast as ast;
@@ -844,7 +844,7 @@ impl<'tcx> LateLintPass<'tcx> for MatchTypeOnDiagItem {
             // Extract the path to the matched type
             if let Some(segments) = path_to_matched_type(cx, ty_path);
             let segments: Vec<&str> = segments.iter().map(Symbol::as_str).collect();
-            if let Some(ty_did) = path_to_res(cx, &segments[..]).opt_def_id();
+            if let Some(ty_did) = def_path_res(cx, &segments[..]).opt_def_id();
             // Check if the matched type is a diagnostic item
             if let Some(item_name) = cx.tcx.get_diagnostic_name(ty_did);
             then {
@@ -917,7 +917,7 @@ fn path_to_matched_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> Option<Ve
 // 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 path_to_res(cx, path) != Res::Err {
+    if def_path_res(cx, path) != Res::Err {
         return true;
     }
 
@@ -999,7 +999,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
         }
 
         for &module in &[&paths::KW_MODULE, &paths::SYM_MODULE] {
-            if let Some(def_id) = path_to_res(cx, module).opt_def_id() {
+            if let Some(def_id) = def_path_res(cx, module).opt_def_id() {
                 for item in cx.tcx.module_children(def_id).iter() {
                     if_chain! {
                         if let Res::Def(DefKind::Const, item_def_id) = item.res;
diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs
index ed573ad9056..a2b10c12eb9 100644
--- a/clippy_utils/src/hir_utils.rs
+++ b/clippy_utils/src/hir_utils.rs
@@ -1,5 +1,4 @@
 use crate::consts::{constant_context, constant_simple};
-use crate::differing_macro_contexts;
 use crate::source::snippet_opt;
 use rustc_ast::ast::InlineAsmTemplatePiece;
 use rustc_data_structures::fx::FxHasher;
@@ -186,7 +185,7 @@ impl HirEqInterExpr<'_, '_, '_> {
 
     #[allow(clippy::similar_names)]
     pub fn eq_expr(&mut self, left: &Expr<'_>, right: &Expr<'_>) -> bool {
-        if !self.inner.allow_side_effects && differing_macro_contexts(left.span, right.span) {
+        if !self.inner.allow_side_effects && left.span.ctxt() != right.span.ctxt() {
             return false;
         }
 
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index c1ef0031e1f..f76004774ad 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -77,9 +77,9 @@ use rustc_hir::itemlikevisit::ItemLikeVisitor;
 use rustc_hir::LangItem::{OptionNone, ResultErr, ResultOk};
 use rustc_hir::{
     def, lang_items, Arm, ArrayLen, BindingAnnotation, Block, BlockCheckMode, Body, Constness, Destination, Expr,
-    ExprKind, FnDecl, ForeignItem, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem,
-    Local, MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind,
-    Target, TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
+    ExprKind, FnDecl, ForeignItem, HirId, Impl, ImplItem, ImplItemKind, IsAsync, Item, ItemKind, LangItem, Local,
+    MatchSource, Mutability, Node, Param, Pat, PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, Target,
+    TraitItem, TraitItemKind, TraitRef, TyKind, UnOp,
 };
 use rustc_lint::{LateContext, Level, Lint, LintContext};
 use rustc_middle::hir::place::PlaceBase;
@@ -134,13 +134,6 @@ macro_rules! extract_msrv_attr {
     };
 }
 
-/// Returns `true` if the two spans come from differing expansions (i.e., one is
-/// from a macro and one isn't).
-#[must_use]
-pub fn differing_macro_contexts(lhs: Span, rhs: Span) -> bool {
-    rhs.ctxt() != lhs.ctxt()
-}
-
 /// If the given expression is a local binding, find the initializer expression.
 /// If that initializer expression is another local binding, find its initializer again.
 /// This process repeats as long as possible (but usually no more than once). Initializer
@@ -262,44 +255,6 @@ pub fn is_wild(pat: &Pat<'_>) -> bool {
     matches!(pat.kind, PatKind::Wild)
 }
 
-/// Checks if the first type parameter is a lang item.
-pub fn is_ty_param_lang_item<'tcx>(
-    cx: &LateContext<'_>,
-    qpath: &QPath<'tcx>,
-    item: LangItem,
-) -> Option<&'tcx hir::Ty<'tcx>> {
-    let ty = get_qpath_generic_tys(qpath).next()?;
-
-    if let TyKind::Path(qpath) = &ty.kind {
-        cx.qpath_res(qpath, ty.hir_id)
-            .opt_def_id()
-            .map_or(false, |id| {
-                cx.tcx.lang_items().require(item).map_or(false, |lang_id| id == lang_id)
-            })
-            .then(|| ty)
-    } else {
-        None
-    }
-}
-
-/// Checks if the first type parameter is a diagnostic item.
-pub fn is_ty_param_diagnostic_item<'tcx>(
-    cx: &LateContext<'_>,
-    qpath: &QPath<'tcx>,
-    item: Symbol,
-) -> Option<&'tcx hir::Ty<'tcx>> {
-    let ty = get_qpath_generic_tys(qpath).next()?;
-
-    if let TyKind::Path(qpath) = &ty.kind {
-        cx.qpath_res(qpath, ty.hir_id)
-            .opt_def_id()
-            .map_or(false, |id| cx.tcx.is_diagnostic_item(item, id))
-            .then(|| ty)
-    } else {
-        None
-    }
-}
-
 /// 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 {
@@ -360,35 +315,17 @@ pub fn last_path_segment<'tcx>(path: &QPath<'tcx>) -> &'tcx PathSegment<'tcx> {
     }
 }
 
-pub fn get_qpath_generics<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx GenericArgs<'tcx>> {
-    match path {
-        QPath::Resolved(_, p) => p.segments.last().and_then(|s| s.args),
-        QPath::TypeRelative(_, s) => s.args,
-        QPath::LangItem(..) => None,
-    }
-}
-
-pub fn get_qpath_generic_tys<'tcx>(path: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
-    get_qpath_generics(path)
-        .map_or([].as_ref(), |a| a.args)
+pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tcx hir::Ty<'tcx>> {
+    last_path_segment(qpath)
+        .args
+        .map_or(&[][..], |a| a.args)
         .iter()
-        .filter_map(|a| {
-            if let hir::GenericArg::Type(ty) = a {
-                Some(ty)
-            } else {
-                None
-            }
+        .filter_map(|a| match a {
+            hir::GenericArg::Type(ty) => Some(ty),
+            _ => None,
         })
 }
 
-pub fn single_segment_path<'tcx>(path: &QPath<'tcx>) -> Option<&'tcx PathSegment<'tcx>> {
-    match *path {
-        QPath::Resolved(_, path) => path.segments.get(0),
-        QPath::TypeRelative(_, seg) => Some(seg),
-        QPath::LangItem(..) => None,
-    }
-}
-
 /// 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()`.
@@ -420,37 +357,17 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
     }
 }
 
-/// If the expression is a path, resolve it. Otherwise, return `Res::Err`.
-pub fn expr_path_res(cx: &LateContext<'_>, expr: &Expr<'_>) -> Res {
-    if let ExprKind::Path(p) = &expr.kind {
-        cx.qpath_res(p, expr.hir_id)
-    } else {
-        Res::Err
-    }
-}
-
-/// Resolves the path to a `DefId` and checks if it matches the given path.
-pub fn is_qpath_def_path(cx: &LateContext<'_>, path: &QPath<'_>, hir_id: HirId, segments: &[&str]) -> bool {
-    cx.qpath_res(path, hir_id)
-        .opt_def_id()
-        .map_or(false, |id| match_def_path(cx, id, segments))
-}
-
 /// If the expression is a path, resolves it to a `DefId` and checks if it matches the given path.
 ///
 /// Please use `is_expr_diagnostic_item` if the target is a diagnostic item.
 pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
-    expr_path_res(cx, expr)
-        .opt_def_id()
-        .map_or(false, |id| match_def_path(cx, id, segments))
+    path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments))
 }
 
 /// If the expression is a path, resolves it to a `DefId` and checks if it matches the given
 /// diagnostic item.
 pub fn is_expr_diagnostic_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
-    expr_path_res(cx, expr)
-        .opt_def_id()
-        .map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
+    path_def_id(cx, expr).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
 }
 
 /// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
@@ -497,8 +414,46 @@ pub fn path_to_local_id(expr: &Expr<'_>, id: HirId) -> bool {
     path_to_local(expr) == Some(id)
 }
 
-/// Gets the definition associated to a path.
-pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
+pub trait MaybePath<'hir> {
+    fn hir_id(&self) -> HirId;
+    fn qpath_opt(&self) -> Option<&QPath<'hir>>;
+}
+
+macro_rules! maybe_path {
+    ($ty:ident, $kind:ident) => {
+        impl<'hir> MaybePath<'hir> for hir::$ty<'hir> {
+            fn hir_id(&self) -> HirId {
+                self.hir_id
+            }
+            fn qpath_opt(&self) -> Option<&QPath<'hir>> {
+                match &self.kind {
+                    hir::$kind::Path(qpath) => Some(qpath),
+                    _ => None,
+                }
+            }
+        }
+    };
+}
+maybe_path!(Expr, ExprKind);
+maybe_path!(Pat, PatKind);
+maybe_path!(Ty, TyKind);
+
+/// If `maybe_path` is a path node, resolves it, otherwise returns `Res::Err`
+pub fn path_res<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Res {
+    match maybe_path.qpath_opt() {
+        None => Res::Err,
+        Some(qpath) => cx.qpath_res(qpath, maybe_path.hir_id()),
+    }
+}
+
+/// If `maybe_path` is a path node which resolves to an item, retrieves the item ID
+pub fn path_def_id<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>) -> Option<DefId> {
+    path_res(cx, maybe_path).opt_def_id()
+}
+
+/// Resolves a def path like `std::vec::Vec`.
+/// This function is expensive and should be used sparingly.
+pub fn def_path_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
     macro_rules! try_res {
         ($e:expr) => {
             match $e {
@@ -574,7 +529,7 @@ pub fn path_to_res(cx: &LateContext<'_>, path: &[&str]) -> Res {
 /// Convenience function to get the `DefId` of a trait by path.
 /// It could be a trait or trait alias.
 pub fn get_trait_def_id(cx: &LateContext<'_>, path: &[&str]) -> Option<DefId> {
-    match path_to_res(cx, path) {
+    match def_path_res(cx, path) {
         Res::Def(DefKind::Trait | DefKind::TraitAlias, trait_id) => Some(trait_id),
         _ => None,
     }
@@ -1649,7 +1604,7 @@ pub fn match_function_call<'tcx>(
 /// Checks if the given `DefId` matches any of the paths. Returns the index of matching path, if
 /// any.
 ///
-/// Please use `match_any_diagnostic_items` if the targets are all diagnostic items.
+/// 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
@@ -1657,14 +1612,6 @@ pub fn match_any_def_paths(cx: &LateContext<'_>, did: DefId, paths: &[&[&str]])
         .position(|p| p.iter().map(|x| Symbol::intern(x)).eq(search_path.iter().copied()))
 }
 
-/// Checks if the given `DefId` matches any of provided diagnostic items. Returns the index of
-/// matching path, if any.
-pub fn match_any_diagnostic_items(cx: &LateContext<'_>, def_id: DefId, diag_items: &[Symbol]) -> Option<usize> {
-    diag_items
-        .iter()
-        .position(|item| cx.tcx.is_diagnostic_item(*item, def_id))
-}
-
 /// Checks if the given `DefId` matches the path.
 pub fn match_def_path<'tcx>(cx: &LateContext<'tcx>, did: DefId, syms: &[&str]) -> bool {
     // We should probably move to Symbols in Clippy as well rather than interning every time.
@@ -1817,8 +1764,7 @@ pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool
 
     match expr.kind {
         ExprKind::Closure(_, _, body_id, _, _) => is_body_identity_function(cx, cx.tcx.hir().body(body_id)),
-        ExprKind::Path(ref path) => is_qpath_def_path(cx, path, expr.hir_id, &paths::CONVERT_IDENTITY),
-        _ => false,
+        _ => path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, &paths::CONVERT_IDENTITY)),
     }
 }
 
diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs
index d057da73302..aa3ea2d23da 100644
--- a/clippy_utils/src/ty.rs
+++ b/clippy_utils/src/ty.rs
@@ -20,7 +20,7 @@ use rustc_trait_selection::infer::InferCtxtExt;
 use rustc_trait_selection::traits::query::normalize::AtExt;
 use std::iter;
 
-use crate::{expr_path_res, match_def_path, must_use_attr};
+use crate::{match_def_path, must_use_attr, path_res};
 
 // Checks if the given type implements copy.
 pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
@@ -443,7 +443,7 @@ impl<'tcx> ExprFnSig<'tcx> {
 
 /// If the expression is function like, get the signature for it.
 pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnSig<'tcx>> {
-    if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = expr_path_res(cx, expr) {
+    if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) {
         Some(ExprFnSig::Sig(cx.tcx.fn_sig(id)))
     } else {
         let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs();
diff --git a/doc/common_tools_writing_lints.md b/doc/common_tools_writing_lints.md
index 6c8a3dc418b..36c454745ba 100644
--- a/doc/common_tools_writing_lints.md
+++ b/doc/common_tools_writing_lints.md
@@ -235,7 +235,11 @@ Use the following functions to deal with macros:
    assert_eq!(in_external_macro(cx.sess(), match_span), true);
    ```
 
-- `differing_macro_contexts()`: returns true if the two given spans are not from the same context
+- `span.ctxt()`: the span's context represents whether it is from expansion, and if so, what expanded it
+
+One thing `SpanContext` is useful for is to check if two spans are in the same context. For example,
+in `a == b`, `a` and `b` have the same context. In a `macro_rules!` with `a == $b`, `$b` is expanded to some
+expression with a different context from `a`.
 
    ```rust
    macro_rules! m {
@@ -252,7 +256,7 @@ Use the following functions to deal with macros:
    // These spans are not from the same context
    // x.is_some() is from inside the macro
    // x.unwrap() is from outside the macro
-   assert_eq!(differing_macro_contexts(x_is_some_span, x_unwrap_span), true);
+   assert_eq!(x_is_some_span.ctxt(), x_unwrap_span.ctxt());
    ```
 
 [TyS]: https://doc.rust-lang.org/nightly/nightly-rustc/rustc_middle/ty/struct.TyS.html