From 760c3fab4f0acff98ee32f05a0726c88e11fe814 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Fri, 26 Jul 2024 11:12:35 +0200
Subject: [PATCH 01/54] Fix case where `doc_markdown` is triggered on words
 ending with "ified"

---
 clippy_lints/src/doc/markdown.rs | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/clippy_lints/src/doc/markdown.rs b/clippy_lints/src/doc/markdown.rs
index 41c0bcd55adca..237badb3f233c 100644
--- a/clippy_lints/src/doc/markdown.rs
+++ b/clippy_lints/src/doc/markdown.rs
@@ -92,6 +92,10 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b
             && matches!(prefix.chars().last(), Some('S' | 'X'))
         {
             prefix
+        } else if let Some(prefix) = s.strip_suffix("ified")
+            && prefix.chars().all(|c| c.is_ascii_uppercase())
+        {
+            prefix
         } else {
             s.strip_suffix('s').unwrap_or(s)
         };

From 88506a9147172ff233f820451982e9514aee3476 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Fri, 26 Jul 2024 11:12:59 +0200
Subject: [PATCH 02/54] Add regression test for #13097

---
 tests/ui/doc/doc_markdown-issue_13097.fixed  | 13 +++++++++++++
 tests/ui/doc/doc_markdown-issue_13097.rs     | 13 +++++++++++++
 tests/ui/doc/doc_markdown-issue_13097.stderr | 18 ++++++++++++++++++
 3 files changed, 44 insertions(+)
 create mode 100644 tests/ui/doc/doc_markdown-issue_13097.fixed
 create mode 100644 tests/ui/doc/doc_markdown-issue_13097.rs
 create mode 100644 tests/ui/doc/doc_markdown-issue_13097.stderr

diff --git a/tests/ui/doc/doc_markdown-issue_13097.fixed b/tests/ui/doc/doc_markdown-issue_13097.fixed
new file mode 100644
index 0000000000000..fb0f40b34a4b8
--- /dev/null
+++ b/tests/ui/doc/doc_markdown-issue_13097.fixed
@@ -0,0 +1,13 @@
+// This test checks that words starting with capital letters and ending with "ified" don't
+// trigger the lint.
+
+#![deny(clippy::doc_markdown)]
+
+pub enum OutputFormat {
+    /// `HumaNified`
+    //~^ ERROR: item in documentation is missing backticks
+    Plain,
+    // Should not warn!
+    /// JSONified console output
+    Json,
+}
diff --git a/tests/ui/doc/doc_markdown-issue_13097.rs b/tests/ui/doc/doc_markdown-issue_13097.rs
new file mode 100644
index 0000000000000..8c1e1a3cd6c24
--- /dev/null
+++ b/tests/ui/doc/doc_markdown-issue_13097.rs
@@ -0,0 +1,13 @@
+// This test checks that words starting with capital letters and ending with "ified" don't
+// trigger the lint.
+
+#![deny(clippy::doc_markdown)]
+
+pub enum OutputFormat {
+    /// HumaNified
+    //~^ ERROR: item in documentation is missing backticks
+    Plain,
+    // Should not warn!
+    /// JSONified console output
+    Json,
+}
diff --git a/tests/ui/doc/doc_markdown-issue_13097.stderr b/tests/ui/doc/doc_markdown-issue_13097.stderr
new file mode 100644
index 0000000000000..ae68a767ec930
--- /dev/null
+++ b/tests/ui/doc/doc_markdown-issue_13097.stderr
@@ -0,0 +1,18 @@
+error: item in documentation is missing backticks
+  --> tests/ui/doc/doc_markdown-issue_13097.rs:7:9
+   |
+LL |     /// HumaNified
+   |         ^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> tests/ui/doc/doc_markdown-issue_13097.rs:4:9
+   |
+LL | #![deny(clippy::doc_markdown)]
+   |         ^^^^^^^^^^^^^^^^^^^^
+help: try
+   |
+LL |     /// `HumaNified`
+   |         ~~~~~~~~~~~~
+
+error: aborting due to 1 previous error
+

From 855a9d1377a2da3e4566de9adb8b783295a771dd Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Mon, 24 Jun 2024 18:42:23 +0200
Subject: [PATCH 03/54] Add new `too_long_first_doc_paragraph` first paragraph
 lint

---
 CHANGELOG.md                                  |   1 +
 clippy_lints/src/declared_lints.rs            |   1 +
 clippy_lints/src/doc/mod.rs                   | 113 +++++++++++++-----
 .../src/doc/too_long_first_doc_paragraph.rs   |  85 +++++++++++++
 .../ui/too_long_first_doc_paragraph-fix.fixed |   8 ++
 tests/ui/too_long_first_doc_paragraph-fix.rs  |   7 ++
 .../too_long_first_doc_paragraph-fix.stderr   |  19 +++
 tests/ui/too_long_first_doc_paragraph.rs      |  47 ++++++++
 tests/ui/too_long_first_doc_paragraph.stderr  |  22 ++++
 9 files changed, 270 insertions(+), 33 deletions(-)
 create mode 100644 clippy_lints/src/doc/too_long_first_doc_paragraph.rs
 create mode 100644 tests/ui/too_long_first_doc_paragraph-fix.fixed
 create mode 100644 tests/ui/too_long_first_doc_paragraph-fix.rs
 create mode 100644 tests/ui/too_long_first_doc_paragraph-fix.stderr
 create mode 100644 tests/ui/too_long_first_doc_paragraph.rs
 create mode 100644 tests/ui/too_long_first_doc_paragraph.stderr

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 60c03b03d9be3..ca70da5bb5170 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5913,6 +5913,7 @@ Released 2018-09-13
 [`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args
 [`to_string_trait_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_trait_impl
 [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
+[`too_long_first_doc_paragraph`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_long_first_doc_paragraph
 [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
 [`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
 [`toplevel_ref_arg`]: https://rust-lang.github.io/rust-clippy/master/index.html#toplevel_ref_arg
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index 69f9eb6842bcd..2933a65a71f58 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -148,6 +148,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::doc::NEEDLESS_DOCTEST_MAIN_INFO,
     crate::doc::SUSPICIOUS_DOC_COMMENTS_INFO,
     crate::doc::TEST_ATTR_IN_DOCTEST_INFO,
+    crate::doc::TOO_LONG_FIRST_DOC_PARAGRAPH_INFO,
     crate::doc::UNNECESSARY_SAFETY_DOC_INFO,
     crate::double_parens::DOUBLE_PARENS_INFO,
     crate::drop_forget_ref::DROP_NON_DROP_INFO,
diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs
index 5b6a5b08aa94c..5e9fb0162bf80 100644
--- a/clippy_lints/src/doc/mod.rs
+++ b/clippy_lints/src/doc/mod.rs
@@ -1,4 +1,6 @@
 mod lazy_continuation;
+mod too_long_first_doc_paragraph;
+
 use clippy_config::Conf;
 use clippy_utils::attrs::is_doc_hidden;
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
@@ -422,6 +424,38 @@ declare_clippy_lint! {
     "require every line of a paragraph to be indented and marked"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks if the first line in the documentation of items listed in module page is too long.
+    ///
+    /// ### Why is this bad?
+    /// Documentation will show the first paragraph of the doscstring in the summary page of a
+    /// module, so having a nice, short summary in the first paragraph is part of writing good docs.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// /// A very short summary.
+    /// /// A much longer explanation that goes into a lot more detail about
+    /// /// how the thing works, possibly with doclinks and so one,
+    /// /// and probably spanning a many rows.
+    /// struct Foo {}
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// /// A very short summary.
+    /// ///
+    /// /// A much longer explanation that goes into a lot more detail about
+    /// /// how the thing works, possibly with doclinks and so one,
+    /// /// and probably spanning a many rows.
+    /// struct Foo {}
+    /// ```
+    #[clippy::version = "1.81.0"]
+    pub TOO_LONG_FIRST_DOC_PARAGRAPH,
+    style,
+    "ensure that the first line of a documentation paragraph isn't too long"
+}
+
+#[derive(Clone)]
 pub struct Documentation {
     valid_idents: &'static FxHashSet<String>,
     check_private_items: bool,
@@ -448,6 +482,7 @@ impl_lint_pass!(Documentation => [
     SUSPICIOUS_DOC_COMMENTS,
     EMPTY_DOCS,
     DOC_LAZY_CONTINUATION,
+    TOO_LONG_FIRST_DOC_PARAGRAPH,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Documentation {
@@ -457,39 +492,44 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
         };
 
         match cx.tcx.hir_node(cx.last_node_with_lint_attrs) {
-            Node::Item(item) => match item.kind {
-                ItemKind::Fn(sig, _, body_id) => {
-                    if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
-                        let body = cx.tcx.hir().body(body_id);
-
-                        let panic_info = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
-                        missing_headers::check(
+            Node::Item(item) => {
+                too_long_first_doc_paragraph::check(cx, attrs, item.kind, headers.first_paragraph_len);
+                match item.kind {
+                    ItemKind::Fn(sig, _, body_id) => {
+                        if !(is_entrypoint_fn(cx, item.owner_id.to_def_id())
+                            || in_external_macro(cx.tcx.sess, item.span))
+                        {
+                            let body = cx.tcx.hir().body(body_id);
+
+                            let panic_info = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
+                            missing_headers::check(
+                                cx,
+                                item.owner_id,
+                                sig,
+                                headers,
+                                Some(body_id),
+                                panic_info,
+                                self.check_private_items,
+                            );
+                        }
+                    },
+                    ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) {
+                        (false, Safety::Unsafe) => span_lint(
                             cx,
-                            item.owner_id,
-                            sig,
-                            headers,
-                            Some(body_id),
-                            panic_info,
-                            self.check_private_items,
-                        );
-                    }
-                },
-                ItemKind::Trait(_, unsafety, ..) => match (headers.safety, unsafety) {
-                    (false, Safety::Unsafe) => span_lint(
-                        cx,
-                        MISSING_SAFETY_DOC,
-                        cx.tcx.def_span(item.owner_id),
-                        "docs for unsafe trait missing `# Safety` section",
-                    ),
-                    (true, Safety::Safe) => span_lint(
-                        cx,
-                        UNNECESSARY_SAFETY_DOC,
-                        cx.tcx.def_span(item.owner_id),
-                        "docs for safe trait have unnecessary `# Safety` section",
-                    ),
+                            MISSING_SAFETY_DOC,
+                            cx.tcx.def_span(item.owner_id),
+                            "docs for unsafe trait missing `# Safety` section",
+                        ),
+                        (true, Safety::Safe) => span_lint(
+                            cx,
+                            UNNECESSARY_SAFETY_DOC,
+                            cx.tcx.def_span(item.owner_id),
+                            "docs for safe trait have unnecessary `# Safety` section",
+                        ),
+                        _ => (),
+                    },
                     _ => (),
-                },
-                _ => (),
+                }
             },
             Node::TraitItem(trait_item) => {
                 if let TraitItemKind::Fn(sig, ..) = trait_item.kind
@@ -547,6 +587,7 @@ struct DocHeaders {
     safety: bool,
     errors: bool,
     panics: bool,
+    first_paragraph_len: usize,
 }
 
 /// Does some pre-processing on raw, desugared `#[doc]` attributes such as parsing them and
@@ -586,8 +627,9 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
         acc
     });
     doc.pop();
+    let doc = doc.trim();
 
-    if doc.trim().is_empty() {
+    if doc.is_empty() {
         if let Some(span) = span_of_fragments(&fragments) {
             span_lint_and_help(
                 cx,
@@ -611,7 +653,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
         cx,
         valid_idents,
         parser.into_offset_iter(),
-        &doc,
+        doc,
         Fragments {
             fragments: &fragments,
             doc: &doc,
@@ -653,6 +695,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
     let mut paragraph_range = 0..0;
     let mut code_level = 0;
     let mut blockquote_level = 0;
+    let mut is_first_paragraph = true;
 
     let mut containers = Vec::new();
 
@@ -720,6 +763,10 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
                 }
                 ticks_unbalanced = false;
                 paragraph_range = range;
+                if is_first_paragraph {
+                    headers.first_paragraph_len = doc[paragraph_range.clone()].chars().count();
+                    is_first_paragraph = false;
+                }
             },
             End(TagEnd::Heading(_) | TagEnd::Paragraph | TagEnd::Item) => {
                 if let End(TagEnd::Heading(_)) = event {
diff --git a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
new file mode 100644
index 0000000000000..a5a58b444011a
--- /dev/null
+++ b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
@@ -0,0 +1,85 @@
+use rustc_ast::ast::Attribute;
+use rustc_errors::Applicability;
+use rustc_hir::ItemKind;
+use rustc_lint::LateContext;
+
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
+use clippy_utils::source::snippet_opt;
+
+use super::TOO_LONG_FIRST_DOC_PARAGRAPH;
+
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    attrs: &[Attribute],
+    item_kind: ItemKind<'_>,
+    mut first_paragraph_len: usize,
+) {
+    if first_paragraph_len <= 100
+        || !matches!(
+            item_kind,
+            ItemKind::Static(..)
+                | ItemKind::Const(..)
+                | ItemKind::Fn(..)
+                | ItemKind::Macro(..)
+                | ItemKind::Mod(..)
+                | ItemKind::TyAlias(..)
+                | ItemKind::Enum(..)
+                | ItemKind::Struct(..)
+                | ItemKind::Union(..)
+                | ItemKind::Trait(..)
+                | ItemKind::TraitAlias(..)
+        )
+    {
+        return;
+    }
+    let mut spans = Vec::new();
+    let mut should_suggest_empty_doc = false;
+
+    for attr in attrs {
+        if let Some(doc) = attr.doc_str() {
+            spans.push(attr.span);
+            let doc = doc.as_str();
+            let doc = doc.trim();
+            if spans.len() == 1 {
+                // We make this suggestion only if the first doc line ends with a punctuation
+                // because if might just need to add an empty line with `///`.
+                should_suggest_empty_doc = doc.ends_with('.') || doc.ends_with('!') || doc.ends_with('?');
+            }
+            let len = doc.chars().count();
+            if len >= first_paragraph_len {
+                break;
+            }
+            first_paragraph_len -= len;
+        }
+    }
+
+    let &[first_span, .., last_span] = spans.as_slice() else { return };
+
+    if should_suggest_empty_doc
+        && let Some(second_span) = spans.get(1)
+        && let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi())
+        && let Some(snippet) = snippet_opt(cx, new_span)
+    {
+        span_lint_and_then(
+            cx,
+            TOO_LONG_FIRST_DOC_PARAGRAPH,
+            first_span.with_hi(last_span.lo()),
+            "first doc comment paragraph is too long",
+            |diag| {
+                diag.span_suggestion(
+                    new_span,
+                    "add",
+                    format!("{snippet}///\n"),
+                    Applicability::MachineApplicable,
+                );
+            },
+        );
+        return;
+    }
+    span_lint(
+        cx,
+        TOO_LONG_FIRST_DOC_PARAGRAPH,
+        first_span.with_hi(last_span.lo()),
+        "first doc comment paragraph is too long",
+    );
+}
diff --git a/tests/ui/too_long_first_doc_paragraph-fix.fixed b/tests/ui/too_long_first_doc_paragraph-fix.fixed
new file mode 100644
index 0000000000000..b3f66f5279384
--- /dev/null
+++ b/tests/ui/too_long_first_doc_paragraph-fix.fixed
@@ -0,0 +1,8 @@
+#![warn(clippy::too_long_first_doc_paragraph)]
+
+/// A very short summary.
+///
+/// A much longer explanation that goes into a lot more detail about
+/// how the thing works, possibly with doclinks and so one,
+/// and probably spanning a many rows.
+struct Foo;
diff --git a/tests/ui/too_long_first_doc_paragraph-fix.rs b/tests/ui/too_long_first_doc_paragraph-fix.rs
new file mode 100644
index 0000000000000..f0ece9523de4b
--- /dev/null
+++ b/tests/ui/too_long_first_doc_paragraph-fix.rs
@@ -0,0 +1,7 @@
+#![warn(clippy::too_long_first_doc_paragraph)]
+
+/// A very short summary.
+/// A much longer explanation that goes into a lot more detail about
+/// how the thing works, possibly with doclinks and so one,
+/// and probably spanning a many rows.
+struct Foo;
diff --git a/tests/ui/too_long_first_doc_paragraph-fix.stderr b/tests/ui/too_long_first_doc_paragraph-fix.stderr
new file mode 100644
index 0000000000000..00949f405d538
--- /dev/null
+++ b/tests/ui/too_long_first_doc_paragraph-fix.stderr
@@ -0,0 +1,19 @@
+error: first doc comment paragraph is too long
+  --> tests/ui/too_long_first_doc_paragraph-fix.rs:3:1
+   |
+LL | / /// A very short summary.
+LL | | /// A much longer explanation that goes into a lot more detail about
+LL | | /// how the thing works, possibly with doclinks and so one,
+LL | | /// and probably spanning a many rows.
+   | |_
+   |
+   = note: `-D clippy::too-long-first-doc-paragraph` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::too_long_first_doc_paragraph)]`
+help: add an empty line
+   |
+LL ~ /// A very short summary.
+LL + ///
+   |
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/too_long_first_doc_paragraph.rs b/tests/ui/too_long_first_doc_paragraph.rs
new file mode 100644
index 0000000000000..88a8f6d38310d
--- /dev/null
+++ b/tests/ui/too_long_first_doc_paragraph.rs
@@ -0,0 +1,47 @@
+//@no-rustfix
+
+#![warn(clippy::too_long_first_doc_paragraph)]
+
+/// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
+/// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
+/// gravida non lacinia at, rhoncus eu lacus.
+pub struct Bar;
+
+// Should not warn! (not an item visible on mod page)
+/// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
+/// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
+/// gravida non lacinia at, rhoncus eu lacus.
+impl Bar {}
+
+// Should not warn! (less than 80 characters)
+/// Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+///
+/// Nunc turpis nunc, lacinia
+/// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
+/// gravida non lacinia at, rhoncus eu lacus.
+enum Enum {
+    A,
+}
+
+/// Lorem
+/// ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
+/// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
+/// gravida non lacinia at, rhoncus eu lacus.
+union Union {
+    a: u8,
+    b: u8,
+}
+
+// Should not warn! (title)
+/// # bla
+/// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
+/// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
+/// gravida non lacinia at, rhoncus eu lacus.
+union Union2 {
+    a: u8,
+    b: u8,
+}
+
+fn main() {
+    // test code goes here
+}
diff --git a/tests/ui/too_long_first_doc_paragraph.stderr b/tests/ui/too_long_first_doc_paragraph.stderr
new file mode 100644
index 0000000000000..7f48e5cf884e6
--- /dev/null
+++ b/tests/ui/too_long_first_doc_paragraph.stderr
@@ -0,0 +1,22 @@
+error: first doc comment paragraph is too long
+  --> tests/ui/too_long_first_doc_paragraph.rs:5:1
+   |
+LL | / /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
+LL | | /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
+LL | | /// gravida non lacinia at, rhoncus eu lacus.
+   | |_
+   |
+   = note: `-D clippy::too-long-first-doc-paragraph` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::too_long_first_doc_paragraph)]`
+
+error: first doc comment paragraph is too long
+  --> tests/ui/too_long_first_doc_paragraph.rs:26:1
+   |
+LL | / /// Lorem
+LL | | /// ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
+LL | | /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
+LL | | /// gravida non lacinia at, rhoncus eu lacus.
+   | |_
+
+error: aborting due to 2 previous errors
+

From f455587feec19b459c5eb75c9dc1995b93bb24f6 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Tue, 9 Jul 2024 16:00:06 +0200
Subject: [PATCH 04/54] Set the limit of characters to 200 and don't run the
 lint on private items unless config allows it

---
 clippy_lints/src/doc/mod.rs                     | 13 +++++++++----
 .../src/doc/too_long_first_doc_paragraph.rs     | 17 +++++++++++------
 tests/ui/too_long_first_doc_paragraph-fix.fixed |  5 +++--
 tests/ui/too_long_first_doc_paragraph-fix.rs    |  5 +++--
 .../ui/too_long_first_doc_paragraph-fix.stderr  |  3 ++-
 tests/ui/too_long_first_doc_paragraph.rs        | 12 +++++++++---
 6 files changed, 37 insertions(+), 18 deletions(-)

diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs
index 5e9fb0162bf80..6debcd9b6a0f1 100644
--- a/clippy_lints/src/doc/mod.rs
+++ b/clippy_lints/src/doc/mod.rs
@@ -493,7 +493,13 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
 
         match cx.tcx.hir_node(cx.last_node_with_lint_attrs) {
             Node::Item(item) => {
-                too_long_first_doc_paragraph::check(cx, attrs, item.kind, headers.first_paragraph_len);
+                too_long_first_doc_paragraph::check(
+                    cx,
+                    item,
+                    attrs,
+                    headers.first_paragraph_len,
+                    self.check_private_items,
+                );
                 match item.kind {
                     ItemKind::Fn(sig, _, body_id) => {
                         if !(is_entrypoint_fn(cx, item.owner_id.to_def_id())
@@ -627,9 +633,8 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
         acc
     });
     doc.pop();
-    let doc = doc.trim();
 
-    if doc.is_empty() {
+    if doc.trim().is_empty() {
         if let Some(span) = span_of_fragments(&fragments) {
             span_lint_and_help(
                 cx,
@@ -653,7 +658,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
         cx,
         valid_idents,
         parser.into_offset_iter(),
-        doc,
+        &doc,
         Fragments {
             fragments: &fragments,
             doc: &doc,
diff --git a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
index a5a58b444011a..a1b714b7d0020 100644
--- a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
+++ b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
@@ -1,6 +1,6 @@
 use rustc_ast::ast::Attribute;
 use rustc_errors::Applicability;
-use rustc_hir::ItemKind;
+use rustc_hir::{Item, ItemKind};
 use rustc_lint::LateContext;
 
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
@@ -10,13 +10,17 @@ use super::TOO_LONG_FIRST_DOC_PARAGRAPH;
 
 pub(super) fn check(
     cx: &LateContext<'_>,
+    item: &Item<'_>,
     attrs: &[Attribute],
-    item_kind: ItemKind<'_>,
     mut first_paragraph_len: usize,
+    check_private_items: bool,
 ) {
-    if first_paragraph_len <= 100
+    if !check_private_items && !cx.effective_visibilities.is_exported(item.owner_id.def_id) {
+        return;
+    }
+    if first_paragraph_len <= 200
         || !matches!(
-            item_kind,
+            item.kind,
             ItemKind::Static(..)
                 | ItemKind::Const(..)
                 | ItemKind::Fn(..)
@@ -32,6 +36,7 @@ pub(super) fn check(
     {
         return;
     }
+
     let mut spans = Vec::new();
     let mut should_suggest_empty_doc = false;
 
@@ -42,7 +47,7 @@ pub(super) fn check(
             let doc = doc.trim();
             if spans.len() == 1 {
                 // We make this suggestion only if the first doc line ends with a punctuation
-                // because if might just need to add an empty line with `///`.
+                // because it might just need to add an empty line with `///`.
                 should_suggest_empty_doc = doc.ends_with('.') || doc.ends_with('!') || doc.ends_with('?');
             }
             let len = doc.chars().count();
@@ -68,7 +73,7 @@ pub(super) fn check(
             |diag| {
                 diag.span_suggestion(
                     new_span,
-                    "add",
+                    "add an empty line",
                     format!("{snippet}///\n"),
                     Applicability::MachineApplicable,
                 );
diff --git a/tests/ui/too_long_first_doc_paragraph-fix.fixed b/tests/ui/too_long_first_doc_paragraph-fix.fixed
index b3f66f5279384..d4a0cdf3447f1 100644
--- a/tests/ui/too_long_first_doc_paragraph-fix.fixed
+++ b/tests/ui/too_long_first_doc_paragraph-fix.fixed
@@ -4,5 +4,6 @@
 ///
 /// A much longer explanation that goes into a lot more detail about
 /// how the thing works, possibly with doclinks and so one,
-/// and probably spanning a many rows.
-struct Foo;
+/// and probably spanning a many rows. Blablabla, it needs to be over
+/// 200 characters so I needed to write something longeeeeeeer.
+pub struct Foo;
diff --git a/tests/ui/too_long_first_doc_paragraph-fix.rs b/tests/ui/too_long_first_doc_paragraph-fix.rs
index f0ece9523de4b..5a3b6c42a328b 100644
--- a/tests/ui/too_long_first_doc_paragraph-fix.rs
+++ b/tests/ui/too_long_first_doc_paragraph-fix.rs
@@ -3,5 +3,6 @@
 /// A very short summary.
 /// A much longer explanation that goes into a lot more detail about
 /// how the thing works, possibly with doclinks and so one,
-/// and probably spanning a many rows.
-struct Foo;
+/// and probably spanning a many rows. Blablabla, it needs to be over
+/// 200 characters so I needed to write something longeeeeeeer.
+pub struct Foo;
diff --git a/tests/ui/too_long_first_doc_paragraph-fix.stderr b/tests/ui/too_long_first_doc_paragraph-fix.stderr
index 00949f405d538..6403265a39c54 100644
--- a/tests/ui/too_long_first_doc_paragraph-fix.stderr
+++ b/tests/ui/too_long_first_doc_paragraph-fix.stderr
@@ -4,7 +4,8 @@ error: first doc comment paragraph is too long
 LL | / /// A very short summary.
 LL | | /// A much longer explanation that goes into a lot more detail about
 LL | | /// how the thing works, possibly with doclinks and so one,
-LL | | /// and probably spanning a many rows.
+LL | | /// and probably spanning a many rows. Blablabla, it needs to be over
+LL | | /// 200 characters so I needed to write something longeeeeeeer.
    | |_
    |
    = note: `-D clippy::too-long-first-doc-paragraph` implied by `-D warnings`
diff --git a/tests/ui/too_long_first_doc_paragraph.rs b/tests/ui/too_long_first_doc_paragraph.rs
index 88a8f6d38310d..1042249c5b7bd 100644
--- a/tests/ui/too_long_first_doc_paragraph.rs
+++ b/tests/ui/too_long_first_doc_paragraph.rs
@@ -19,7 +19,7 @@ impl Bar {}
 /// Nunc turpis nunc, lacinia
 /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
 /// gravida non lacinia at, rhoncus eu lacus.
-enum Enum {
+pub enum Enum {
     A,
 }
 
@@ -27,7 +27,7 @@ enum Enum {
 /// ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
 /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
 /// gravida non lacinia at, rhoncus eu lacus.
-union Union {
+pub union Union {
     a: u8,
     b: u8,
 }
@@ -37,11 +37,17 @@ union Union {
 /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
 /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
 /// gravida non lacinia at, rhoncus eu lacus.
-union Union2 {
+pub union Union2 {
     a: u8,
     b: u8,
 }
 
+// Should not warn! (not public)
+/// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
+/// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
+/// gravida non lacinia at, rhoncus eu lacus.
+fn f() {}
+
 fn main() {
     // test code goes here
 }

From 4969960a9c0a9ac4e8bbe0083124186eb5159028 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Tue, 9 Jul 2024 16:29:39 +0200
Subject: [PATCH 05/54] Fix dogfood lints

---
 .../src/doc/too_long_first_doc_paragraph.rs   |  4 +++-
 clippy_utils/src/lib.rs                       | 21 ++++++++++++-------
 clippy_utils/src/macros.rs                    |  9 ++++----
 clippy_utils/src/source.rs                    | 14 +++++++------
 clippy_utils/src/ty.rs                        | 20 +++++++++++-------
 5 files changed, 42 insertions(+), 26 deletions(-)

diff --git a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
index a1b714b7d0020..45ec392d55380 100644
--- a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
+++ b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
@@ -58,7 +58,9 @@ pub(super) fn check(
         }
     }
 
-    let &[first_span, .., last_span] = spans.as_slice() else { return };
+    let &[first_span, .., last_span] = spans.as_slice() else {
+        return;
+    };
 
     if should_suggest_empty_doc
         && let Some(second_span) = spans.get(1)
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index 1d5f1a2a2bb13..7d613a50665f4 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -150,6 +150,7 @@ macro_rules! extract_msrv_attr {
 
 /// 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
 /// expressions with adjustments are ignored. If this is not desired, use [`find_binding_init`]
 /// instead.
@@ -180,6 +181,7 @@ pub fn expr_or_init<'a, 'b, 'tcx: 'b>(cx: &LateContext<'tcx>, mut expr: &'a Expr
 }
 
 /// Finds the initializer expression for a local binding. Returns `None` if the binding is mutable.
+///
 /// By only considering immutable bindings, we guarantee that the returned expression represents the
 /// value of the binding wherever it is referenced.
 ///
@@ -428,12 +430,12 @@ pub fn qpath_generic_tys<'tcx>(qpath: &QPath<'tcx>) -> impl Iterator<Item = &'tc
         })
 }
 
-/// THIS METHOD IS DEPRECATED and will eventually be removed since it does not match against the
+/// 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()`.
 ///
-/// Matches a `QPath` against a slice of segment string literals.
-///
 /// There is also `match_path` if you are dealing with a `rustc_hir::Path` instead of a
 /// `rustc_hir::QPath`.
 ///
@@ -482,12 +484,12 @@ pub fn is_path_diagnostic_item<'tcx>(
     path_def_id(cx, maybe_path).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
+/// 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()`.
 ///
-/// Matches a `Path` against a slice of segment string literals.
-///
 /// There is also `match_qpath` if you are dealing with a `rustc_hir::QPath` instead of a
 /// `rustc_hir::Path`.
 ///
@@ -903,6 +905,7 @@ pub fn is_default_equivalent_call(cx: &LateContext<'_>, repl_func: &Expr<'_>) ->
 }
 
 /// Returns true if the expr is equal to `Default::default()` of it's type when evaluated.
+///
 /// It doesn't cover all cases, for example indirect function calls (some of std
 /// functions are supported) but it is the best we have.
 pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
@@ -1059,6 +1062,7 @@ impl std::ops::BitOrAssign for CaptureKind {
 }
 
 /// Given an expression referencing a local, determines how it would be captured in a closure.
+///
 /// Note as this will walk up to parent expressions until the capture can be determined it should
 /// only be used while making a closure somewhere a value is consumed. e.g. a block, match arm, or
 /// function argument (other than a receiver).
@@ -2361,8 +2365,9 @@ pub fn fn_def_id_with_node_args<'tcx>(
 }
 
 /// Returns `Option<String>` where String is a textual representation of the type encapsulated in
-/// the slice iff the given expression is a slice of primitives (as defined in the
-/// `is_recursively_primitive_type` function) and `None` otherwise.
+/// the slice iff the given expression is a slice of primitives.
+///
+/// (As defined in the `is_recursively_primitive_type` function.) Returns `None` otherwise.
 pub fn is_slice_of_primitives(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
     let expr_type = cx.typeck_results().expr_ty_adjusted(expr);
     let expr_kind = expr_type.kind();
diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs
index 455239cc37f35..3c85eb1296e1f 100644
--- a/clippy_utils/src/macros.rs
+++ b/clippy_utils/src/macros.rs
@@ -150,10 +150,11 @@ pub fn first_node_macro_backtrace(cx: &LateContext<'_>, node: &impl HirNode) ->
 }
 
 /// If `node` is the "first node" in a macro expansion, returns `Some` with the `ExpnId` of the
-/// macro call site (i.e. the parent of the macro expansion). This generally means that `node`
-/// is the outermost node of an entire macro expansion, but there are some caveats noted below.
-/// This is useful for finding macro calls while visiting the HIR without processing the macro call
-/// at every node within its expansion.
+/// macro call site (i.e. the parent of the macro expansion).
+///
+/// This generally means that `node` is the outermost node of an entire macro expansion, but there
+/// are some caveats noted below. This is useful for finding macro calls while visiting the HIR
+/// without processing the macro call at every node within its expansion.
 ///
 /// If you already have immediate access to the parent node, it is simpler to
 /// just check the context of that span directly (e.g. `parent.span.from_expansion()`).
diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs
index 496c8f5b55373..924cdf1b8735c 100644
--- a/clippy_utils/src/source.rs
+++ b/clippy_utils/src/source.rs
@@ -527,9 +527,10 @@ pub fn snippet_block_with_context<'a>(
     (reindent_multiline(snip, true, indent), from_macro)
 }
 
-/// Same as `snippet_with_applicability`, but first walks the span up to the given context. This
-/// will result in the macro call, rather than the expansion, if the span is from a child context.
-/// If the span is not from a child context, it will be used directly instead.
+/// Same as `snippet_with_applicability`, but first walks the span up to the given context.
+///
+/// This will result in the macro call, rather than the expansion, if the span is from a child
+/// context. If the span is not from a child context, it will be used directly instead.
 ///
 /// e.g. Given the expression `&vec![]`, getting a snippet from the span for `vec![]` as a HIR node
 /// would result in `box []`. If given the context of the address of expression, this function will
@@ -572,9 +573,10 @@ fn snippet_with_context_sess<'a>(
 }
 
 /// Walks the span up to the target context, thereby returning the macro call site if the span is
-/// inside a macro expansion, or the original span if it is not. Note this will return `None` in the
-/// case of the span being in a macro expansion, but the target context is from expanding a macro
-/// argument.
+/// inside a macro expansion, or the original span if it is not.
+///
+/// Note this will return `None` in the case of the span being in a macro expansion, but the target
+/// context is from expanding a macro argument.
 ///
 /// Given the following
 ///
diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs
index 812fb647fdab6..de5ae484834cc 100644
--- a/clippy_utils/src/ty.rs
+++ b/clippy_utils/src/ty.rs
@@ -160,8 +160,10 @@ pub fn get_type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symb
 }
 
 /// Returns true if `ty` is a type on which calling `Clone` through a function instead of
-/// as a method, such as `Arc::clone()` is considered idiomatic. Lints should avoid suggesting to
-/// replace instances of `ty::Clone()` by `.clone()` for objects of those types.
+/// as a method, such as `Arc::clone()` is considered idiomatic.
+///
+/// Lints should avoid suggesting to replace instances of `ty::Clone()` by `.clone()` for objects
+/// of those types.
 pub fn should_call_clone_as_function(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
     matches!(
         get_type_diagnostic_name(cx, ty),
@@ -398,8 +400,10 @@ fn is_normalizable_helper<'tcx>(
 }
 
 /// Returns `true` if the given type is a non aggregate primitive (a `bool` or `char`, any
-/// integer or floating-point number type). For checking aggregation of primitive types (e.g.
-/// tuples and slices of primitive type) see `is_recursively_primitive_type`
+/// integer or floating-point number type).
+///
+/// For checking aggregation of primitive types (e.g. tuples and slices of primitive type) see
+/// `is_recursively_primitive_type`
 pub fn is_non_aggregate_primitive_type(ty: Ty<'_>) -> bool {
     matches!(ty.kind(), ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_))
 }
@@ -476,9 +480,10 @@ pub fn match_type(cx: &LateContext<'_>, ty: Ty<'_>, path: &[&str]) -> bool {
     }
 }
 
-/// Checks if the drop order for a type matters. Some std types implement drop solely to
-/// deallocate memory. For these types, and composites containing them, changing the drop order
-/// won't result in any observable side effects.
+/// Checks if the drop order for a type matters.
+///
+/// Some std types implement drop solely to deallocate memory. For these types, and composites
+/// containing them, changing the drop order won't result in any observable side effects.
 pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
     fn needs_ordered_drop_inner<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, seen: &mut FxHashSet<Ty<'tcx>>) -> bool {
         if !seen.insert(ty) {
@@ -1324,6 +1329,7 @@ pub fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl
 }
 
 /// Checks if a Ty<'_> has some inherent method Symbol.
+///
 /// This does not look for impls in the type's `Deref::Target` type.
 /// If you need this, you should wrap this call in `clippy_utils::ty::deref_chain().any(...)`.
 pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<&'a AssocItem> {

From 3c6e5ef4ae20c7439a2bc951ef349c59954412c6 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Sun, 21 Jul 2024 17:29:32 +0200
Subject: [PATCH 06/54] Add comment explaining what the matching items are for
 `TOO_LONG_FIRST_DOC_PARAGRAPH` lint

---
 clippy_lints/src/doc/too_long_first_doc_paragraph.rs | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
index 45ec392d55380..a47cba64b28e5 100644
--- a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
+++ b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
@@ -21,6 +21,8 @@ pub(super) fn check(
     if first_paragraph_len <= 200
         || !matches!(
             item.kind,
+            // This is the list of items which can be documented AND are displayed on the module
+            // page. So associated items or impl blocks are not part of this list.
             ItemKind::Static(..)
                 | ItemKind::Const(..)
                 | ItemKind::Fn(..)

From b7e7975dedb42edf3ba1b53708950db34c31c182 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Tue, 30 Jul 2024 16:01:36 +0200
Subject: [PATCH 07/54] Move style into its file

---
 .github/deploy.sh        |   1 +
 util/gh-pages/index.html | 366 +--------------------------------------
 util/gh-pages/style.css  | 364 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 366 insertions(+), 365 deletions(-)
 create mode 100644 util/gh-pages/style.css

diff --git a/.github/deploy.sh b/.github/deploy.sh
index 5a59f94ec918b..d937661c0f828 100644
--- a/.github/deploy.sh
+++ b/.github/deploy.sh
@@ -10,6 +10,7 @@ mkdir out/master/
 cp util/gh-pages/index.html out/master
 cp util/gh-pages/script.js out/master
 cp util/gh-pages/lints.json out/master
+cp util/gh-pages/style.css out/master
 
 if [[ -n $TAG_NAME ]]; then
   echo "Save the doc for the current tag ($TAG_NAME) and point stable/ to it"
diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html
index 7f271ac838597..267354cc8bfc6 100644
--- a/util/gh-pages/index.html
+++ b/util/gh-pages/index.html
@@ -23,371 +23,7 @@
     <link id="styleHighlight" rel="stylesheet" href="https://rust-lang.github.io/mdBook/highlight.css">
     <link id="styleNight" rel="stylesheet" href="https://rust-lang.github.io/mdBook/tomorrow-night.css" disabled="true">
     <link id="styleAyu" rel="stylesheet" href="https://rust-lang.github.io/mdBook/ayu-highlight.css" disabled="true">
-    <style>
-        blockquote { font-size: 1em; }
-        [ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak { display: none !important; }
-
-        .dropdown-menu {
-            color: var(--fg);
-            background: var(--theme-popup-bg);
-            border: 1px solid var(--theme-popup-border);
-        }
-
-        .dropdown-menu .divider {
-            background-color: var(--theme-popup-border);
-        }
-
-        .dropdown-menu .checkbox {
-            display: block;
-            white-space: nowrap;
-            margin: 0;
-        }
-        .dropdown-menu .checkbox label {
-            padding: 3px 20px;
-            width: 100%;
-        }
-
-        .dropdown-menu .checkbox input {
-            position: relative;
-            margin: 0 0.5rem 0;
-            padding: 0;
-        }
-
-        .dropdown-menu .checkbox:hover {
-            background-color: var(--theme-hover);
-        }
-
-        div.panel div.panel-body button {
-            background: var(--searchbar-bg);
-            color: var(--searchbar-fg);
-            border-color: var(--theme-popup-border);
-        }
-
-        div.panel div.panel-body button:hover {
-            box-shadow: 0 0 3px var(--searchbar-shadow-color);
-        }
-
-        div.panel div.panel-body  button.open {
-            filter: brightness(90%);
-        }
-
-        .dropdown-toggle .badge {
-            background-color: #777;
-        }
-
-        .panel-heading { cursor: pointer; }
-
-        .panel-title { display: flex; flex-wrap: wrap;}
-        .panel-title .label { display: inline-block; }
-
-        .panel-title-name { flex: 1; min-width: 400px;}
-        .panel-title-name span { vertical-align: bottom; }
-
-        .panel .panel-title-name .anchor { display: none; }
-        .panel:hover .panel-title-name .anchor { display: inline;}
-
-        .search-control {
-            margin-top: 15px;
-        }
-
-        @media (min-width: 992px) {
-            .search-control {
-                margin-top: 0;
-            }
-        }
-
-        @media (min-width: 405px) {
-            #upper-filters {
-                display: flex;
-                flex-wrap: wrap;
-            }
-        }
-
-        @media (max-width: 430px) {
-            /* Turn the version filter list to the left */
-            #version-filter-selector {
-                right: 0;
-                left: auto;
-            }
-        }
-
-        @media (max-width: 412px) {
-            #upper-filters,
-            .panel-body .search-control  {
-                padding-right: 8px;
-                padding-left: 8px;
-            }
-        }
-
-        .label {
-            padding-top: 0.3em;
-            padding-bottom: 0.3em;
-        }
-
-        .label-lint-group {
-            min-width: 8em;
-        }
-        .label-lint-level {
-            min-width: 4em;
-        }
-
-        .label-lint-level-allow {
-            background-color: #5cb85c;
-        }
-        .label-lint-level-warn {
-            background-color: #f0ad4e;
-        }
-        .label-lint-level-deny {
-            background-color: #d9534f;
-        }
-        .label-lint-level-none {
-            background-color: #777777;
-            opacity: 0.5;
-        }
-
-        .label-group-deprecated {
-            opacity: 0.5;
-        }
-
-        .label-doc-folding {
-            color: #000;
-            background-color: #fff;
-            border: 1px solid var(--theme-popup-border);
-        }
-        .label-doc-folding:hover {
-            background-color: #e6e6e6;
-        }
-
-        .lint-doc-md > h3 {
-            border-top: 1px solid var(--theme-popup-border);
-            padding: 10px 15px;
-            margin: 0 -15px;
-            font-size: 18px;
-        }
-        .lint-doc-md > h3:first-child {
-            border-top: none;
-            padding-top: 0px;
-        }
-
-        @media (max-width:749px) {
-            .lint-additional-info-container {
-                display: flex;
-                flex-flow: column;
-            }
-            .lint-additional-info-item + .lint-additional-info-item {
-                border-top: 1px solid var(--theme-popup-border);
-            }
-        }
-        @media (min-width:750px) {
-            .lint-additional-info-container {
-                display: flex;
-                flex-flow: row;
-            }
-            .lint-additional-info-item + .lint-additional-info-item {
-                border-left: 1px solid var(--theme-popup-border);
-            }
-        }
-
-        .lint-additional-info-item {
-            display: inline-flex;
-            min-width: 200px;
-            flex-grow: 1;
-            padding: 9px 5px 5px 15px;
-        }
-
-        .label-applicability {
-            background-color: #777777;
-            margin: auto 5px;
-        }
-
-        .label-version {
-            background-color: #777777;
-            margin: auto 5px;
-            font-family: monospace;
-        }
-
-        details {
-            border-radius: 4px;
-            padding: .5em .5em 0;
-        }
-
-        code {
-            white-space: pre !important;
-        }
-
-        summary {
-            font-weight: bold;
-            margin: -.5em -.5em 0;
-            padding: .5em;
-            display: revert;
-        }
-
-        details[open] {
-            padding: .5em;
-        }
-    </style>
-    <style>
-        /* Expanding the mdBoom theme*/
-        .light {
-            --inline-code-bg: #f6f7f6;
-        }
-        .rust {
-            --inline-code-bg: #f6f7f6;
-        }
-        .coal {
-            --inline-code-bg: #1d1f21;
-        }
-        .navy {
-            --inline-code-bg: #1d1f21;
-        }
-        .ayu {
-            --inline-code-bg: #191f26;
-        }
-
-        .theme-dropdown {
-            position: absolute;
-            margin: 0.7em;
-            z-index: 10;
-        }
-
-        /* Applying the mdBook theme */
-        .theme-icon {
-            text-align: center;
-            width: 2em;
-            height: 2em;
-            line-height: 2em;
-            border: solid 1px var(--icons);
-            border-radius: 5px;
-            user-select: none;
-            cursor: pointer;
-        }
-        .theme-icon:hover {
-            background: var(--theme-hover);
-        }
-        .theme-choice {
-            display: none;
-            list-style: none;
-            border: 1px solid var(--theme-popup-border);
-            border-radius: 5px;
-            color: var(--fg);
-            background: var(--theme-popup-bg);
-            padding: 0 0;
-            overflow: hidden;
-        }
-
-        .theme-dropdown.open .theme-choice {
-            display: block;
-        }
-
-        .theme-choice > li {
-            padding: 5px 10px;
-            font-size: 0.8em;
-            user-select: none;
-            cursor: pointer;
-        }
-
-        .theme-choice > li:hover {
-            background: var(--theme-hover);
-        }
-
-        .alert {
-            color: var(--fg);
-            background: var(--theme-hover);
-            border: 1px solid var(--theme-popup-border);
-        }
-        .page-header {
-            border-color: var(--theme-popup-border);
-        }
-        .panel-default > .panel-heading {
-            background: var(--theme-hover);
-            color: var(--fg);
-            border: 1px solid var(--theme-popup-border);
-        }
-        .panel-default > .panel-heading:hover {
-            filter: brightness(90%);
-        }
-        .list-group-item {
-            background: 0%;
-            border: 1px solid var(--theme-popup-border);
-        }
-        .panel, pre, hr {
-            background: var(--bg);
-            border: 1px solid var(--theme-popup-border);
-        }
-
-        #version-filter-selector .checkbox {
-            display: flex;
-        }
-
-        #version-filter {
-            min-width: available;
-        }
-
-        #version-filter li label {
-            padding-right: 0;
-            width: 35%;
-        }
-
-        .version-filter-input {
-            height: 60%;
-            width: 30%;
-            text-align: center;
-            border: none;
-            border-bottom: 1px solid #000000;
-        }
-
-        #filter-label, .filter-clear {
-            background: var(--searchbar-bg);
-            color: var(--searchbar-fg);
-            border-color: var(--theme-popup-border);
-            filter: brightness(95%);
-        }
-        #filter-label:hover, .filter-clear:hover {
-            filter: brightness(90%);
-        }
-        .filter-input {
-            background: var(--searchbar-bg);
-            color: var(--searchbar-fg);
-            border-color: var(--theme-popup-border);
-        }
-
-        .filter-input::-webkit-input-placeholder,
-        .filter-input::-moz-placeholder {
-            color: var(--searchbar-fg);
-            opacity: 30%;
-        }
-
-        .expansion-group {
-            margin-top: 15px;
-            padding: 0px 8px;
-            display: flex;
-            flex-wrap: nowrap;
-        }
-
-        @media (min-width: 992px) {
-            .expansion-group {
-                margin-top: 0;
-                padding: 0px 15px;
-            }
-        }
-
-        .expansion-control {
-            width: 50%;
-        }
-
-        :not(pre) > code {
-            color: var(--inline-code-color);
-            background-color: var(--inline-code-bg);
-        }
-        html {
-            scrollbar-color: var(--scrollbar) var(--bg);
-        }
-        body {
-            background: var(--bg);
-            color: var(--fg);
-        }
-
-    </style>
+    <link rel="stylesheet" href="style.css">
 </head>
 <body ng-app="clippy" ng-controller="lintList">
     <div theme-dropdown class="theme-dropdown">
diff --git a/util/gh-pages/style.css b/util/gh-pages/style.css
new file mode 100644
index 0000000000000..f9feb0ba13ae3
--- /dev/null
+++ b/util/gh-pages/style.css
@@ -0,0 +1,364 @@
+blockquote { font-size: 1em; }
+
+[ng\:cloak], [ng-cloak], [data-ng-cloak], [x-ng-cloak], .ng-cloak, .x-ng-cloak {
+    display: none !important;
+}
+
+.dropdown-menu {
+    color: var(--fg);
+    background: var(--theme-popup-bg);
+    border: 1px solid var(--theme-popup-border);
+}
+
+.dropdown-menu .divider {
+    background-color: var(--theme-popup-border);
+}
+
+.dropdown-menu .checkbox {
+    display: block;
+    white-space: nowrap;
+    margin: 0;
+}
+.dropdown-menu .checkbox label {
+    padding: 3px 20px;
+    width: 100%;
+}
+
+.dropdown-menu .checkbox input {
+    position: relative;
+    margin: 0 0.5rem 0;
+    padding: 0;
+}
+
+.dropdown-menu .checkbox:hover {
+    background-color: var(--theme-hover);
+}
+
+div.panel div.panel-body button {
+    background: var(--searchbar-bg);
+    color: var(--searchbar-fg);
+    border-color: var(--theme-popup-border);
+}
+
+div.panel div.panel-body button:hover {
+    box-shadow: 0 0 3px var(--searchbar-shadow-color);
+}
+
+div.panel div.panel-body  button.open {
+    filter: brightness(90%);
+}
+
+.dropdown-toggle .badge {
+    background-color: #777;
+}
+
+.panel-heading { cursor: pointer; }
+
+.panel-title { display: flex; flex-wrap: wrap;}
+.panel-title .label { display: inline-block; }
+
+.panel-title-name { flex: 1; min-width: 400px;}
+.panel-title-name span { vertical-align: bottom; }
+
+.panel .panel-title-name .anchor { display: none; }
+.panel:hover .panel-title-name .anchor { display: inline;}
+
+.search-control {
+    margin-top: 15px;
+}
+
+@media (min-width: 992px) {
+    .search-control {
+        margin-top: 0;
+    }
+}
+
+@media (min-width: 405px) {
+    #upper-filters {
+        display: flex;
+        flex-wrap: wrap;
+    }
+}
+
+@media (max-width: 430px) {
+    /* Turn the version filter list to the left */
+    #version-filter-selector {
+        right: 0;
+        left: auto;
+    }
+}
+
+@media (max-width: 412px) {
+    #upper-filters,
+    .panel-body .search-control  {
+        padding-right: 8px;
+        padding-left: 8px;
+    }
+}
+
+.label {
+    padding-top: 0.3em;
+    padding-bottom: 0.3em;
+}
+
+.label-lint-group {
+    min-width: 8em;
+}
+.label-lint-level {
+    min-width: 4em;
+}
+
+.label-lint-level-allow {
+    background-color: #5cb85c;
+}
+.label-lint-level-warn {
+    background-color: #f0ad4e;
+}
+.label-lint-level-deny {
+    background-color: #d9534f;
+}
+.label-lint-level-none {
+    background-color: #777777;
+    opacity: 0.5;
+}
+
+.label-group-deprecated {
+    opacity: 0.5;
+}
+
+.label-doc-folding {
+    color: #000;
+    background-color: #fff;
+    border: 1px solid var(--theme-popup-border);
+}
+.label-doc-folding:hover {
+    background-color: #e6e6e6;
+}
+
+.lint-doc-md > h3 {
+    border-top: 1px solid var(--theme-popup-border);
+    padding: 10px 15px;
+    margin: 0 -15px;
+    font-size: 18px;
+}
+.lint-doc-md > h3:first-child {
+    border-top: none;
+    padding-top: 0px;
+}
+
+@media (max-width:749px) {
+    .lint-additional-info-container {
+        display: flex;
+        flex-flow: column;
+    }
+    .lint-additional-info-item + .lint-additional-info-item {
+        border-top: 1px solid var(--theme-popup-border);
+    }
+}
+@media (min-width:750px) {
+    .lint-additional-info-container {
+        display: flex;
+        flex-flow: row;
+    }
+    .lint-additional-info-item + .lint-additional-info-item {
+        border-left: 1px solid var(--theme-popup-border);
+    }
+}
+
+.lint-additional-info-item {
+    display: inline-flex;
+    min-width: 200px;
+    flex-grow: 1;
+    padding: 9px 5px 5px 15px;
+}
+
+.label-applicability {
+    background-color: #777777;
+    margin: auto 5px;
+}
+
+.label-version {
+    background-color: #777777;
+    margin: auto 5px;
+    font-family: monospace;
+}
+
+details {
+    border-radius: 4px;
+    padding: .5em .5em 0;
+}
+
+code {
+    white-space: pre !important;
+}
+
+summary {
+    font-weight: bold;
+    margin: -.5em -.5em 0;
+    padding: .5em;
+    display: revert;
+}
+
+details[open] {
+    padding: .5em;
+}
+
+/* Expanding the mdBook theme*/
+.light {
+    --inline-code-bg: #f6f7f6;
+}
+.rust {
+    --inline-code-bg: #f6f7f6;
+}
+.coal {
+    --inline-code-bg: #1d1f21;
+}
+.navy {
+    --inline-code-bg: #1d1f21;
+}
+.ayu {
+    --inline-code-bg: #191f26;
+}
+
+.theme-dropdown {
+    position: absolute;
+    margin: 0.7em;
+    z-index: 10;
+}
+
+/* Applying the mdBook theme */
+.theme-icon {
+    text-align: center;
+    width: 2em;
+    height: 2em;
+    line-height: 2em;
+    border: solid 1px var(--icons);
+    border-radius: 5px;
+    user-select: none;
+    cursor: pointer;
+}
+.theme-icon:hover {
+    background: var(--theme-hover);
+}
+.theme-choice {
+    display: none;
+    list-style: none;
+    border: 1px solid var(--theme-popup-border);
+    border-radius: 5px;
+    color: var(--fg);
+    background: var(--theme-popup-bg);
+    padding: 0 0;
+    overflow: hidden;
+}
+
+.theme-dropdown.open .theme-choice {
+    display: block;
+}
+
+.theme-choice > li {
+    padding: 5px 10px;
+    font-size: 0.8em;
+    user-select: none;
+    cursor: pointer;
+}
+
+.theme-choice > li:hover {
+    background: var(--theme-hover);
+}
+
+.alert {
+    color: var(--fg);
+    background: var(--theme-hover);
+    border: 1px solid var(--theme-popup-border);
+}
+.page-header {
+    border-color: var(--theme-popup-border);
+}
+.panel-default > .panel-heading {
+    background: var(--theme-hover);
+    color: var(--fg);
+    border: 1px solid var(--theme-popup-border);
+}
+.panel-default > .panel-heading:hover {
+    filter: brightness(90%);
+}
+.list-group-item {
+    background: 0%;
+    border: 1px solid var(--theme-popup-border);
+}
+.panel, pre, hr {
+    background: var(--bg);
+    border: 1px solid var(--theme-popup-border);
+}
+
+#version-filter-selector .checkbox {
+    display: flex;
+}
+
+#version-filter {
+    min-width: available;
+}
+
+#version-filter li label {
+    padding-right: 0;
+    width: 35%;
+}
+
+.version-filter-input {
+    height: 60%;
+    width: 30%;
+    text-align: center;
+    border: none;
+    border-bottom: 1px solid #000000;
+}
+
+#filter-label, .filter-clear {
+    background: var(--searchbar-bg);
+    color: var(--searchbar-fg);
+    border-color: var(--theme-popup-border);
+    filter: brightness(95%);
+}
+#filter-label:hover, .filter-clear:hover {
+    filter: brightness(90%);
+}
+.filter-input {
+    background: var(--searchbar-bg);
+    color: var(--searchbar-fg);
+    border-color: var(--theme-popup-border);
+}
+
+.filter-input::-webkit-input-placeholder,
+.filter-input::-moz-placeholder {
+    color: var(--searchbar-fg);
+    opacity: 30%;
+}
+
+.expansion-group {
+    margin-top: 15px;
+    padding: 0px 8px;
+    display: flex;
+    flex-wrap: nowrap;
+}
+
+@media (min-width: 992px) {
+    .expansion-group {
+        margin-top: 0;
+        padding: 0px 15px;
+    }
+}
+
+.expansion-control {
+    width: 50%;
+}
+
+:not(pre) > code {
+    color: var(--inline-code-color);
+    background-color: var(--inline-code-bg);
+}
+html {
+    scrollbar-color: var(--scrollbar) var(--bg);
+}
+body {
+    background: var(--bg);
+    color: var(--fg);
+}

From 7c5209121d52411d95bca199124d35ab3300ce9d Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Tue, 30 Jul 2024 17:31:42 +0200
Subject: [PATCH 08/54] Add new settings menu

---
 util/gh-pages/index.html | 27 ++++++++++++++++----
 util/gh-pages/script.js  | 45 ++++++++++++++++++++++++++++++----
 util/gh-pages/style.css  | 53 ++++++++++++++++++++++++++++++++++++----
 3 files changed, 110 insertions(+), 15 deletions(-)

diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html
index 267354cc8bfc6..12c9608f6f5f7 100644
--- a/util/gh-pages/index.html
+++ b/util/gh-pages/index.html
@@ -26,11 +26,28 @@
     <link rel="stylesheet" href="style.css">
 </head>
 <body ng-app="clippy" ng-controller="lintList">
-    <div theme-dropdown class="theme-dropdown">
-        <div id="theme-icon" class="theme-icon">&#128396;</div>
-        <ul id="theme-menu" class="theme-choice">
-            <li id="{{id}}" ng-repeat="(id, name) in themes" ng-click="selectTheme(id)">{{name}}</li>
-        </ul>
+    <div id="settings">
+        <div theme-dropdown class="theme-dropdown">
+            <div class="menu-container">
+                <div id="theme-icon" class="theme-icon">&#128396;</div>
+                <ul id="theme-menu" class="theme-choice">
+                    <li id="{{id}}" ng-repeat="(id, name) in themes" ng-click="selectTheme(id)">{{name}}</li>
+                </ul>
+            </div>
+        </div>
+        <div settings-dropdown class="settings-dropdown">
+            <div class="menu-container">
+                <div id="settings-icon" class="settings-icon"></div>
+                <ul id="settings-menu" class="settings-choice">
+                    <li>
+                        <label>
+                            <input type="checkbox" id="disable-shortcuts" onchange="changeSetting(this)">
+                            <span>Disable keyboard shortcuts</span>
+                        </label>
+                    </li>
+                </ul>
+            </div>
+        </div>
     </div>
 
     <div class="container">
diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js
index ed1e090e1b540..f072327bc340f 100644
--- a/util/gh-pages/script.js
+++ b/util/gh-pages/script.js
@@ -68,6 +68,24 @@
                 }
             }
         })
+        .directive('settingsDropdown', function ($document) {
+            return {
+                restrict: 'A',
+                link: function ($scope, $element, $attr) {
+                    $element.bind('click', function () {
+                        $element.toggleClass('open');
+                        $element.addClass('open-recent');
+                    });
+
+                    $document.bind('click', function () {
+                        if (!$element.hasClass('open-recent')) {
+                            $element.removeClass('open');
+                        }
+                        $element.removeClass('open-recent');
+                    })
+                }
+            }
+        })
         .directive('filterDropdown', function ($document) {
             return {
                 restrict: 'A',
@@ -537,6 +555,16 @@ function getQueryVariable(variable) {
     }
 }
 
+function storeValue(settingName, value) {
+    try {
+        localStorage.setItem(`clippy-lint-list-${settingName}`, value);
+    } catch (e) { }
+}
+
+function loadValue(settingName) {
+    return localStorage.getItem(`clippy-lint-list-${settingName}`);
+}
+
 function setTheme(theme, store) {
     let enableHighlight = false;
     let enableNight = false;
@@ -569,14 +597,12 @@ function setTheme(theme, store) {
     document.getElementById("styleAyu").disabled = !enableAyu;
 
     if (store) {
-        try {
-            localStorage.setItem('clippy-lint-list-theme', theme);
-        } catch (e) { }
+        storeValue("theme", theme);
     }
 }
 
 function handleShortcut(ev) {
-    if (ev.ctrlKey || ev.altKey || ev.metaKey) {
+    if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts) {
         return;
     }
 
@@ -601,11 +627,20 @@ function handleShortcut(ev) {
 document.addEventListener("keypress", handleShortcut);
 document.addEventListener("keydown", handleShortcut);
 
+function changeSetting(elem) {
+    if (elem.id === "disable-shortcuts") {
+        disableShortcuts = elem.checked;
+        storeValue(elem.id, elem.checked);
+    }
+}
+
 // loading the theme after the initial load
 const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
-const theme = localStorage.getItem('clippy-lint-list-theme');
+const theme = loadValue('theme');
 if (prefersDark.matches && !theme) {
     setTheme("coal", false);
 } else {
     setTheme(theme, false);
 }
+let disableShortcuts = loadValue('disable-shortcuts') === "true";
+document.getElementById("disable-shortcuts").checked = disableShortcuts;
diff --git a/util/gh-pages/style.css b/util/gh-pages/style.css
index f9feb0ba13ae3..4ad8b502dd82f 100644
--- a/util/gh-pages/style.css
+++ b/util/gh-pages/style.css
@@ -220,14 +220,20 @@ details[open] {
     --inline-code-bg: #191f26;
 }
 
-.theme-dropdown {
+#settings {
     position: absolute;
     margin: 0.7em;
     z-index: 10;
+    display: flex;
+}
+
+.menu-container {
+    position: relative;
+    width: 28px;
 }
 
 /* Applying the mdBook theme */
-.theme-icon {
+.theme-icon, .settings-icon {
     text-align: center;
     width: 2em;
     height: 2em;
@@ -237,10 +243,10 @@ details[open] {
     user-select: none;
     cursor: pointer;
 }
-.theme-icon:hover {
+.theme-icon:hover, .settings-icon:hover {
     background: var(--theme-hover);
 }
-.theme-choice {
+.theme-choice, .settings-choice {
     display: none;
     list-style: none;
     border: 1px solid var(--theme-popup-border);
@@ -249,9 +255,46 @@ details[open] {
     background: var(--theme-popup-bg);
     padding: 0 0;
     overflow: hidden;
+    position: absolute;
+}
+
+.settings-dropdown {
+    margin-left: 4px;
+}
+
+.settings-icon::before {
+    /* Wheel <https://www.svgrepo.com/svg/384069/settings-cog-gear> */
+    content: url('data:image/svg+xml,<svg width="18" height="18" viewBox="0 0 12 12" \
+enable-background="new 0 0 12 12" xmlns="http://www.w3.org/2000/svg">\
+<path d="M10.25,6c0-0.1243286-0.0261841-0.241333-0.0366211-0.362915l1.6077881-1.5545654l\
+-1.25-2.1650391  c0,0-1.2674561,0.3625488-2.1323853,0.6099854c-0.2034912-0.1431885-0.421875\
+-0.2639771-0.6494751-0.3701782L7.25,0h-2.5 c0,0-0.3214111,1.2857666-0.5393066,2.1572876\
+C3.9830933,2.2634888,3.7647095,2.3842773,3.5612183,2.5274658L1.428833,1.9174805 \
+l-1.25,2.1650391c0,0,0.9641113,0.9321899,1.6077881,1.5545654C1.7761841,5.758667,\
+1.75,5.8756714,1.75,6  s0.0261841,0.241333,0.0366211,0.362915L0.178833,7.9174805l1.25,\
+2.1650391l2.1323853-0.6099854  c0.2034912,0.1432495,0.421875,0.2639771,0.6494751,0.3701782\
+L4.75,12h2.5l0.5393066-2.1572876  c0.2276001-0.1062012,0.4459839-0.2269287,0.6494751\
+-0.3701782l2.1323853,0.6099854l1.25-2.1650391L10.2133789,6.362915  C10.2238159,6.241333,\
+10.25,6.1243286,10.25,6z M6,7.5C5.1715698,7.5,4.5,6.8284302,4.5,6S5.1715698,4.5,6,4.5S7.5\
+,5.1715698,7.5,6  S6.8284302,7.5,6,7.5z" fill="black"/></svg>');
+  width: 18px;
+  height: 18px;
+  display: block;
+  filter: invert(0.7);
+  padding-left: 4px;
+  padding-top: 3px;
+}
+
+.settings-choice {
+    padding: 4px;
+    width: 212px;
+}
+
+.settings-choice label {
+    cursor: pointer;
 }
 
-.theme-dropdown.open .theme-choice {
+.theme-dropdown.open .theme-choice, .settings-dropdown.open .settings-choice {
     display: block;
 }
 

From 8bf5a8332244dfc57cbd608ece30929ff44b7316 Mon Sep 17 00:00:00 2001
From: alexey semenyuk <alexsemenyuk88@gmail.com>
Date: Tue, 30 Jul 2024 22:34:56 +0500
Subject: [PATCH 09/54] Fix example

---
 clippy_lints/src/unused_io_amount.rs | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/clippy_lints/src/unused_io_amount.rs b/clippy_lints/src/unused_io_amount.rs
index 448946bd66d51..cfc4ea46bdb2b 100644
--- a/clippy_lints/src/unused_io_amount.rs
+++ b/clippy_lints/src/unused_io_amount.rs
@@ -34,11 +34,18 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// use std::io;
     /// fn foo<W: io::Write>(w: &mut W) -> io::Result<()> {
-    ///     // must be `w.write_all(b"foo")?;`
     ///     w.write(b"foo")?;
     ///     Ok(())
     /// }
     /// ```
+    /// Use instead:
+    /// ```rust,ignore
+    /// use std::io;
+    /// fn foo<W: io::Write>(w: &mut W) -> io::Result<()> {
+    ///     w.write_all(b"foo")?;
+    ///     Ok(())
+    /// }
+    /// ```
     #[clippy::version = "pre 1.29.0"]
     pub UNUSED_IO_AMOUNT,
     correctness,

From 5364cbea80f0e9b251151f40aa67592b0827759e Mon Sep 17 00:00:00 2001
From: rzvxa <rzvxa@protonmail.com>
Date: Sat, 3 Aug 2024 22:52:22 +0330
Subject: [PATCH 10/54] Respect allow inconsistent_struct_constructor on the
 type definition

---
 .../src/inconsistent_struct_constructor.rs    |  4 ++++
 .../ui/inconsistent_struct_constructor.fixed  | 21 +++++++++++++++++++
 tests/ui/inconsistent_struct_constructor.rs   | 21 +++++++++++++++++++
 .../ui/inconsistent_struct_constructor.stderr |  4 ++--
 4 files changed, 48 insertions(+), 2 deletions(-)

diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs
index 5b0aadf35c62d..da289225509bf 100644
--- a/clippy_lints/src/inconsistent_struct_constructor.rs
+++ b/clippy_lints/src/inconsistent_struct_constructor.rs
@@ -1,4 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_lint_allowed;
 use clippy_utils::source::snippet;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::Applicability;
@@ -71,6 +72,9 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
             && let ty = cx.typeck_results().expr_ty(expr)
             && let Some(adt_def) = ty.ty_adt_def()
             && adt_def.is_struct()
+            && let Some(local_def_id) = adt_def.did().as_local()
+            && let ty_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id)
+            && !is_lint_allowed(cx, INCONSISTENT_STRUCT_CONSTRUCTOR, ty_hir_id)
             && let Some(variant) = adt_def.variants().iter().next()
         {
             let mut def_order_map = FxHashMap::default();
diff --git a/tests/ui/inconsistent_struct_constructor.fixed b/tests/ui/inconsistent_struct_constructor.fixed
index 5778f8f526f86..4c324587c96fa 100644
--- a/tests/ui/inconsistent_struct_constructor.fixed
+++ b/tests/ui/inconsistent_struct_constructor.fixed
@@ -15,6 +15,14 @@ struct Foo {
     z: i32,
 }
 
+#[derive(Default)]
+#[allow(clippy::inconsistent_struct_constructor)]
+struct Bar {
+    x: i32,
+    y: i32,
+    z: i32,
+}
+
 mod without_base {
     use super::Foo;
 
@@ -70,4 +78,17 @@ mod with_base {
     }
 }
 
+mod with_allow_ty_def {
+    use super::Bar;
+
+    fn test() {
+        let x = 1;
+        let y = 1;
+        let z = 1;
+
+        // Should NOT lint because `Bar` is defined with `#[allow(clippy::inconsistent_struct_constructor)]`
+        Bar { y, x, z };
+    }
+}
+
 fn main() {}
diff --git a/tests/ui/inconsistent_struct_constructor.rs b/tests/ui/inconsistent_struct_constructor.rs
index 9efaf0689342f..d49f236b9b07b 100644
--- a/tests/ui/inconsistent_struct_constructor.rs
+++ b/tests/ui/inconsistent_struct_constructor.rs
@@ -15,6 +15,14 @@ struct Foo {
     z: i32,
 }
 
+#[derive(Default)]
+#[allow(clippy::inconsistent_struct_constructor)]
+struct Bar {
+    x: i32,
+    y: i32,
+    z: i32,
+}
+
 mod without_base {
     use super::Foo;
 
@@ -74,4 +82,17 @@ mod with_base {
     }
 }
 
+mod with_allow_ty_def {
+    use super::Bar;
+
+    fn test() {
+        let x = 1;
+        let y = 1;
+        let z = 1;
+
+        // Should NOT lint because `Bar` is defined with `#[allow(clippy::inconsistent_struct_constructor)]`
+        Bar { y, x, z };
+    }
+}
+
 fn main() {}
diff --git a/tests/ui/inconsistent_struct_constructor.stderr b/tests/ui/inconsistent_struct_constructor.stderr
index 1192271f911fd..97bb7c789a720 100644
--- a/tests/ui/inconsistent_struct_constructor.stderr
+++ b/tests/ui/inconsistent_struct_constructor.stderr
@@ -1,5 +1,5 @@
 error: struct constructor field order is inconsistent with struct definition field order
-  --> tests/ui/inconsistent_struct_constructor.rs:28:9
+  --> tests/ui/inconsistent_struct_constructor.rs:36:9
    |
 LL |         Foo { y, x, z };
    |         ^^^^^^^^^^^^^^^ help: try: `Foo { x, y, z }`
@@ -8,7 +8,7 @@ LL |         Foo { y, x, z };
    = help: to override `-D warnings` add `#[allow(clippy::inconsistent_struct_constructor)]`
 
 error: struct constructor field order is inconsistent with struct definition field order
-  --> tests/ui/inconsistent_struct_constructor.rs:55:9
+  --> tests/ui/inconsistent_struct_constructor.rs:63:9
    |
 LL | /         Foo {
 LL | |             z,

From 234a1d35d9f1acc1da25b5069908ca2de209d322 Mon Sep 17 00:00:00 2001
From: y21 <30553356+y21@users.noreply.github.com>
Date: Mon, 5 Aug 2024 12:02:08 +0200
Subject: [PATCH 11/54] recognize metavariables in tail expressions

---
 clippy_lints/src/macro_metavars_in_unsafe.rs  | 21 +++++++++++++++++--
 .../macro_metavars_in_unsafe/default/test.rs  | 15 ++++++++++++-
 .../default/test.stderr                       | 16 +++++++++++---
 3 files changed, 46 insertions(+), 6 deletions(-)

diff --git a/clippy_lints/src/macro_metavars_in_unsafe.rs b/clippy_lints/src/macro_metavars_in_unsafe.rs
index fed58f7ff1481..e215097142be9 100644
--- a/clippy_lints/src/macro_metavars_in_unsafe.rs
+++ b/clippy_lints/src/macro_metavars_in_unsafe.rs
@@ -122,8 +122,23 @@ struct BodyVisitor<'a, 'tcx> {
     /// within a relevant macro.
     macro_unsafe_blocks: Vec<HirId>,
     /// When this is >0, it means that the node currently being visited is "within" a
-    /// macro definition. This is not necessary for correctness, it merely helps reduce the number
-    /// of spans we need to insert into the map, since only spans from macros are relevant.
+    /// macro definition.
+    /// This is used to detect if an expression represents a metavariable.
+    ///
+    /// For example, the following pre-expansion code that we want to lint
+    /// ```ignore
+    /// macro_rules! m { ($e:expr) => { unsafe { $e; } } }
+    /// m!(1);
+    /// ```
+    /// would look like this post-expansion code:
+    /// ```ignore
+    /// unsafe { /* macro */
+    ///     1 /* root */; /* macro */
+    /// }
+    /// ```
+    /// Visiting the block and the statement will increment the `expn_depth` so that it is >0,
+    /// and visiting the expression with a root context while `expn_depth > 0` tells us
+    /// that it must be a metavariable.
     expn_depth: u32,
     cx: &'a LateContext<'tcx>,
     lint: &'a mut ExprMetavarsInUnsafe,
@@ -157,7 +172,9 @@ impl<'a, 'tcx> Visitor<'tcx> for BodyVisitor<'a, 'tcx> {
             && (self.lint.warn_unsafe_macro_metavars_in_private_macros || is_public_macro(self.cx, macro_def_id))
         {
             self.macro_unsafe_blocks.push(block.hir_id);
+            self.expn_depth += 1;
             walk_block(self, block);
+            self.expn_depth -= 1;
             self.macro_unsafe_blocks.pop();
         } else if ctxt.is_root() && self.expn_depth > 0 {
             let unsafe_block = self.macro_unsafe_blocks.last().copied();
diff --git a/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs b/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs
index a312df5a43a07..3dafea56514db 100644
--- a/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs
+++ b/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs
@@ -1,7 +1,7 @@
 //! Tests macro_metavars_in_unsafe with default configuration
 #![feature(decl_macro)]
 #![warn(clippy::macro_metavars_in_unsafe)]
-#![allow(clippy::no_effect)]
+#![allow(clippy::no_effect, clippy::not_unsafe_ptr_arg_deref)]
 
 #[macro_export]
 macro_rules! allow_works {
@@ -237,6 +237,19 @@ macro_rules! nested_macros {
     }};
 }
 
+pub mod issue13219 {
+    #[macro_export]
+    macro_rules! m {
+        ($e:expr) => {
+            // Metavariable in a block tail expression
+            unsafe { $e }
+        };
+    }
+    pub fn f(p: *const i32) -> i32 {
+        m!(*p)
+    }
+}
+
 fn main() {
     allow_works!(1);
     simple!(1);
diff --git a/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr b/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr
index d6b97f6fde1e1..6f0ebcbba0239 100644
--- a/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr
+++ b/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr
@@ -1,3 +1,15 @@
+error: this macro expands metavariables in an unsafe block
+  --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:245:13
+   |
+LL |             unsafe { $e }
+   |             ^^^^^^^^^^^^^
+   |
+   = note: this allows the user of the macro to write unsafe code outside of an unsafe block
+   = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
+   = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
+   = note: `-D clippy::macro-metavars-in-unsafe` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::macro_metavars_in_unsafe)]`
+
 error: this macro expands metavariables in an unsafe block
   --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:19:9
    |
@@ -10,8 +22,6 @@ LL | |         }
    = note: this allows the user of the macro to write unsafe code outside of an unsafe block
    = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
    = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
-   = note: `-D clippy::macro-metavars-in-unsafe` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::macro_metavars_in_unsafe)]`
 
 error: this macro expands metavariables in an unsafe block
   --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:30:9
@@ -183,5 +193,5 @@ LL | |         }
    = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
    = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
 
-error: aborting due to 14 previous errors
+error: aborting due to 15 previous errors
 

From 5e25e7c37051df50009b7fad52adacd30eda5824 Mon Sep 17 00:00:00 2001
From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com>
Date: Wed, 7 Aug 2024 21:43:52 +0900
Subject: [PATCH 12/54] add a test for ice-3717.rs

https://github.com/rust-lang/rust-clippy/issues/13099
---
 tests/ui/crashes/ice-3717.fixed  | 11 +++++++++++
 tests/ui/crashes/ice-3717.rs     |  2 --
 tests/ui/crashes/ice-3717.stderr |  2 +-
 3 files changed, 12 insertions(+), 3 deletions(-)
 create mode 100644 tests/ui/crashes/ice-3717.fixed

diff --git a/tests/ui/crashes/ice-3717.fixed b/tests/ui/crashes/ice-3717.fixed
new file mode 100644
index 0000000000000..3f54b326979c9
--- /dev/null
+++ b/tests/ui/crashes/ice-3717.fixed
@@ -0,0 +1,11 @@
+#![deny(clippy::implicit_hasher)]
+
+use std::collections::HashSet;
+
+fn main() {}
+
+pub fn ice_3717<S: ::std::hash::BuildHasher + Default>(_: &HashSet<usize, S>) {
+    //~^ ERROR: parameter of type `HashSet` should be generalized over different hashers
+    let _ = [0u8; 0];
+    let _: HashSet<usize> = HashSet::default();
+}
diff --git a/tests/ui/crashes/ice-3717.rs b/tests/ui/crashes/ice-3717.rs
index 770f6cf448a63..2890a9277c719 100644
--- a/tests/ui/crashes/ice-3717.rs
+++ b/tests/ui/crashes/ice-3717.rs
@@ -1,7 +1,5 @@
 #![deny(clippy::implicit_hasher)]
 
-//@no-rustfix: need to change the suggestion to a multipart suggestion
-
 use std::collections::HashSet;
 
 fn main() {}
diff --git a/tests/ui/crashes/ice-3717.stderr b/tests/ui/crashes/ice-3717.stderr
index 4b4618ee1bbd0..aac72c669654a 100644
--- a/tests/ui/crashes/ice-3717.stderr
+++ b/tests/ui/crashes/ice-3717.stderr
@@ -1,5 +1,5 @@
 error: parameter of type `HashSet` should be generalized over different hashers
-  --> tests/ui/crashes/ice-3717.rs:9:21
+  --> tests/ui/crashes/ice-3717.rs:7:21
    |
 LL | pub fn ice_3717(_: &HashSet<usize>) {
    |                     ^^^^^^^^^^^^^^

From 6ce7e6d99f8bc97375f70331204c78f6438e7495 Mon Sep 17 00:00:00 2001
From: Michael Goulet <michael@errs.io>
Date: Thu, 8 Aug 2024 11:56:12 -0400
Subject: [PATCH 13/54] Rename struct_tail_erasing_lifetimes to
 struct_tail_for_codegen

---
 clippy_utils/src/qualify_min_const_fn.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs
index f206b2ceebcb1..553af913ef9df 100644
--- a/clippy_utils/src/qualify_min_const_fn.rs
+++ b/clippy_utils/src/qualify_min_const_fn.rs
@@ -142,7 +142,7 @@ fn check_rvalue<'tcx>(
                 // We cannot allow this for now.
                 return Err((span, "unsizing casts are only allowed for references right now".into()));
             };
-            let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
+            let unsized_ty = tcx.struct_tail_for_codegen(pointee_ty, tcx.param_env(def_id));
             if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
                 check_operand(tcx, op, span, body, msrv)?;
                 // Casting/coercing things to slices is fine.

From 1ac76a2062b94b72c36650d21ac1af40b4aea0e3 Mon Sep 17 00:00:00 2001
From: Philipp Krones <hello@philkrones.com>
Date: Thu, 8 Aug 2024 19:13:50 +0200
Subject: [PATCH 14/54] Merge commit 'cb806113e0f83a8f9b47d35b453b676543bcc40e'
 into clippy-subtree-update

---
 .cargo/config.toml                            |   7 +
 .github/workflows/clippy.yml                  |  13 +-
 .github/workflows/clippy_bors.yml             |  17 +-
 .github/workflows/clippy_dev.yml              |   5 +-
 .github/workflows/lintcheck.yml               |   4 +-
 CHANGELOG.md                                  |   2 +
 Cargo.toml                                    |   3 +-
 book/src/lint_configuration.md                |   6 +-
 clippy_config/Cargo.toml                      |   4 +-
 clippy_config/src/conf.rs                     | 724 +++++++++---------
 clippy_config/src/lib.rs                      |   3 +-
 clippy_config/src/metadata.rs                 | 105 +--
 clippy_dev/Cargo.toml                         |   3 -
 clippy_dev/src/fmt.rs                         | 392 +++++++---
 clippy_dev/src/lib.rs                         |   1 -
 clippy_dev/src/main.rs                        |   5 +-
 clippy_dev/src/serve.rs                       |  15 +-
 clippy_dev/src/update_lints.rs                | 324 +++-----
 clippy_lints/Cargo.toml                       |   1 -
 clippy_lints/src/approx_const.rs              |   2 +-
 clippy_lints/src/as_conversions.rs            |  10 +-
 clippy_lints/src/asm_syntax.rs                |  22 +-
 clippy_lints/src/assertions_on_constants.rs   |   4 +-
 .../src/assertions_on_result_states.rs        |  47 +-
 clippy_lints/src/assigning_clones.rs          |  29 +-
 clippy_lints/src/attrs/allow_attributes.rs    |  20 +-
 .../attrs/allow_attributes_without_reason.rs  |  10 +-
 clippy_lints/src/bool_to_int_with_if.rs       |   4 +-
 clippy_lints/src/booleans.rs                  |  10 +-
 clippy_lints/src/casts/cast_lossless.rs       |   8 +-
 clippy_lints/src/casts/cast_nan_to_int.rs     |   4 +-
 .../src/casts/cast_possible_truncation.rs     |   4 +-
 clippy_lints/src/casts/cast_sign_loss.rs      |   6 +-
 .../src/casts/fn_to_numeric_cast_any.rs       |  37 +-
 clippy_lints/src/casts/zero_ptr.rs            |   4 +-
 clippy_lints/src/checked_conversions.rs       |   4 +-
 clippy_lints/src/comparison_chain.rs          |   4 +-
 clippy_lints/src/create_dir.rs                |  24 +-
 clippy_lints/src/dbg_macro.rs                 | 108 +--
 clippy_lints/src/declared_lints.rs            |   3 +-
 clippy_lints/src/default.rs                   |   2 +-
 clippy_lints/src/default_numeric_fallback.rs  |  23 +-
 .../src/default_union_representation.rs       |  16 +-
 clippy_lints/src/deprecated_lints.rs          | 394 ++++------
 clippy_lints/src/dereference.rs               |   4 +-
 clippy_lints/src/doc/mod.rs                   |   9 +-
 clippy_lints/src/drop_forget_ref.rs           |  18 +-
 clippy_lints/src/else_if_without_else.rs      |  10 +-
 clippy_lints/src/empty_drop.rs                |  19 +-
 clippy_lints/src/endian_bytes.rs              |  89 +--
 clippy_lints/src/enum_clike.rs                |   2 +-
 clippy_lints/src/eta_reduction.rs             | 327 ++++----
 clippy_lints/src/exhaustive_items.rs          |   6 +-
 .../src/field_scoped_visibility_modifiers.rs  |  10 +-
 clippy_lints/src/float_literal.rs             |  45 +-
 clippy_lints/src/floating_point_arithmetic.rs |  36 +-
 clippy_lints/src/format_push_string.rs        |  10 +-
 clippy_lints/src/from_str_radix_10.rs         |   6 +-
 clippy_lints/src/if_let_mutex.rs              |  66 +-
 clippy_lints/src/if_not_else.rs               |   4 +-
 clippy_lints/src/if_then_some_else_none.rs    |  47 +-
 clippy_lints/src/implicit_hasher.rs           |  53 +-
 clippy_lints/src/implicit_return.rs           |  21 +-
 clippy_lints/src/implicit_saturating_add.rs   |   8 +-
 clippy_lints/src/incompatible_msrv.rs         |   7 +-
 clippy_lints/src/index_refutable_slice.rs     |   4 +-
 clippy_lints/src/indexing_slicing.rs          |  11 +-
 clippy_lints/src/infinite_iter.rs             |   1 -
 clippy_lints/src/inherent_impl.rs             |   9 +-
 .../src/invalid_upcast_comparisons.rs         |   4 +-
 clippy_lints/src/large_include_file.rs        |  16 +-
 clippy_lints/src/let_underscore.rs            |  58 +-
 clippy_lints/src/lib.deprecated.rs            |  78 --
 clippy_lints/src/lib.rs                       |  81 +-
 clippy_lints/src/literal_representation.rs    |  77 +-
 .../src/loops/explicit_counter_loop.rs        |   8 +-
 clippy_lints/src/loops/for_kv_map.rs          |   7 +-
 .../src/loops/manual_while_let_some.rs        |   7 +-
 clippy_lints/src/loops/mod.rs                 |  10 +-
 clippy_lints/src/loops/needless_range_loop.rs |  11 +-
 .../src/loops/unused_enumerate_index.rs       |   7 +-
 .../src/loops/while_immutable_condition.rs    |   4 +-
 .../src/loops/while_let_on_iterator.rs        |   7 +-
 clippy_lints/src/manual_clamp.rs              |  13 +-
 clippy_lints/src/manual_float_methods.rs      |   7 +-
 clippy_lints/src/manual_is_ascii_check.rs     |   4 +-
 clippy_lints/src/manual_rem_euclid.rs         |   8 +-
 clippy_lints/src/manual_rotate.rs             |   4 +-
 .../src/manual_slice_size_calculation.rs      |   4 +-
 clippy_lints/src/manual_strip.rs              |  26 +-
 clippy_lints/src/manual_unwrap_or_default.rs  |   4 +-
 clippy_lints/src/matches/manual_unwrap_or.rs  |   4 +-
 clippy_lints/src/matches/match_ref_pats.rs    |   8 +-
 clippy_lints/src/matches/match_wild_enum.rs   |  39 +-
 .../src/matches/match_wild_err_arm.rs         |   4 +-
 clippy_lints/src/matches/mod.rs               |   6 +-
 clippy_lints/src/matches/overlapping_arms.rs  |  30 +-
 clippy_lints/src/matches/redundant_guards.rs  |   4 +-
 .../matches/rest_pat_in_fully_bound_struct.rs |  10 +-
 clippy_lints/src/matches/single_match.rs      | 394 ++++++----
 clippy_lints/src/matches/try_err.rs           |  39 +-
 .../src/methods/bind_instead_of_map.rs        |  19 +-
 clippy_lints/src/methods/clone_on_ref_ptr.rs  |  23 +-
 clippy_lints/src/methods/filetype_is_file.rs  |   9 +-
 clippy_lints/src/methods/get_unwrap.rs        |  44 +-
 clippy_lints/src/methods/implicit_clone.rs    |   6 +-
 .../src/methods/is_digit_ascii_radix.rs       |   4 +-
 clippy_lints/src/methods/is_empty.rs          |   4 +-
 clippy_lints/src/methods/iter_kv_map.rs       |  15 +-
 clippy_lints/src/methods/iter_nth_zero.rs     |   4 +-
 clippy_lints/src/methods/iter_skip_zero.rs    |   4 +-
 .../src/methods/iterator_step_by_zero.rs      |   4 +-
 clippy_lints/src/methods/map_clone.rs         |   6 +-
 clippy_lints/src/methods/map_err_ignore.rs    |  12 +-
 clippy_lints/src/methods/mod.rs               |  28 +-
 clippy_lints/src/methods/open_options.rs      |   2 +-
 clippy_lints/src/methods/repeat_once.rs       |   4 +-
 clippy_lints/src/methods/str_splitn.rs        |   4 +-
 .../src/methods/unnecessary_min_or_max.rs     |  13 +-
 .../src/methods/unnecessary_to_owned.rs       |  13 +-
 .../src/methods/unused_enumerate_index.rs     |   7 +-
 clippy_lints/src/methods/useless_asref.rs     |   6 +-
 .../src/methods/verbose_file_reads.rs         |   7 +-
 .../src/methods/wrong_self_convention.rs      |   2 +-
 clippy_lints/src/minmax.rs                    |  20 +-
 clippy_lints/src/misc_early/literal_suffix.rs |  30 +-
 .../src/misc_early/unneeded_field_pattern.rs  |  21 +-
 clippy_lints/src/missing_assert_message.rs    |  10 +-
 clippy_lints/src/missing_const_for_fn.rs      |   2 +-
 clippy_lints/src/missing_trait_methods.rs     |  46 +-
 .../src/mixed_read_write_in_expression.rs     |  12 +-
 clippy_lints/src/module_style.rs              |  32 +-
 .../src/needless_borrows_for_generic_args.rs  |  48 +-
 clippy_lints/src/needless_pass_by_value.rs    |  11 +-
 clippy_lints/src/no_effect.rs                 |   2 +-
 clippy_lints/src/non_copy_const.rs            |   4 +-
 clippy_lints/src/only_used_in_recursion.rs    |   1 -
 .../operators/absurd_extreme_comparisons.rs   |   4 +-
 .../src/operators/arithmetic_side_effects.rs  |  10 +-
 clippy_lints/src/operators/bit_mask.rs        |   4 +-
 .../src/operators/const_comparisons.rs        |   7 +-
 clippy_lints/src/operators/duration_subsec.rs |   4 +-
 clippy_lints/src/operators/erasing_op.rs      |   6 +-
 clippy_lints/src/operators/float_cmp.rs       |   8 +-
 clippy_lints/src/operators/identity_op.rs     |  13 +-
 .../src/operators/integer_division.rs         |  14 +-
 .../src/operators/modulo_arithmetic.rs        |  16 +-
 .../src/operators/numeric_arithmetic.rs       |   4 +-
 clippy_lints/src/operators/op_ref.rs          |   6 +-
 clippy_lints/src/option_if_let_else.rs        |   6 +-
 clippy_lints/src/partial_pub_fields.rs        |  26 +-
 clippy_lints/src/pathbuf_init_then_push.rs    |   4 +-
 clippy_lints/src/pattern_type_mismatch.rs     |  30 +-
 clippy_lints/src/pub_use.rs                   |  14 +-
 clippy_lints/src/question_mark.rs             |   8 +-
 clippy_lints/src/question_mark_used.rs        |  10 +-
 clippy_lints/src/ranges.rs                    |  15 +-
 clippy_lints/src/redundant_clone.rs           |   2 +-
 clippy_lints/src/redundant_slicing.rs         |  68 +-
 clippy_lints/src/ref_patterns.rs              |  14 +-
 clippy_lints/src/regex.rs                     |   4 +-
 clippy_lints/src/renamed_lints.rs             |  65 --
 clippy_lints/src/repeat_vec_with_capacity.rs  |   4 +-
 clippy_lints/src/returns.rs                   |   2 +-
 clippy_lints/src/semicolon_block.rs           |  12 +-
 clippy_lints/src/set_contains_or_insert.rs    |  33 +-
 clippy_lints/src/shadow.rs                    |  13 +-
 .../src/single_char_lifetime_names.rs         |  10 +-
 clippy_lints/src/size_of_ref.rs               |   5 +-
 clippy_lints/src/std_instead_of_core.rs       |  69 +-
 clippy_lints/src/strings.rs                   |  23 +-
 .../src/suspicious_xor_used_as_pow.rs         |  15 +-
 clippy_lints/src/swap.rs                      |   4 +-
 clippy_lints/src/tests_outside_test_module.rs |  10 +-
 clippy_lints/src/transmute/mod.rs             |   4 +-
 .../src/transmute/transmute_null_to_fn.rs     |   7 +-
 .../src/transmute/transmuting_null.rs         |   4 +-
 clippy_lints/src/types/borrowed_box.rs        |  24 +-
 clippy_lints/src/types/rc_buffer.rs           |  62 +-
 clippy_lints/src/types/rc_mutex.rs            |  14 +-
 .../src/undocumented_unsafe_blocks.rs         |  53 +-
 clippy_lints/src/unicode.rs                   |  64 +-
 clippy_lints/src/unused_result_ok.rs          |  59 ++
 .../interning_defined_symbol.rs               |   6 +-
 .../src/utils/internal_lints/invalid_paths.rs |   8 +-
 .../internal_lints/lint_without_lint_pass.rs  |  85 +-
 .../internal_lints/metadata_collector.rs      | 117 ++-
 clippy_lints/src/vec.rs                       |   4 +-
 clippy_lints/src/visibility.rs                |  43 +-
 clippy_lints/src/zero_div_zero.rs             |   7 +-
 clippy_utils/Cargo.toml                       |   3 -
 clippy_utils/src/consts.rs                    | 279 ++++---
 clippy_utils/src/diagnostics.rs               |  29 -
 clippy_utils/src/eager_or_lazy.rs             |  16 +-
 clippy_utils/src/higher.rs                    |  13 +-
 clippy_utils/src/hir_utils.rs                 |  12 +-
 clippy_utils/src/lib.rs                       |  46 +-
 clippy_utils/src/paths.rs                     |   1 -
 clippy_utils/src/source.rs                    | 129 ++--
 clippy_utils/src/ty.rs                        |  13 -
 clippy_utils/src/visitors.rs                  |  98 ++-
 declare_clippy_lint/Cargo.toml                |   3 -
 declare_clippy_lint/src/lib.rs                |   1 -
 lintcheck/Cargo.toml                          |   3 -
 lintcheck/src/config.rs                       |   8 +-
 lintcheck/src/input.rs                        |   6 +-
 lintcheck/src/main.rs                         |  28 +-
 rust-toolchain                                |   2 +-
 rustc_tools_util/Cargo.toml                   |   3 -
 rustc_tools_util/src/lib.rs                   |  59 +-
 src/driver.rs                                 |   2 -
 src/main.rs                                   |   1 -
 tests/check-fmt.rs                            |   1 -
 tests/compile-test.rs                         |   6 +-
 tests/dogfood.rs                              |   1 -
 tests/integration.rs                          |   1 -
 tests/lint_message_convention.rs              |   1 -
 tests/missing-test-files.rs                   |   1 -
 .../ui-internal/default_deprecation_reason.rs |  30 -
 .../default_deprecation_reason.stderr         |  22 -
 .../excessive_nesting/excessive_nesting.rs    |  20 +-
 .../excessive_nesting.stderr                  |  74 +-
 tests/ui-toml/unwrap_used/unwrap_used.stderr  | 134 +++-
 tests/ui/assigning_clones.fixed               |  68 ++
 tests/ui/assigning_clones.rs                  |  68 ++
 tests/ui/assigning_clones.stderr              |  86 ++-
 tests/ui/bind_instead_of_map_multipart.stderr |  10 +-
 tests/ui/crashes/ice-3717.stderr              |  11 +-
 tests/ui/crashes/ice-6254.rs                  |   3 +-
 tests/ui/create_dir.stderr                    |  13 +-
 tests/ui/dbg_macro/dbg_macro.stderr           |   4 +-
 tests/ui/dbg_macro/dbg_macro_unfixable.stderr |   2 +-
 tests/ui/deprecated.rs                        |  31 +-
 tests/ui/deprecated.stderr                    |  68 +-
 tests/ui/deprecated_old.rs                    |   9 -
 tests/ui/deprecated_old.stderr                |  23 -
 tests/ui/deref_by_slicing.fixed               |   4 +
 tests/ui/deref_by_slicing.rs                  |   4 +
 tests/ui/deref_by_slicing.stderr              |   8 +-
 tests/ui/doc/footnote_issue_13183.rs          |  10 +
 tests/ui/empty_drop.stderr                    |   7 +-
 tests/ui/eta.fixed                            |  17 +
 tests/ui/eta.rs                               |  17 +
 tests/ui/eta.stderr                           |  74 +-
 tests/ui/excessive_precision.stderr           | 111 ++-
 tests/ui/explicit_counter_loop.rs             |  13 +
 tests/ui/explicit_counter_loop.stderr         |   8 +-
 tests/ui/fn_to_numeric_cast_any.stderr        | 118 ++-
 tests/ui/for_kv_map.fixed                     |  10 +
 tests/ui/for_kv_map.rs                        |  10 +
 tests/ui/for_kv_map.stderr                    |  13 +-
 tests/ui/get_unwrap.stderr                    | 152 +++-
 tests/ui/if_let_mutex.rs                      |   9 +
 tests/ui/if_let_mutex.stderr                  |  24 +-
 tests/ui/if_then_some_else_none.fixed         | 119 +++
 tests/ui/if_then_some_else_none.rs            |   2 +-
 tests/ui/if_then_some_else_none.stderr        |  19 +-
 tests/ui/implicit_hasher.fixed                | 102 +++
 tests/ui/implicit_hasher.rs                   |   9 +-
 tests/ui/implicit_hasher.stderr               | 131 +++-
 tests/ui/implicit_return.stderr               | 104 ++-
 tests/ui/lossy_float_literal.stderr           |  76 +-
 tests/ui/missing_trait_methods.rs             |   8 +
 tests/ui/missing_trait_methods.stderr         |  38 +-
 .../needless_borrows_for_generic_args.fixed   |   8 +
 tests/ui/needless_borrows_for_generic_args.rs |   8 +
 tests/ui/needless_pub_self.stderr             |  11 +-
 tests/ui/nonminimal_bool.rs                   |   6 +
 tests/ui/patterns.fixed                       |   2 +-
 tests/ui/patterns.rs                          |   2 +-
 tests/ui/rename.fixed                         | 131 ++--
 tests/ui/rename.rs                            | 131 ++--
 tests/ui/rename.stderr                        | 158 ++--
 tests/ui/set_contains_or_insert.rs            |  83 +-
 tests/ui/set_contains_or_insert.stderr        |  72 +-
 tests/ui/single_match.fixed                   |  43 ++
 tests/ui/single_match.rs                      |  49 ++
 tests/ui/single_match.stderr                  |  20 +-
 tests/ui/std_instead_of_core.fixed            |  17 +-
 tests/ui/std_instead_of_core.rs               |  17 +-
 tests/ui/std_instead_of_core.stderr           |  14 +-
 tests/ui/suspicious_xor_used_as_pow.stderr    |  47 +-
 tests/ui/try_err.fixed                        |  10 +-
 tests/ui/try_err.rs                           |  10 +-
 tests/ui/uninit_vec.rs                        |   2 +-
 tests/ui/unneeded_field_pattern.rs            |   2 +-
 tests/ui/unused_result_ok.fixed               |  40 +
 tests/ui/unused_result_ok.rs                  |  40 +
 tests/ui/unused_result_ok.stderr              |  52 ++
 tests/ui/unwrap_expect_used.rs                |   2 +-
 tests/ui/while_let_on_iterator.fixed          |   9 +
 tests/ui/while_let_on_iterator.rs             |   9 +
 tests/ui/while_let_on_iterator.stderr         |   8 +-
 tests/versioncheck.rs                         |   1 -
 tests/workspace_test/path_dep/Cargo.toml      |   3 +
 util/gh-pages/index.html                      |   4 +-
 util/gh-pages/script.js                       |  74 +-
 297 files changed, 5572 insertions(+), 4012 deletions(-)
 delete mode 100644 clippy_lints/src/lib.deprecated.rs
 delete mode 100644 clippy_lints/src/renamed_lints.rs
 create mode 100644 clippy_lints/src/unused_result_ok.rs
 delete mode 100644 tests/ui-internal/default_deprecation_reason.rs
 delete mode 100644 tests/ui-internal/default_deprecation_reason.stderr
 delete mode 100644 tests/ui/deprecated_old.rs
 delete mode 100644 tests/ui/deprecated_old.stderr
 create mode 100644 tests/ui/doc/footnote_issue_13183.rs
 create mode 100644 tests/ui/if_then_some_else_none.fixed
 create mode 100644 tests/ui/implicit_hasher.fixed
 create mode 100644 tests/ui/unused_result_ok.fixed
 create mode 100644 tests/ui/unused_result_ok.rs
 create mode 100644 tests/ui/unused_result_ok.stderr

diff --git a/.cargo/config.toml b/.cargo/config.toml
index 7afdd068a9905..ce07290d1e1e5 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -13,6 +13,13 @@ target-dir = "target"
 
 [unstable]
 binary-dep-depinfo = true
+profile-rustflags = true
 
 [profile.dev]
 split-debuginfo = "unpacked"
+
+# Add back the containing directory of the packages we have to refer to using --manifest-path
+[profile.dev.package.clippy_dev]
+rustflags = ["--remap-path-prefix", "=clippy_dev"]
+[profile.dev.package.lintcheck]
+rustflags = ["--remap-path-prefix", "=lintcheck"]
diff --git a/.github/workflows/clippy.yml b/.github/workflows/clippy.yml
index 06bf3b6fdbfab..0a0538490cce9 100644
--- a/.github/workflows/clippy.yml
+++ b/.github/workflows/clippy.yml
@@ -25,6 +25,7 @@ env:
   CARGO_TARGET_DIR: '${{ github.workspace }}/target'
   NO_FMT_TEST: 1
   CARGO_INCREMENTAL: 0
+  RUSTFLAGS: -D warnings
 
 concurrency:
   # For a given workflow, if we push to the same PR, cancel all previous builds on that PR.
@@ -47,25 +48,25 @@ jobs:
 
     # Run
     - name: Build
-      run: cargo build --tests --features deny-warnings,internal
+      run: cargo build --tests --features internal
 
     - name: Test
-      run: cargo test --features deny-warnings,internal
+      run: cargo test --features internal
 
     - name: Test clippy_lints
-      run: cargo test --features deny-warnings,internal
+      run: cargo test --features internal
       working-directory: clippy_lints
 
     - name: Test clippy_utils
-      run: cargo test --features deny-warnings
+      run: cargo test
       working-directory: clippy_utils
 
     - name: Test rustc_tools_util
-      run: cargo test --features deny-warnings
+      run: cargo test
       working-directory: rustc_tools_util
 
     - name: Test clippy_dev
-      run: cargo test --features deny-warnings
+      run: cargo test
       working-directory: clippy_dev
 
     - name: Test clippy-driver
diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml
index 1f4bec9291823..10e18e84c89fd 100644
--- a/.github/workflows/clippy_bors.yml
+++ b/.github/workflows/clippy_bors.yml
@@ -11,6 +11,7 @@ env:
   CARGO_TARGET_DIR: '${{ github.workspace }}/target'
   NO_FMT_TEST: 1
   CARGO_INCREMENTAL: 0
+  RUSTFLAGS: -D warnings
 
 concurrency:
   # For a given workflow, if we push to the same branch, cancel all previous builds on that branch.
@@ -85,34 +86,34 @@ jobs:
 
     # Run
     - name: Build
-      run: cargo build --tests --features deny-warnings,internal
+      run: cargo build --tests --features internal
 
     - name: Test
       if: matrix.host == 'x86_64-unknown-linux-gnu'
-      run: cargo test --features deny-warnings,internal
+      run: cargo test --features internal
 
     - name: Test
       if: matrix.host != 'x86_64-unknown-linux-gnu'
-      run: cargo test --features deny-warnings,internal -- --skip dogfood
+      run: cargo test --features internal -- --skip dogfood
 
     - name: Test clippy_lints
-      run: cargo test --features deny-warnings,internal
+      run: cargo test --features internal
       working-directory: clippy_lints
 
     - name: Test clippy_utils
-      run: cargo test --features deny-warnings
+      run: cargo test
       working-directory: clippy_utils
 
     - name: Test clippy_config
-      run: cargo test --features deny-warnings
+      run: cargo test
       working-directory: clippy_config
 
     - name: Test rustc_tools_util
-      run: cargo test --features deny-warnings
+      run: cargo test
       working-directory: rustc_tools_util
 
     - name: Test clippy_dev
-      run: cargo test --features deny-warnings
+      run: cargo test
       working-directory: clippy_dev
 
     - name: Test clippy-driver
diff --git a/.github/workflows/clippy_dev.yml b/.github/workflows/clippy_dev.yml
index 37f18a4c08740..cf0a8bde202ad 100644
--- a/.github/workflows/clippy_dev.yml
+++ b/.github/workflows/clippy_dev.yml
@@ -16,6 +16,7 @@ on:
 env:
   RUST_BACKTRACE: 1
   CARGO_INCREMENTAL: 0
+  RUSTFLAGS: -D warnings
 
 jobs:
   clippy_dev:
@@ -28,7 +29,7 @@ jobs:
 
     # Run
     - name: Build
-      run: cargo build --features deny-warnings
+      run: cargo build
       working-directory: clippy_dev
 
     - name: Test update_lints
@@ -38,6 +39,8 @@ jobs:
       run: cargo dev fmt --check
 
     - name: Test cargo dev new lint
+      env:
+        RUSTFLAGS: -A unused-imports
       run: |
         cargo dev new_lint --name new_early_pass --pass early
         cargo dev new_lint --name new_late_pass --pass late
diff --git a/.github/workflows/lintcheck.yml b/.github/workflows/lintcheck.yml
index 6a5139b6dc0bb..3cbda0b382436 100644
--- a/.github/workflows/lintcheck.yml
+++ b/.github/workflows/lintcheck.yml
@@ -58,7 +58,7 @@ jobs:
 
     - name: Run lintcheck
       if: steps.cache-json.outputs.cache-hit != 'true'
-      run: ./target/debug/lintcheck --format json --warn-all --crates-toml ./lintcheck/ci_crates.toml
+      run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml
 
     - name: Upload base JSON
       uses: actions/upload-artifact@v4
@@ -86,7 +86,7 @@ jobs:
       run: cargo build --manifest-path=lintcheck/Cargo.toml
 
     - name: Run lintcheck
-      run: ./target/debug/lintcheck --format json --warn-all --crates-toml ./lintcheck/ci_crates.toml
+      run: ./target/debug/lintcheck --format json --all-lints --crates-toml ./lintcheck/ci_crates.toml
 
     - name: Upload head JSON
       uses: actions/upload-artifact@v4
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 60c03b03d9be3..fddc2fd994e87 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5830,6 +5830,7 @@ Released 2018-09-13
 [`result_unit_err`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unit_err
 [`result_unwrap_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#result_unwrap_used
 [`return_self_not_must_use`]: https://rust-lang.github.io/rust-clippy/master/index.html#return_self_not_must_use
+[`reverse_range_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#reverse_range_loop
 [`reversed_empty_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#reversed_empty_ranges
 [`same_functions_in_if_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_functions_in_if_condition
 [`same_item_push`]: https://rust-lang.github.io/rust-clippy/master/index.html#same_item_push
@@ -5998,6 +5999,7 @@ Released 2018-09-13
 [`unused_io_amount`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_io_amount
 [`unused_label`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_label
 [`unused_peekable`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_peekable
+[`unused_result_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_result_ok
 [`unused_rounding`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_rounding
 [`unused_self`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_self
 [`unused_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#unused_unit
diff --git a/Cargo.toml b/Cargo.toml
index bb4dc97e748e3..78409c7a09e29 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -30,7 +30,7 @@ color-print = "0.3.4"
 anstream = "0.6.0"
 
 [dev-dependencies]
-ui_test = "0.24"
+ui_test = "0.25"
 regex = "1.5.5"
 toml = "0.7.3"
 walkdir = "2.3"
@@ -51,7 +51,6 @@ tokio = { version = "1", features = ["io-util"] }
 rustc_tools_util = "0.3.0"
 
 [features]
-deny-warnings = ["clippy_lints/deny-warnings"]
 integration = ["tempfile"]
 internal = ["clippy_lints/internal", "tempfile"]
 
diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md
index fb717a2c166d4..e3d550b146629 100644
--- a/book/src/lint_configuration.md
+++ b/book/src/lint_configuration.md
@@ -364,7 +364,7 @@ Suppress lints whenever the suggested change would cause breakage for other crat
 
 
 ## `await-holding-invalid-types`
-
+The list of types which may not be held across an await point.
 
 **Default Value:** `[]`
 
@@ -668,6 +668,8 @@ crate. For example, `pub(crate)` items.
 ## `msrv`
 The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml`
 
+**Default Value:** `current version`
+
 ---
 **Affected lints:**
 * [`allow_attributes`](https://rust-lang.github.io/rust-clippy/master/index.html#allow_attributes)
@@ -862,6 +864,8 @@ The maximum number of lines a function or method can have
 The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by
 reference. By default there is no limit
 
+**Default Value:** `target_pointer_width * 2`
+
 ---
 **Affected lints:**
 * [`trivially_copy_pass_by_ref`](https://rust-lang.github.io/rust-clippy/master/index.html#trivially_copy_pass_by_ref)
diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml
index e1b2edc8a6ff9..d5b28e2532371 100644
--- a/clippy_config/Cargo.toml
+++ b/clippy_config/Cargo.toml
@@ -6,6 +6,7 @@ edition = "2021"
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
 
 [dependencies]
+itertools = "0.12"
 rustc-semver = "1.1"
 serde = { version = "1.0", features = ["derive"] }
 toml = "0.7.3"
@@ -13,9 +14,6 @@ toml = "0.7.3"
 [dev-dependencies]
 walkdir = "2.3"
 
-[features]
-deny-warnings = []
-
 [package.metadata.rust-analyzer]
 # This crate uses #[feature(rustc_private)]
 rustc_private = true
diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs
index 63140a36875da..4c2a8255d6b65 100644
--- a/clippy_config/src/conf.rs
+++ b/clippy_config/src/conf.rs
@@ -123,7 +123,8 @@ macro_rules! define_Conf {
         $(#[doc = $doc:literal])+
         $(#[conf_deprecated($dep:literal, $new_conf:ident)])?
         $(#[default_text = $default_text:expr])?
-        ($name:ident: $ty:ty = $default:expr),
+        $(#[lints($($for_lints:ident),* $(,)?)])?
+        $name:ident: $ty:ty = $default:expr,
     )*) => {
         /// Clippy lint configuration
         pub struct Conf {
@@ -201,29 +202,128 @@ macro_rules! define_Conf {
         }
 
         pub fn get_configuration_metadata() -> Vec<ClippyConfiguration> {
-            let mut sorted = vec![
-                $(
-                    {
-                        let deprecation_reason = wrap_option!($($dep)?);
-
-                        ClippyConfiguration::new(
-                            stringify!($name),
-                            default_text!(defaults::$name() $(, $default_text)?),
-                            concat!($($doc, '\n',)*),
-                            deprecation_reason,
-                        )
-                    },
-                )+
-            ];
-            sorted.sort_by(|a, b| a.name.cmp(&b.name));
-            sorted
+            vec![$(
+                ClippyConfiguration {
+                    name: stringify!($name).replace('_', "-"),
+                    default: default_text!(defaults::$name() $(, $default_text)?),
+                    lints: &[$($(stringify!($for_lints)),*)?],
+                    doc: concat!($($doc, '\n',)*),
+                    deprecation_reason: wrap_option!($($dep)?)
+                },
+            )*]
         }
     };
 }
 
 define_Conf! {
-    /// Lint: ARITHMETIC_SIDE_EFFECTS.
+    /// Which crates to allow absolute paths from
+    #[lints(absolute_paths)]
+    absolute_paths_allowed_crates: FxHashSet<String> = FxHashSet::default(),
+    /// The maximum number of segments a path can have before being linted, anything above this will
+    /// be linted.
+    #[lints(absolute_paths)]
+    absolute_paths_max_segments: u64 = 2,
+    /// Whether to accept a safety comment to be placed above the attributes for the `unsafe` block
+    #[lints(undocumented_unsafe_blocks)]
+    accept_comment_above_attributes: bool = true,
+    /// Whether to accept a safety comment to be placed above the statement containing the `unsafe` block
+    #[lints(undocumented_unsafe_blocks)]
+    accept_comment_above_statement: bool = true,
+    /// Don't lint when comparing the result of a modulo operation to zero.
+    #[lints(modulo_arithmetic)]
+    allow_comparison_to_zero: bool = true,
+    /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]`
+    #[lints(dbg_macro)]
+    allow_dbg_in_tests: bool = false,
+    /// Whether `expect` should be allowed in test functions or `#[cfg(test)]`
+    #[lints(expect_used)]
+    allow_expect_in_tests: bool = false,
+    /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)`
+    #[lints(uninlined_format_args)]
+    allow_mixed_uninlined_format_args: bool = true,
+    /// Whether to allow `r#""#` when `r""` can be used
+    #[lints(unnecessary_raw_string_hashes)]
+    allow_one_hash_in_raw_strings: bool = false,
+    /// Whether `panic` should be allowed in test functions or `#[cfg(test)]`
+    #[lints(panic)]
+    allow_panic_in_tests: bool = false,
+    /// Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]`
+    #[lints(print_stderr, print_stdout)]
+    allow_print_in_tests: bool = false,
+    /// Whether to allow module inception if it's not public.
+    #[lints(module_inception)]
+    allow_private_module_inception: bool = false,
+    /// List of trait paths to ignore when checking renamed function parameters.
+    ///
+    /// #### Example
+    ///
+    /// ```toml
+    /// allow-renamed-params-for = [ "std::convert::From" ]
+    /// ```
+    ///
+    /// #### Noteworthy
+    ///
+    /// - By default, the following traits are ignored: `From`, `TryFrom`, `FromStr`
+    /// - `".."` can be used as part of the list to indicate that the configured values should be appended to the
+    /// default configuration of Clippy. By default, any configuration will replace the default value.
+    #[lints(renamed_function_params)]
+    allow_renamed_params_for: Vec<String> =
+        DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect(),
+    /// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
+    #[lints(unwrap_used)]
+    allow_unwrap_in_tests: bool = false,
+    /// Whether `useless_vec` should ignore test functions or `#[cfg(test)]`
+    #[lints(useless_vec)]
+    allow_useless_vec_in_tests: bool = false,
+    /// Additional dotfiles (files or directories starting with a dot) to allow
+    #[lints(path_ends_with_ext)]
+    allowed_dotfiles: Vec<String> = Vec::default(),
+    /// A list of crate names to allow duplicates of
+    #[lints(multiple_crate_versions)]
+    allowed_duplicate_crates: FxHashSet<String> = FxHashSet::default(),
+    /// Allowed names below the minimum allowed characters. The value `".."` can be used as part of
+    /// the list to indicate, that the configured values should be appended to the default
+    /// configuration of Clippy. By default, any configuration will replace the default value.
+    #[lints(min_ident_chars)]
+    allowed_idents_below_min_chars: FxHashSet<String> =
+        DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string).collect(),
+    /// List of prefixes to allow when determining whether an item's name ends with the module's name.
+    /// If the rest of an item's name is an allowed prefix (e.g. item `ToFoo` or `to_foo` in module `foo`),
+    /// then don't emit a warning.
+    ///
+    /// #### Example
+    ///
+    /// ```toml
+    /// allowed-prefixes = [ "to", "from" ]
+    /// ```
+    ///
+    /// #### Noteworthy
+    ///
+    /// - By default, the following prefixes are allowed: `to`, `as`, `into`, `from`, `try_into` and `try_from`
+    /// - PascalCase variant is included automatically for each snake_case variant (e.g. if `try_into` is included,
+    ///   `TryInto` will also be included)
+    /// - Use `".."` as part of the list to indicate that the configured values should be appended to the
+    /// default configuration of Clippy. By default, any configuration will replace the default value
+    #[lints(module_name_repetitions)]
+    allowed_prefixes: Vec<String> = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect(),
+    /// The list of unicode scripts allowed to be used in the scope.
+    #[lints(disallowed_script_idents)]
+    allowed_scripts: Vec<String> = vec!["Latin".to_string()],
+    /// List of path segments allowed to have wildcard imports.
     ///
+    /// #### Example
+    ///
+    /// ```toml
+    /// allowed-wildcard-imports = [ "utils", "common" ]
+    /// ```
+    ///
+    /// #### Noteworthy
+    ///
+    /// 1. This configuration has no effects if used with `warn_on_all_wildcard_imports = true`.
+    /// 2. Paths with any segment that containing the word 'prelude'
+    /// are already allowed by default.
+    #[lints(wildcard_imports)]
+    allowed_wildcard_imports: FxHashSet<String> = FxHashSet::default(),
     /// Suppress checking of the passed type names in all types of operations.
     ///
     /// If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead.
@@ -238,9 +338,8 @@ define_Conf! {
     ///
     /// A type, say `SomeType`, listed in this configuration has the same behavior of
     /// `["SomeType" , "*"], ["*", "SomeType"]` in `arithmetic_side_effects_allowed_binary`.
-    (arithmetic_side_effects_allowed: Vec<String> = <_>::default()),
-    /// Lint: ARITHMETIC_SIDE_EFFECTS.
-    ///
+    #[lints(arithmetic_side_effects)]
+    arithmetic_side_effects_allowed: Vec<String> = <_>::default(),
     /// Suppress checking of the passed type pair names in binary operations like addition or
     /// multiplication.
     ///
@@ -255,9 +354,8 @@ define_Conf! {
     /// ```toml
     /// arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]]
     /// ```
-    (arithmetic_side_effects_allowed_binary: Vec<[String; 2]> = <_>::default()),
-    /// Lint: ARITHMETIC_SIDE_EFFECTS.
-    ///
+    #[lints(arithmetic_side_effects)]
+    arithmetic_side_effects_allowed_binary: Vec<[String; 2]> = <_>::default(),
     /// Suppress checking of the passed type names in unary operations like "negation" (`-`).
     ///
     /// #### Example
@@ -265,298 +363,78 @@ define_Conf! {
     /// ```toml
     /// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]
     /// ```
-    (arithmetic_side_effects_allowed_unary: Vec<String> = <_>::default()),
-    /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX, UNNECESSARY_BOX_RETURNS, SINGLE_CALL_FN, NEEDLESS_PASS_BY_REF_MUT.
-    ///
+    #[lints(arithmetic_side_effects)]
+    arithmetic_side_effects_allowed_unary: Vec<String> = <_>::default(),
+    /// The maximum allowed size for arrays on the stack
+    #[lints(large_const_arrays, large_stack_arrays)]
+    array_size_threshold: u64 = 512_000,
     /// Suppress lints whenever the suggested change would cause breakage for other crates.
-    (avoid_breaking_exported_api: bool = true),
-    /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS, ASSIGNING_CLONES, LEGACY_NUMERIC_CONSTANTS, MANUAL_PATTERN_CHAR_COMPARISON, ALLOW_ATTRIBUTES, ALLOW_ATTRIBUTES_WITHOUT_REASON, COLLAPSIBLE_MATCH.
-    ///
-    /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml`
-    #[default_text = ""]
-    (msrv: Msrv = Msrv::empty()),
+    #[lints(
+        box_collection,
+        enum_variant_names,
+        large_types_passed_by_value,
+        linkedlist,
+        needless_pass_by_ref_mut,
+        option_option,
+        rc_buffer,
+        rc_mutex,
+        redundant_allocation,
+        single_call_fn,
+        trivially_copy_pass_by_ref,
+        unnecessary_box_returns,
+        unnecessary_wraps,
+        unused_self,
+        upper_case_acronyms,
+        vec_box,
+        wrong_self_convention,
+    )]
+    avoid_breaking_exported_api: bool = true,
+    /// The list of types which may not be held across an await point.
+    #[lints(await_holding_invalid_type)]
+    await_holding_invalid_types: Vec<DisallowedPath> = Vec::new(),
     /// DEPRECATED LINT: BLACKLISTED_NAME.
     ///
     /// Use the Disallowed Names lint instead
     #[conf_deprecated("Please use `disallowed-names` instead", disallowed_names)]
-    (blacklisted_names: Vec<String> = Vec::new()),
-    /// Lint: COGNITIVE_COMPLEXITY.
-    ///
+    blacklisted_names: Vec<String> = Vec::new(),
+    /// For internal testing only, ignores the current `publish` settings in the Cargo manifest.
+    #[lints(cargo_common_metadata)]
+    cargo_ignore_publish: bool = false,
+    /// Whether to also run the listed lints on private items.
+    #[lints(missing_errors_doc, missing_panics_doc, missing_safety_doc, unnecessary_safety_doc)]
+    check_private_items: bool = false,
     /// The maximum cognitive complexity a function can have
-    (cognitive_complexity_threshold: u64 = 25),
-    /// Lint: EXCESSIVE_NESTING.
-    ///
-    /// The maximum amount of nesting a block can reside in
-    (excessive_nesting_threshold: u64 = 0),
+    #[lints(cognitive_complexity)]
+    cognitive_complexity_threshold: u64 = 25,
     /// DEPRECATED LINT: CYCLOMATIC_COMPLEXITY.
     ///
     /// Use the Cognitive Complexity lint instead.
     #[conf_deprecated("Please use `cognitive-complexity-threshold` instead", cognitive_complexity_threshold)]
-    (cyclomatic_complexity_threshold: u64 = 25),
-    /// Lint: DISALLOWED_NAMES.
-    ///
+    cyclomatic_complexity_threshold: u64 = 25,
+    /// The list of disallowed macros, written as fully qualified paths.
+    #[lints(disallowed_macros)]
+    disallowed_macros: Vec<DisallowedPath> = Vec::new(),
+    /// The list of disallowed methods, written as fully qualified paths.
+    #[lints(disallowed_methods)]
+    disallowed_methods: Vec<DisallowedPath> = Vec::new(),
     /// The list of disallowed names to lint about. NB: `bar` is not here since it has legitimate uses. The value
     /// `".."` can be used as part of the list to indicate that the configured values should be appended to the
     /// default configuration of Clippy. By default, any configuration will replace the default value.
-    (disallowed_names: Vec<String> = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect()),
-    /// Lint: SEMICOLON_INSIDE_BLOCK.
-    ///
-    /// Whether to lint only if it's multiline.
-    (semicolon_inside_block_ignore_singleline: bool = false),
-    /// Lint: SEMICOLON_OUTSIDE_BLOCK.
-    ///
-    /// Whether to lint only if it's singleline.
-    (semicolon_outside_block_ignore_multiline: bool = false),
-    /// Lint: DOC_MARKDOWN.
-    ///
+    #[lints(disallowed_names)]
+    disallowed_names: Vec<String> = DEFAULT_DISALLOWED_NAMES.iter().map(ToString::to_string).collect(),
+    /// The list of disallowed types, written as fully qualified paths.
+    #[lints(disallowed_types)]
+    disallowed_types: Vec<DisallowedPath> = Vec::new(),
     /// The list of words this lint should not consider as identifiers needing ticks. The value
     /// `".."` can be used as part of the list to indicate, that the configured values should be appended to the
     /// default configuration of Clippy. By default, any configuration will replace the default value. For example:
     /// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
     /// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
-    (doc_valid_idents: FxHashSet<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect()),
-    /// Lint: TOO_MANY_ARGUMENTS.
-    ///
-    /// The maximum number of argument a function or method can have
-    (too_many_arguments_threshold: u64 = 7),
-    /// Lint: TYPE_COMPLEXITY.
-    ///
-    /// The maximum complexity a type can have
-    (type_complexity_threshold: u64 = 250),
-    /// Lint: MANY_SINGLE_CHAR_NAMES.
-    ///
-    /// The maximum number of single char bindings a scope may have
-    (single_char_binding_names_threshold: u64 = 4),
-    /// Lint: BOXED_LOCAL, USELESS_VEC.
-    ///
-    /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
-    (too_large_for_stack: u64 = 200),
-    /// Lint: ENUM_VARIANT_NAMES.
-    ///
-    /// The minimum number of enum variants for the lints about variant names to trigger
-    (enum_variant_name_threshold: u64 = 3),
-    /// Lint: STRUCT_FIELD_NAMES.
-    ///
-    /// The minimum number of struct fields for the lints about field names to trigger
-    (struct_field_name_threshold: u64 = 3),
-    /// Lint: LARGE_ENUM_VARIANT.
-    ///
-    /// The maximum size of an enum's variant to avoid box suggestion
-    (enum_variant_size_threshold: u64 = 200),
-    /// Lint: VERBOSE_BIT_MASK.
-    ///
-    /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
-    (verbose_bit_mask_threshold: u64 = 1),
-    /// Lint: DECIMAL_LITERAL_REPRESENTATION.
-    ///
-    /// The lower bound for linting decimal literals
-    (literal_representation_threshold: u64 = 16384),
-    /// Lint: TRIVIALLY_COPY_PASS_BY_REF.
-    ///
-    /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by
-    /// reference. By default there is no limit
-    #[default_text = ""]
-    (trivial_copy_size_limit: Option<u64> = None),
-    /// Lint: LARGE_TYPES_PASSED_BY_VALUE.
-    ///
-    /// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
-    (pass_by_value_size_limit: u64 = 256),
-    /// Lint: TOO_MANY_LINES.
-    ///
-    /// The maximum number of lines a function or method can have
-    (too_many_lines_threshold: u64 = 100),
-    /// Lint: LARGE_STACK_ARRAYS, LARGE_CONST_ARRAYS.
-    ///
-    /// The maximum allowed size for arrays on the stack
-    (array_size_threshold: u64 = 512_000),
-    /// Lint: LARGE_STACK_FRAMES.
-    ///
-    /// The maximum allowed stack size for functions in bytes
-    (stack_size_threshold: u64 = 512_000),
-    /// Lint: VEC_BOX.
-    ///
-    /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed
-    (vec_box_size_threshold: u64 = 4096),
-    /// Lint: TYPE_REPETITION_IN_BOUNDS.
-    ///
-    /// The maximum number of bounds a trait can have to be linted
-    (max_trait_bounds: u64 = 3),
-    /// Lint: STRUCT_EXCESSIVE_BOOLS.
-    ///
-    /// The maximum number of bool fields a struct can have
-    (max_struct_bools: u64 = 3),
-    /// Lint: FN_PARAMS_EXCESSIVE_BOOLS.
-    ///
-    /// The maximum number of bool parameters a function can have
-    (max_fn_params_bools: u64 = 3),
-    /// Lint: WILDCARD_IMPORTS.
-    ///
-    /// Whether to allow certain wildcard imports (prelude, super in tests).
-    (warn_on_all_wildcard_imports: bool = false),
-    /// Lint: DISALLOWED_MACROS.
-    ///
-    /// The list of disallowed macros, written as fully qualified paths.
-    (disallowed_macros: Vec<DisallowedPath> = Vec::new()),
-    /// Lint: DISALLOWED_METHODS.
-    ///
-    /// The list of disallowed methods, written as fully qualified paths.
-    (disallowed_methods: Vec<DisallowedPath> = Vec::new()),
-    /// Lint: DISALLOWED_TYPES.
-    ///
-    /// The list of disallowed types, written as fully qualified paths.
-    (disallowed_types: Vec<DisallowedPath> = Vec::new()),
-    /// Lint: UNREADABLE_LITERAL.
-    ///
-    /// Should the fraction of a decimal be linted to include separators.
-    (unreadable_literal_lint_fractions: bool = true),
-    /// Lint: UPPER_CASE_ACRONYMS.
-    ///
-    /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other
-    (upper_case_acronyms_aggressive: bool = false),
-    /// Lint: MANUAL_LET_ELSE.
-    ///
-    /// Whether the matches should be considered by the lint, and whether there should
-    /// be filtering for common types.
-    (matches_for_let_else: MatchLintBehaviour = MatchLintBehaviour::WellKnownTypes),
-    /// Lint: CARGO_COMMON_METADATA.
-    ///
-    /// For internal testing only, ignores the current `publish` settings in the Cargo manifest.
-    (cargo_ignore_publish: bool = false),
-    /// Lint: NONSTANDARD_MACRO_BRACES.
-    ///
-    /// Enforce the named macros always use the braces specified.
-    ///
-    /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro
-    /// could be used with a full path two `MacroMatcher`s have to be added one with the full path
-    /// `crate_name::macro_name` and one with just the macro name.
-    (standard_macro_braces: Vec<MacroMatcher> = Vec::new()),
-    /// Lint: MISSING_ENFORCED_IMPORT_RENAMES.
-    ///
-    /// The list of imports to always rename, a fully qualified path followed by the rename.
-    (enforced_import_renames: Vec<Rename> = Vec::new()),
-    /// Lint: DISALLOWED_SCRIPT_IDENTS.
-    ///
-    /// The list of unicode scripts allowed to be used in the scope.
-    (allowed_scripts: Vec<String> = vec!["Latin".to_string()]),
-    /// Lint: NON_SEND_FIELDS_IN_SEND_TY.
-    ///
+    #[lints(doc_markdown)]
+    doc_valid_idents: FxHashSet<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect(),
     /// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
-    (enable_raw_pointer_heuristic_for_send: bool = true),
-    /// Lint: INDEX_REFUTABLE_SLICE.
-    ///
-    /// When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in
-    /// the slice pattern that is suggested. If more elements are necessary, the lint is suppressed.
-    /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
-    (max_suggested_slice_pattern_length: u64 = 3),
-    /// Lint: AWAIT_HOLDING_INVALID_TYPE.
-    (await_holding_invalid_types: Vec<DisallowedPath> = Vec::new()),
-    /// Lint: LARGE_INCLUDE_FILE.
-    ///
-    /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
-    (max_include_file_size: u64 = 1_000_000),
-    /// Lint: EXPECT_USED.
-    ///
-    /// Whether `expect` should be allowed in test functions or `#[cfg(test)]`
-    (allow_expect_in_tests: bool = false),
-    /// Lint: UNWRAP_USED.
-    ///
-    /// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
-    (allow_unwrap_in_tests: bool = false),
-    /// Lint: PANIC.
-    ///
-    /// Whether `panic` should be allowed in test functions or `#[cfg(test)]`
-    (allow_panic_in_tests: bool = false),
-    /// Lint: DBG_MACRO.
-    ///
-    /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]`
-    (allow_dbg_in_tests: bool = false),
-    /// Lint: PRINT_STDOUT, PRINT_STDERR.
-    ///
-    /// Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]`
-    (allow_print_in_tests: bool = false),
-    /// Lint: USELESS_VEC.
-    ///
-    /// Whether `useless_vec` should ignore test functions or `#[cfg(test)]`
-    (allow_useless_vec_in_tests: bool = false),
-    /// Lint: RESULT_LARGE_ERR.
-    ///
-    /// The maximum size of the `Err`-variant in a `Result` returned from a function
-    (large_error_threshold: u64 = 128),
-    /// Lint: MUTABLE_KEY_TYPE, IFS_SAME_COND, BORROW_INTERIOR_MUTABLE_CONST, DECLARE_INTERIOR_MUTABLE_CONST.
-    ///
-    /// A list of paths to types that should be treated as if they do not contain interior mutability
-    (ignore_interior_mutability: Vec<String> = Vec::from(["bytes::Bytes".into()])),
-    /// Lint: UNINLINED_FORMAT_ARGS.
-    ///
-    /// Whether to allow mixed uninlined format args, e.g. `format!("{} {}", a, foo.bar)`
-    (allow_mixed_uninlined_format_args: bool = true),
-    /// Lint: INDEXING_SLICING.
-    ///
-    /// Whether to suppress a restriction lint in constant code. In same
-    /// cases the restructured operation might not be unavoidable, as the
-    /// suggested counterparts are unavailable in constant code. This
-    /// configuration will cause restriction lints to trigger even
-    /// if no suggestion can be made.
-    (suppress_restriction_lint_in_const: bool = false),
-    /// Lint: MISSING_DOCS_IN_PRIVATE_ITEMS.
-    ///
-    /// Whether to **only** check for missing documentation in items visible within the current
-    /// crate. For example, `pub(crate)` items.
-    (missing_docs_in_crate_items: bool = false),
-    /// Lint: LARGE_FUTURES.
-    ///
-    /// The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint
-    (future_size_threshold: u64 = 16 * 1024),
-    /// Lint: UNNECESSARY_BOX_RETURNS.
-    ///
-    /// The byte size a `T` in `Box<T>` can have, below which it triggers the `clippy::unnecessary_box` lint
-    (unnecessary_box_size: u64 = 128),
-    /// Lint: MODULE_INCEPTION.
-    ///
-    /// Whether to allow module inception if it's not public.
-    (allow_private_module_inception: bool = false),
-    /// Lint: MIN_IDENT_CHARS.
-    ///
-    /// Allowed names below the minimum allowed characters. The value `".."` can be used as part of
-    /// the list to indicate, that the configured values should be appended to the default
-    /// configuration of Clippy. By default, any configuration will replace the default value.
-    (allowed_idents_below_min_chars: FxHashSet<String> =
-        DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string).collect()),
-    /// Lint: MIN_IDENT_CHARS.
-    ///
-    /// Minimum chars an ident can have, anything below or equal to this will be linted.
-    (min_ident_chars_threshold: u64 = 1),
-    /// Lint: UNDOCUMENTED_UNSAFE_BLOCKS.
-    ///
-    /// Whether to accept a safety comment to be placed above the statement containing the `unsafe` block
-    (accept_comment_above_statement: bool = true),
-    /// Lint: UNDOCUMENTED_UNSAFE_BLOCKS.
-    ///
-    /// Whether to accept a safety comment to be placed above the attributes for the `unsafe` block
-    (accept_comment_above_attributes: bool = true),
-    /// Lint: UNNECESSARY_RAW_STRING_HASHES.
-    ///
-    /// Whether to allow `r#""#` when `r""` can be used
-    (allow_one_hash_in_raw_strings: bool = false),
-    /// Lint: ABSOLUTE_PATHS.
-    ///
-    /// The maximum number of segments a path can have before being linted, anything above this will
-    /// be linted.
-    (absolute_paths_max_segments: u64 = 2),
-    /// Lint: ABSOLUTE_PATHS.
-    ///
-    /// Which crates to allow absolute paths from
-    (absolute_paths_allowed_crates: FxHashSet<String> = FxHashSet::default()),
-    /// Lint: PATH_ENDS_WITH_EXT.
-    ///
-    /// Additional dotfiles (files or directories starting with a dot) to allow
-    (allowed_dotfiles: Vec<String> = Vec::default()),
-    /// Lint: MULTIPLE_CRATE_VERSIONS.
-    ///
-    /// A list of crate names to allow duplicates of
-    (allowed_duplicate_crates: FxHashSet<String> = FxHashSet::default()),
-    /// Lint: EXPLICIT_ITER_LOOP.
-    ///
+    #[lints(non_send_fields_in_send_ty)]
+    enable_raw_pointer_heuristic_for_send: bool = true,
     /// Whether to recommend using implicit into iter for reborrowed values.
     ///
     /// #### Example
@@ -574,77 +452,195 @@ define_Conf! {
     /// for _ in &*rmvec {}
     /// for _ in &mut *rmvec {}
     /// ```
-    (enforce_iter_loop_reborrow: bool = false),
-    /// Lint: MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC, MISSING_PANICS_DOC, MISSING_ERRORS_DOC.
-    ///
-    /// Whether to also run the listed lints on private items.
-    (check_private_items: bool = false),
-    /// Lint: PUB_UNDERSCORE_FIELDS.
-    ///
+    #[lints(explicit_iter_loop)]
+    enforce_iter_loop_reborrow: bool = false,
+    /// The list of imports to always rename, a fully qualified path followed by the rename.
+    #[lints(missing_enforced_import_renames)]
+    enforced_import_renames: Vec<Rename> = Vec::new(),
+    /// The minimum number of enum variants for the lints about variant names to trigger
+    #[lints(enum_variant_names)]
+    enum_variant_name_threshold: u64 = 3,
+    /// The maximum size of an enum's variant to avoid box suggestion
+    #[lints(large_enum_variant)]
+    enum_variant_size_threshold: u64 = 200,
+    /// The maximum amount of nesting a block can reside in
+    #[lints(excessive_nesting)]
+    excessive_nesting_threshold: u64 = 0,
+    /// The maximum byte size a `Future` can have, before it triggers the `clippy::large_futures` lint
+    #[lints(large_futures)]
+    future_size_threshold: u64 = 16 * 1024,
+    /// A list of paths to types that should be treated as if they do not contain interior mutability
+    #[lints(borrow_interior_mutable_const, declare_interior_mutable_const, ifs_same_cond, mutable_key_type)]
+    ignore_interior_mutability: Vec<String> = Vec::from(["bytes::Bytes".into()]),
+    /// The maximum size of the `Err`-variant in a `Result` returned from a function
+    #[lints(result_large_err)]
+    large_error_threshold: u64 = 128,
+    /// The lower bound for linting decimal literals
+    #[lints(decimal_literal_representation)]
+    literal_representation_threshold: u64 = 16384,
+    /// Whether the matches should be considered by the lint, and whether there should
+    /// be filtering for common types.
+    #[lints(manual_let_else)]
+    matches_for_let_else: MatchLintBehaviour = MatchLintBehaviour::WellKnownTypes,
+    /// The maximum number of bool parameters a function can have
+    #[lints(fn_params_excessive_bools)]
+    max_fn_params_bools: u64 = 3,
+    /// The maximum size of a file included via `include_bytes!()` or `include_str!()`, in bytes
+    #[lints(large_include_file)]
+    max_include_file_size: u64 = 1_000_000,
+    /// The maximum number of bool fields a struct can have
+    #[lints(struct_excessive_bools)]
+    max_struct_bools: u64 = 3,
+    /// When Clippy suggests using a slice pattern, this is the maximum number of elements allowed in
+    /// the slice pattern that is suggested. If more elements are necessary, the lint is suppressed.
+    /// For example, `[_, _, _, e, ..]` is a slice pattern with 4 elements.
+    #[lints(index_refutable_slice)]
+    max_suggested_slice_pattern_length: u64 = 3,
+    /// The maximum number of bounds a trait can have to be linted
+    #[lints(type_repetition_in_bounds)]
+    max_trait_bounds: u64 = 3,
+    /// Minimum chars an ident can have, anything below or equal to this will be linted.
+    #[lints(min_ident_chars)]
+    min_ident_chars_threshold: u64 = 1,
+    /// Whether to **only** check for missing documentation in items visible within the current
+    /// crate. For example, `pub(crate)` items.
+    #[lints(missing_docs_in_private_items)]
+    missing_docs_in_crate_items: bool = false,
+    /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml`
+    #[default_text = "current version"]
+    #[lints(
+        allow_attributes,
+        allow_attributes_without_reason,
+        almost_complete_range,
+        approx_constant,
+        assigning_clones,
+        borrow_as_ptr,
+        cast_abs_to_unsigned,
+        checked_conversions,
+        cloned_instead_of_copied,
+        collapsible_match,
+        collapsible_str_replace,
+        deprecated_cfg_attr,
+        derivable_impls,
+        err_expect,
+        filter_map_next,
+        from_over_into,
+        if_then_some_else_none,
+        index_refutable_slice,
+        iter_kv_map,
+        legacy_numeric_constants,
+        manual_bits,
+        manual_c_str_literals,
+        manual_clamp,
+        manual_hash_one,
+        manual_is_ascii_check,
+        manual_let_else,
+        manual_non_exhaustive,
+        manual_pattern_char_comparison,
+        manual_range_contains,
+        manual_rem_euclid,
+        manual_retain,
+        manual_split_once,
+        manual_str_repeat,
+        manual_strip,
+        manual_try_fold,
+        map_clone,
+        map_unwrap_or,
+        match_like_matches_macro,
+        mem_replace_with_default,
+        missing_const_for_fn,
+        needless_borrow,
+        option_as_ref_deref,
+        option_map_unwrap_or,
+        ptr_as_ptr,
+        redundant_field_names,
+        redundant_static_lifetimes,
+        seek_from_current,
+        seek_rewind,
+        transmute_ptr_to_ref,
+        tuple_array_conversions,
+        type_repetition_in_bounds,
+        unchecked_duration_subtraction,
+        uninlined_format_args,
+        unnecessary_lazy_evaluations,
+        unnested_or_patterns,
+        use_self,
+    )]
+    msrv: Msrv = Msrv::empty(),
+    /// The minimum size (in bytes) to consider a type for passing by reference instead of by value.
+    #[lints(large_types_passed_by_value)]
+    pass_by_value_size_limit: u64 = 256,
     /// Lint "public" fields in a struct that are prefixed with an underscore based on their
     /// exported visibility, or whether they are marked as "pub".
-    (pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported),
-    /// Lint: MODULO_ARITHMETIC.
-    ///
-    /// Don't lint when comparing the result of a modulo operation to zero.
-    (allow_comparison_to_zero: bool = true),
-    /// Lint: WILDCARD_IMPORTS.
-    ///
-    /// List of path segments allowed to have wildcard imports.
-    ///
-    /// #### Example
-    ///
-    /// ```toml
-    /// allowed-wildcard-imports = [ "utils", "common" ]
-    /// ```
-    ///
-    /// #### Noteworthy
-    ///
-    /// 1. This configuration has no effects if used with `warn_on_all_wildcard_imports = true`.
-    /// 2. Paths with any segment that containing the word 'prelude'
-    /// are already allowed by default.
-    (allowed_wildcard_imports: FxHashSet<String> = FxHashSet::default()),
-    /// Lint: MODULE_NAME_REPETITIONS.
-    ///
-    /// List of prefixes to allow when determining whether an item's name ends with the module's name.
-    /// If the rest of an item's name is an allowed prefix (e.g. item `ToFoo` or `to_foo` in module `foo`),
-    /// then don't emit a warning.
-    ///
-    /// #### Example
-    ///
-    /// ```toml
-    /// allowed-prefixes = [ "to", "from" ]
-    /// ```
-    ///
-    /// #### Noteworthy
-    ///
-    /// - By default, the following prefixes are allowed: `to`, `as`, `into`, `from`, `try_into` and `try_from`
-    /// - PascalCase variant is included automatically for each snake_case variant (e.g. if `try_into` is included,
-    ///   `TryInto` will also be included)
-    /// - Use `".."` as part of the list to indicate that the configured values should be appended to the
-    /// default configuration of Clippy. By default, any configuration will replace the default value
-    (allowed_prefixes: Vec<String> = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect()),
-    /// Lint: RENAMED_FUNCTION_PARAMS.
-    ///
-    /// List of trait paths to ignore when checking renamed function parameters.
-    ///
-    /// #### Example
-    ///
-    /// ```toml
-    /// allow-renamed-params-for = [ "std::convert::From" ]
-    /// ```
-    ///
-    /// #### Noteworthy
-    ///
-    /// - By default, the following traits are ignored: `From`, `TryFrom`, `FromStr`
-    /// - `".."` can be used as part of the list to indicate that the configured values should be appended to the
-    /// default configuration of Clippy. By default, any configuration will replace the default value.
-    (allow_renamed_params_for: Vec<String> =
-        DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect()),
-    /// Lint: MACRO_METAVARS_IN_UNSAFE.
+    #[lints(pub_underscore_fields)]
+    pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported,
+    /// Whether to lint only if it's multiline.
+    #[lints(semicolon_inside_block)]
+    semicolon_inside_block_ignore_singleline: bool = false,
+    /// Whether to lint only if it's singleline.
+    #[lints(semicolon_outside_block)]
+    semicolon_outside_block_ignore_multiline: bool = false,
+    /// The maximum number of single char bindings a scope may have
+    #[lints(many_single_char_names)]
+    single_char_binding_names_threshold: u64 = 4,
+    /// The maximum allowed stack size for functions in bytes
+    #[lints(large_stack_frames)]
+    stack_size_threshold: u64 = 512_000,
+    /// Enforce the named macros always use the braces specified.
     ///
+    /// A `MacroMatcher` can be added like so `{ name = "macro_name", brace = "(" }`. If the macro
+    /// could be used with a full path two `MacroMatcher`s have to be added one with the full path
+    /// `crate_name::macro_name` and one with just the macro name.
+    #[lints(nonstandard_macro_braces)]
+    standard_macro_braces: Vec<MacroMatcher> = Vec::new(),
+    /// The minimum number of struct fields for the lints about field names to trigger
+    #[lints(struct_field_names)]
+    struct_field_name_threshold: u64 = 3,
+    /// Whether to suppress a restriction lint in constant code. In same
+    /// cases the restructured operation might not be unavoidable, as the
+    /// suggested counterparts are unavailable in constant code. This
+    /// configuration will cause restriction lints to trigger even
+    /// if no suggestion can be made.
+    #[lints(indexing_slicing)]
+    suppress_restriction_lint_in_const: bool = false,
+    /// The maximum size of objects (in bytes) that will be linted. Larger objects are ok on the heap
+    #[lints(boxed_local, useless_vec)]
+    too_large_for_stack: u64 = 200,
+    /// The maximum number of argument a function or method can have
+    #[lints(too_many_arguments)]
+    too_many_arguments_threshold: u64 = 7,
+    /// The maximum number of lines a function or method can have
+    #[lints(too_many_lines)]
+    too_many_lines_threshold: u64 = 100,
+    /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by
+    /// reference. By default there is no limit
+    #[default_text = "target_pointer_width * 2"]
+    #[lints(trivially_copy_pass_by_ref)]
+    trivial_copy_size_limit: Option<u64> = None,
+    /// The maximum complexity a type can have
+    #[lints(type_complexity)]
+    type_complexity_threshold: u64 = 250,
+    /// The byte size a `T` in `Box<T>` can have, below which it triggers the `clippy::unnecessary_box` lint
+    #[lints(unnecessary_box_returns)]
+    unnecessary_box_size: u64 = 128,
+    /// Should the fraction of a decimal be linted to include separators.
+    #[lints(unreadable_literal)]
+    unreadable_literal_lint_fractions: bool = true,
+    /// Enables verbose mode. Triggers if there is more than one uppercase char next to each other
+    #[lints(upper_case_acronyms)]
+    upper_case_acronyms_aggressive: bool = false,
+    /// The size of the boxed type in bytes, where boxing in a `Vec` is allowed
+    #[lints(vec_box)]
+    vec_box_size_threshold: u64 = 4096,
+    /// The maximum allowed size of a bit mask before suggesting to use 'trailing_zeros'
+    #[lints(verbose_bit_mask)]
+    verbose_bit_mask_threshold: u64 = 1,
+    /// Whether to allow certain wildcard imports (prelude, super in tests).
+    #[lints(wildcard_imports)]
+    warn_on_all_wildcard_imports: bool = false,
     /// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros.
-    (warn_unsafe_macro_metavars_in_private_macros: bool = false),
+    #[lints(macro_metavars_in_unsafe)]
+    warn_unsafe_macro_metavars_in_private_macros: bool = false,
 }
 
 /// Search for the configuration file.
diff --git a/clippy_config/src/lib.rs b/clippy_config/src/lib.rs
index ff7fa7241cb96..d2246f12029b3 100644
--- a/clippy_config/src/lib.rs
+++ b/clippy_config/src/lib.rs
@@ -1,5 +1,4 @@
-#![feature(rustc_private, let_chains)]
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+#![feature(rustc_private, array_windows, let_chains)]
 #![warn(
     trivial_casts,
     trivial_numeric_casts,
diff --git a/clippy_config/src/metadata.rs b/clippy_config/src/metadata.rs
index 400887185e8cf..7cbd92a9b7100 100644
--- a/clippy_config/src/metadata.rs
+++ b/clippy_config/src/metadata.rs
@@ -1,11 +1,12 @@
-use std::fmt::{self, Write};
+use itertools::Itertools;
+use std::fmt;
 
 #[derive(Debug, Clone, Default)]
 pub struct ClippyConfiguration {
     pub name: String,
     pub default: String,
-    pub lints: Vec<String>,
-    pub doc: String,
+    pub lints: &'static [&'static str],
+    pub doc: &'static str,
     pub deprecation_reason: Option<&'static str>,
 }
 
@@ -13,61 +14,23 @@ impl fmt::Display for ClippyConfiguration {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         write!(f, "- `{}`: {}", self.name, self.doc)?;
         if !self.default.is_empty() {
-            write!(f, " (default: `{}`)", self.default)?;
+            write!(f, "\n\n   (default: `{}`)", self.default)?;
         }
         Ok(())
     }
 }
 
 impl ClippyConfiguration {
-    pub fn new(
-        name: &'static str,
-        default: String,
-        doc_comment: &'static str,
-        deprecation_reason: Option<&'static str>,
-    ) -> Self {
-        let (mut lints, doc) = parse_config_field_doc(doc_comment)
-            .unwrap_or_else(|| (vec![], "[ERROR] MALFORMED DOC COMMENT".to_string()));
-
-        lints.sort();
-
-        Self {
-            name: to_kebab(name),
-            lints,
-            doc,
-            default,
-            deprecation_reason,
-        }
-    }
-
     pub fn to_markdown_paragraph(&self) -> String {
-        let mut out = format!(
-            "## `{}`\n{}\n\n",
+        format!(
+            "## `{}`\n{}\n\n**Default Value:** `{}`\n\n---\n**Affected lints:**\n{}\n\n",
             self.name,
-            self.doc
-                .lines()
-                .map(|line| line.strip_prefix("    ").unwrap_or(line))
-                .collect::<Vec<_>>()
-                .join("\n"),
-        );
-
-        if !self.default.is_empty() {
-            write!(out, "**Default Value:** `{}`\n\n", self.default).unwrap();
-        }
-
-        write!(
-            out,
-            "---\n**Affected lints:**\n{}\n\n",
-            self.lints
-                .iter()
-                .map(|name| name.to_string().split_whitespace().next().unwrap().to_string())
-                .map(|name| format!("* [`{name}`](https://rust-lang.github.io/rust-clippy/master/index.html#{name})"))
-                .collect::<Vec<_>>()
-                .join("\n"),
+            self.doc.lines().map(|x| x.strip_prefix(' ').unwrap_or(x)).join("\n"),
+            self.default,
+            self.lints.iter().format_with("\n", |name, f| f(&format_args!(
+                "* [`{name}`](https://rust-lang.github.io/rust-clippy/master/index.html#{name})"
+            ))),
         )
-        .unwrap();
-
-        out
     }
 
     pub fn to_markdown_link(&self) -> String {
@@ -75,47 +38,3 @@ impl ClippyConfiguration {
         format!("[`{}`]: {BOOK_CONFIGS_PATH}#{}", self.name, self.name)
     }
 }
-
-/// This parses the field documentation of the config struct.
-///
-/// ```rust, ignore
-/// parse_config_field_doc(cx, "Lint: LINT_NAME_1, LINT_NAME_2. Papa penguin, papa penguin")
-/// ```
-///
-/// Would yield:
-/// ```rust, ignore
-/// Some(["lint_name_1", "lint_name_2"], "Papa penguin, papa penguin")
-/// ```
-fn parse_config_field_doc(doc_comment: &str) -> Option<(Vec<String>, String)> {
-    const DOC_START: &str = " Lint: ";
-    if doc_comment.starts_with(DOC_START)
-        && let Some(split_pos) = doc_comment.find('.')
-    {
-        let mut doc_comment = doc_comment.to_string();
-        let mut documentation = doc_comment.split_off(split_pos);
-
-        // Extract lints
-        doc_comment.make_ascii_lowercase();
-        let lints: Vec<String> = doc_comment
-            .split_off(DOC_START.len())
-            .lines()
-            .next()
-            .unwrap()
-            .split(", ")
-            .map(str::to_string)
-            .collect();
-
-        // Format documentation correctly
-        // split off leading `.` from lint name list and indent for correct formatting
-        documentation = documentation.trim_start_matches('.').trim().replace("\n ", "\n    ");
-
-        Some((lints, documentation))
-    } else {
-        None
-    }
-}
-
-/// Transforms a given `snake_case_string` to a tasty `kebab-case-string`
-fn to_kebab(config_name: &str) -> String {
-    config_name.replace('_', "-")
-}
diff --git a/clippy_dev/Cargo.toml b/clippy_dev/Cargo.toml
index 4104e7d94f146..a5d72c3a559df 100644
--- a/clippy_dev/Cargo.toml
+++ b/clippy_dev/Cargo.toml
@@ -13,9 +13,6 @@ opener = "0.6"
 shell-escape = "0.1"
 walkdir = "2.3"
 
-[features]
-deny-warnings = []
-
 [package.metadata.rust-analyzer]
 # This package uses #[feature(rustc_private)]
 rustc_private = true
diff --git a/clippy_dev/src/fmt.rs b/clippy_dev/src/fmt.rs
index 2562314418172..5fc4365c6e785 100644
--- a/clippy_dev/src/fmt.rs
+++ b/clippy_dev/src/fmt.rs
@@ -1,30 +1,65 @@
 use crate::clippy_project_root;
 use itertools::Itertools;
+use rustc_lexer::{tokenize, TokenKind};
 use shell_escape::escape;
 use std::ffi::{OsStr, OsString};
-use std::path::Path;
+use std::ops::ControlFlow;
+use std::path::{Path, PathBuf};
 use std::process::{self, Command, Stdio};
 use std::{fs, io};
 use walkdir::WalkDir;
 
-#[derive(Debug)]
-pub enum CliError {
+pub enum Error {
     CommandFailed(String, String),
-    IoError(io::Error),
+    Io(io::Error),
     RustfmtNotInstalled,
-    WalkDirError(walkdir::Error),
+    WalkDir(walkdir::Error),
     IntellijSetupActive,
+    Parse(PathBuf, usize, String),
+    CheckFailed,
 }
 
-impl From<io::Error> for CliError {
+impl From<io::Error> for Error {
     fn from(error: io::Error) -> Self {
-        Self::IoError(error)
+        Self::Io(error)
     }
 }
 
-impl From<walkdir::Error> for CliError {
+impl From<walkdir::Error> for Error {
     fn from(error: walkdir::Error) -> Self {
-        Self::WalkDirError(error)
+        Self::WalkDir(error)
+    }
+}
+
+impl Error {
+    fn display(&self) {
+        match self {
+            Self::CheckFailed => {
+                eprintln!("Formatting check failed!\nRun `cargo dev fmt` to update.");
+            },
+            Self::CommandFailed(command, stderr) => {
+                eprintln!("error: command `{command}` failed!\nstderr: {stderr}");
+            },
+            Self::Io(err) => {
+                eprintln!("error: {err}");
+            },
+            Self::RustfmtNotInstalled => {
+                eprintln!("error: rustfmt nightly is not installed.");
+            },
+            Self::WalkDir(err) => {
+                eprintln!("error: {err}");
+            },
+            Self::IntellijSetupActive => {
+                eprintln!(
+                    "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.\n\
+                    Not formatting because that would format the local repo as well!\n\
+                    Please revert the changes to `Cargo.toml`s with `cargo dev remove intellij`."
+                );
+            },
+            Self::Parse(path, line, msg) => {
+                eprintln!("error parsing `{}:{line}`: {msg}", path.display());
+            },
+        }
     }
 }
 
@@ -34,75 +69,244 @@ struct FmtContext {
     rustfmt_path: String,
 }
 
-// the "main" function of cargo dev fmt
-pub fn run(check: bool, verbose: bool) {
-    fn try_run(context: &FmtContext) -> Result<bool, CliError> {
-        let mut success = true;
-
-        let project_root = clippy_project_root();
-
-        // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to
-        // format because rustfmt would also format the entire rustc repo as it is a local
-        // dependency
-        if fs::read_to_string(project_root.join("Cargo.toml"))
-            .expect("Failed to read clippy Cargo.toml")
-            .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
-        {
-            return Err(CliError::IntellijSetupActive);
-        }
-
-        rustfmt_test(context)?;
+struct ClippyConf<'a> {
+    name: &'a str,
+    attrs: &'a str,
+    lints: Vec<&'a str>,
+    field: &'a str,
+}
 
-        success &= cargo_fmt(context, project_root.as_path())?;
-        success &= cargo_fmt(context, &project_root.join("clippy_dev"))?;
-        success &= cargo_fmt(context, &project_root.join("rustc_tools_util"))?;
-        success &= cargo_fmt(context, &project_root.join("lintcheck"))?;
+fn offset_to_line(text: &str, offset: usize) -> usize {
+    match text.split('\n').try_fold((1usize, 0usize), |(line, pos), s| {
+        let pos = pos + s.len() + 1;
+        if pos > offset {
+            ControlFlow::Break(line)
+        } else {
+            ControlFlow::Continue((line + 1, pos))
+        }
+    }) {
+        ControlFlow::Break(x) | ControlFlow::Continue((x, _)) => x,
+    }
+}
 
-        let chunks = WalkDir::new(project_root.join("tests"))
-            .into_iter()
-            .filter_map(|entry| {
-                let entry = entry.expect("failed to find tests");
-                let path = entry.path();
+/// Formats the configuration list in `clippy_config/src/conf.rs`
+#[expect(clippy::too_many_lines)]
+fn fmt_conf(check: bool) -> Result<(), Error> {
+    #[derive(Clone, Copy)]
+    enum State {
+        Start,
+        Docs,
+        Pound,
+        OpenBracket,
+        Attr(u32),
+        Lints,
+        EndLints,
+        Field,
+    }
 
-                if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" {
-                    None
-                } else {
-                    Some(entry.into_path().into_os_string())
-                }
-            })
-            .chunks(250);
+    let path: PathBuf = [
+        clippy_project_root().as_path(),
+        "clippy_config".as_ref(),
+        "src".as_ref(),
+        "conf.rs".as_ref(),
+    ]
+    .into_iter()
+    .collect();
+    let text = fs::read_to_string(&path)?;
 
-        for chunk in &chunks {
-            success &= rustfmt(context, chunk)?;
-        }
+    let (pre, conf) = text
+        .split_once("define_Conf! {\n")
+        .expect("can't find config definition");
+    let (conf, post) = conf.split_once("\n}\n").expect("can't find config definition");
+    let conf_offset = pre.len() + 15;
 
-        Ok(success)
-    }
+    let mut pos = 0u32;
+    let mut attrs_start = 0;
+    let mut attrs_end = 0;
+    let mut field_start = 0;
+    let mut lints = Vec::new();
+    let mut name = "";
+    let mut fields = Vec::new();
+    let mut state = State::Start;
 
-    fn output_err(err: CliError) {
-        match err {
-            CliError::CommandFailed(command, stderr) => {
-                eprintln!("error: A command failed! `{command}`\nstderr: {stderr}");
+    for (i, t) in tokenize(conf)
+        .map(|x| {
+            let start = pos;
+            pos += x.len;
+            (start as usize, x)
+        })
+        .filter(|(_, t)| !matches!(t.kind, TokenKind::Whitespace))
+    {
+        match (state, t.kind) {
+            (State::Start, TokenKind::LineComment { doc_style: Some(_) }) => {
+                attrs_start = i;
+                attrs_end = i + t.len as usize;
+                state = State::Docs;
             },
-            CliError::IoError(err) => {
-                eprintln!("error: {err}");
+            (State::Start, TokenKind::Pound) => {
+                attrs_start = i;
+                attrs_end = i;
+                state = State::Pound;
             },
-            CliError::RustfmtNotInstalled => {
-                eprintln!("error: rustfmt nightly is not installed.");
+            (State::Docs, TokenKind::LineComment { doc_style: Some(_) }) => attrs_end = i + t.len as usize,
+            (State::Docs, TokenKind::Pound) => state = State::Pound,
+            (State::Pound, TokenKind::OpenBracket) => state = State::OpenBracket,
+            (State::OpenBracket, TokenKind::Ident) => {
+                state = if conf[i..i + t.len as usize] == *"lints" {
+                    State::Lints
+                } else {
+                    State::Attr(0)
+                };
             },
-            CliError::WalkDirError(err) => {
-                eprintln!("error: {err}");
+            (State::Attr(0), TokenKind::CloseBracket) => {
+                attrs_end = i + 1;
+                state = State::Docs;
             },
-            CliError::IntellijSetupActive => {
-                eprintln!(
-                    "error: a local rustc repo is enabled as path dependency via `cargo dev setup intellij`.
-Not formatting because that would format the local repo as well!
-Please revert the changes to Cargo.tomls with `cargo dev remove intellij`."
-                );
+            (State::Attr(x), TokenKind::OpenParen | TokenKind::OpenBracket | TokenKind::OpenBrace) => {
+                state = State::Attr(x + 1);
+            },
+            (State::Attr(x), TokenKind::CloseParen | TokenKind::CloseBracket | TokenKind::CloseBrace) => {
+                state = State::Attr(x - 1);
+            },
+            (State::Lints, TokenKind::Ident) => lints.push(&conf[i..i + t.len as usize]),
+            (State::Lints, TokenKind::CloseBracket) => state = State::EndLints,
+            (State::EndLints | State::Docs, TokenKind::Ident) => {
+                field_start = i;
+                name = &conf[i..i + t.len as usize];
+                state = State::Field;
+            },
+            (State::Field, TokenKind::LineComment { doc_style: Some(_) }) => {
+                #[expect(clippy::drain_collect)]
+                fields.push(ClippyConf {
+                    name,
+                    lints: lints.drain(..).collect(),
+                    attrs: &conf[attrs_start..attrs_end],
+                    field: conf[field_start..i].trim_end(),
+                });
+                attrs_start = i;
+                attrs_end = i + t.len as usize;
+                state = State::Docs;
+            },
+            (State::Field, TokenKind::Pound) => {
+                #[expect(clippy::drain_collect)]
+                fields.push(ClippyConf {
+                    name,
+                    lints: lints.drain(..).collect(),
+                    attrs: &conf[attrs_start..attrs_end],
+                    field: conf[field_start..i].trim_end(),
+                });
+                attrs_start = i;
+                attrs_end = i;
+                state = State::Pound;
+            },
+            (State::Field | State::Attr(_), _)
+            | (State::Lints, TokenKind::Comma | TokenKind::OpenParen | TokenKind::CloseParen) => {},
+            _ => {
+                return Err(Error::Parse(
+                    path,
+                    offset_to_line(&text, conf_offset + i),
+                    format!("unexpected token `{}`", &conf[i..i + t.len as usize]),
+                ));
             },
         }
     }
 
+    if !matches!(state, State::Field) {
+        return Err(Error::Parse(
+            path,
+            offset_to_line(&text, conf_offset + conf.len()),
+            "incomplete field".into(),
+        ));
+    }
+    fields.push(ClippyConf {
+        name,
+        lints,
+        attrs: &conf[attrs_start..attrs_end],
+        field: conf[field_start..].trim_end(),
+    });
+
+    for field in &mut fields {
+        field.lints.sort_unstable();
+    }
+    fields.sort_by_key(|x| x.name);
+
+    let new_text = format!(
+        "{pre}define_Conf! {{\n{}}}\n{post}",
+        fields.iter().format_with("", |field, f| {
+            if field.lints.is_empty() {
+                f(&format_args!("    {}\n    {}\n", field.attrs, field.field))
+            } else if field.lints.iter().map(|x| x.len() + 2).sum::<usize>() < 120 - 14 {
+                f(&format_args!(
+                    "    {}\n    #[lints({})]\n    {}\n",
+                    field.attrs,
+                    field.lints.iter().join(", "),
+                    field.field,
+                ))
+            } else {
+                f(&format_args!(
+                    "    {}\n    #[lints({}\n    )]\n    {}\n",
+                    field.attrs,
+                    field
+                        .lints
+                        .iter()
+                        .format_with("", |x, f| f(&format_args!("\n        {x},"))),
+                    field.field,
+                ))
+            }
+        })
+    );
+
+    if text != new_text {
+        if check {
+            return Err(Error::CheckFailed);
+        }
+        fs::write(&path, new_text.as_bytes())?;
+    }
+    Ok(())
+}
+
+fn run_rustfmt(context: &FmtContext) -> Result<(), Error> {
+    let project_root = clippy_project_root();
+
+    // if we added a local rustc repo as path dependency to clippy for rust analyzer, we do NOT want to
+    // format because rustfmt would also format the entire rustc repo as it is a local
+    // dependency
+    if fs::read_to_string(project_root.join("Cargo.toml"))
+        .expect("Failed to read clippy Cargo.toml")
+        .contains("[target.'cfg(NOT_A_PLATFORM)'.dependencies]")
+    {
+        return Err(Error::IntellijSetupActive);
+    }
+
+    check_for_rustfmt(context)?;
+
+    cargo_fmt(context, project_root.as_path())?;
+    cargo_fmt(context, &project_root.join("clippy_dev"))?;
+    cargo_fmt(context, &project_root.join("rustc_tools_util"))?;
+    cargo_fmt(context, &project_root.join("lintcheck"))?;
+
+    let chunks = WalkDir::new(project_root.join("tests"))
+        .into_iter()
+        .filter_map(|entry| {
+            let entry = entry.expect("failed to find tests");
+            let path = entry.path();
+
+            if path.extension() != Some("rs".as_ref()) || entry.file_name() == "ice-3891.rs" {
+                None
+            } else {
+                Some(entry.into_path().into_os_string())
+            }
+        })
+        .chunks(250);
+
+    for chunk in &chunks {
+        rustfmt(context, chunk)?;
+    }
+    Ok(())
+}
+
+// the "main" function of cargo dev fmt
+pub fn run(check: bool, verbose: bool) {
     let output = Command::new("rustup")
         .args(["which", "rustfmt"])
         .stderr(Stdio::inherit())
@@ -120,21 +324,10 @@ Please revert the changes to Cargo.tomls with `cargo dev remove intellij`."
         verbose,
         rustfmt_path,
     };
-    let result = try_run(&context);
-    let code = match result {
-        Ok(true) => 0,
-        Ok(false) => {
-            eprintln!();
-            eprintln!("Formatting check failed.");
-            eprintln!("Run `cargo dev fmt` to update formatting.");
-            1
-        },
-        Err(err) => {
-            output_err(err);
-            1
-        },
-    };
-    process::exit(code);
+    if let Err(e) = run_rustfmt(&context).and_then(|()| fmt_conf(check)) {
+        e.display();
+        process::exit(1);
+    }
 }
 
 fn format_command(program: impl AsRef<OsStr>, dir: impl AsRef<Path>, args: &[impl AsRef<OsStr>]) -> String {
@@ -148,12 +341,12 @@ fn format_command(program: impl AsRef<OsStr>, dir: impl AsRef<Path>, args: &[imp
     )
 }
 
-fn exec(
+fn exec_fmt_command(
     context: &FmtContext,
     program: impl AsRef<OsStr>,
     dir: impl AsRef<Path>,
     args: &[impl AsRef<OsStr>],
-) -> Result<bool, CliError> {
+) -> Result<(), Error> {
     if context.verbose {
         println!("{}", format_command(&program, &dir, args));
     }
@@ -166,28 +359,28 @@ fn exec(
         .unwrap();
     let success = output.status.success();
 
-    if !context.check && !success {
-        let stderr = std::str::from_utf8(&output.stderr).unwrap_or("");
-        return Err(CliError::CommandFailed(
-            format_command(&program, &dir, args),
-            String::from(stderr),
-        ));
+    match (context.check, success) {
+        (_, true) => Ok(()),
+        (true, false) => Err(Error::CheckFailed),
+        (false, false) => {
+            let stderr = std::str::from_utf8(&output.stderr).unwrap_or("");
+            Err(Error::CommandFailed(
+                format_command(&program, &dir, args),
+                String::from(stderr),
+            ))
+        },
     }
-
-    Ok(success)
 }
 
-fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<bool, CliError> {
+fn cargo_fmt(context: &FmtContext, path: &Path) -> Result<(), Error> {
     let mut args = vec!["fmt", "--all"];
     if context.check {
         args.push("--check");
     }
-    let success = exec(context, "cargo", path, &args)?;
-
-    Ok(success)
+    exec_fmt_command(context, "cargo", path, &args)
 }
 
-fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
+fn check_for_rustfmt(context: &FmtContext) -> Result<(), Error> {
     let program = "rustfmt";
     let dir = std::env::current_dir()?;
     let args = &["--version"];
@@ -204,23 +397,20 @@ fn rustfmt_test(context: &FmtContext) -> Result<(), CliError> {
         .unwrap_or("")
         .starts_with("error: 'rustfmt' is not installed")
     {
-        Err(CliError::RustfmtNotInstalled)
+        Err(Error::RustfmtNotInstalled)
     } else {
-        Err(CliError::CommandFailed(
+        Err(Error::CommandFailed(
             format_command(program, &dir, args),
             std::str::from_utf8(&output.stderr).unwrap_or("").to_string(),
         ))
     }
 }
 
-fn rustfmt(context: &FmtContext, paths: impl Iterator<Item = OsString>) -> Result<bool, CliError> {
+fn rustfmt(context: &FmtContext, paths: impl Iterator<Item = OsString>) -> Result<(), Error> {
     let mut args = Vec::new();
     if context.check {
         args.push(OsString::from("--check"));
     }
     args.extend(paths);
-
-    let success = exec(context, &context.rustfmt_path, std::env::current_dir()?, &args)?;
-
-    Ok(success)
+    exec_fmt_command(context, &context.rustfmt_path, std::env::current_dir()?, &args)
 }
diff --git a/clippy_dev/src/lib.rs b/clippy_dev/src/lib.rs
index 3aa43dbe23ed6..ad385d5fbd29f 100644
--- a/clippy_dev/src/lib.rs
+++ b/clippy_dev/src/lib.rs
@@ -1,6 +1,5 @@
 #![feature(let_chains)]
 #![feature(rustc_private)]
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![warn(
     trivial_casts,
     trivial_numeric_casts,
diff --git a/clippy_dev/src/main.rs b/clippy_dev/src/main.rs
index 366b52b25dfcf..755b04b0b232f 100644
--- a/clippy_dev/src/main.rs
+++ b/clippy_dev/src/main.rs
@@ -1,4 +1,3 @@
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 // warn on lints, that are included in `rust-lang/rust`s bootstrap
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
@@ -74,7 +73,7 @@ fn main() {
             new_name,
             uplift,
         } => update_lints::rename(&old_name, new_name.as_ref().unwrap_or(&old_name), uplift),
-        DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, reason.as_deref()),
+        DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, &reason),
     }
 }
 
@@ -223,7 +222,7 @@ enum DevCommand {
         name: String,
         #[arg(long, short)]
         /// The reason for deprecation
-        reason: Option<String>,
+        reason: String,
     },
 }
 
diff --git a/clippy_dev/src/serve.rs b/clippy_dev/src/serve.rs
index 4a4261d1a1e63..19560b31fd3e2 100644
--- a/clippy_dev/src/serve.rs
+++ b/clippy_dev/src/serve.rs
@@ -1,10 +1,14 @@
-use std::ffi::OsStr;
-use std::num::ParseIntError;
 use std::path::Path;
 use std::process::Command;
 use std::time::{Duration, SystemTime};
 use std::{env, thread};
 
+#[cfg(windows)]
+const PYTHON: &str = "python";
+
+#[cfg(not(windows))]
+const PYTHON: &str = "python3";
+
 /// # Panics
 ///
 /// Panics if the python commands could not be spawned
@@ -25,7 +29,7 @@ pub fn run(port: u16, lint: Option<String>) -> ! {
         }
         if let Some(url) = url.take() {
             thread::spawn(move || {
-                Command::new("python3")
+                Command::new(PYTHON)
                     .arg("-m")
                     .arg("http.server")
                     .arg(port.to_string())
@@ -58,8 +62,3 @@ fn mtime(path: impl AsRef<Path>) -> SystemTime {
             .unwrap_or(SystemTime::UNIX_EPOCH)
     }
 }
-
-#[allow(clippy::missing_errors_doc)]
-pub fn validate_port(arg: &OsStr) -> Result<(), ParseIntError> {
-    arg.to_string_lossy().parse::<u16>().map(|_| ())
-}
diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs
index 45353901c98fd..15578d69c3a9d 100644
--- a/clippy_dev/src/update_lints.rs
+++ b/clippy_dev/src/update_lints.rs
@@ -1,13 +1,12 @@
 use crate::clippy_project_root;
 use aho_corasick::AhoCorasickBuilder;
-use indoc::writedoc;
 use itertools::Itertools;
 use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
 use std::collections::{HashMap, HashSet};
 use std::ffi::OsStr;
 use std::fmt::{self, Write};
 use std::fs::{self, OpenOptions};
-use std::io::{self, Read, Seek, SeekFrom, Write as _};
+use std::io::{self, Read, Seek, Write as _};
 use std::ops::Range;
 use std::path::{Path, PathBuf};
 use walkdir::{DirEntry, WalkDir};
@@ -77,12 +76,8 @@ fn generate_lint_files(
             for lint in usable_lints
                 .iter()
                 .map(|l| &*l.name)
-                .chain(deprecated_lints.iter().map(|l| &*l.name))
-                .chain(
-                    renamed_lints
-                        .iter()
-                        .map(|l| l.old_name.strip_prefix("clippy::").unwrap_or(&l.old_name)),
-                )
+                .chain(deprecated_lints.iter().filter_map(|l| l.name.strip_prefix("clippy::")))
+                .chain(renamed_lints.iter().filter_map(|l| l.old_name.strip_prefix("clippy::")))
                 .sorted()
             {
                 writeln!(res, "[`{lint}`]: {DOCS_LINK}#{lint}").unwrap();
@@ -108,11 +103,6 @@ fn generate_lint_files(
         update_mode,
         &gen_declared_lints(internal_lints.iter(), usable_lints.iter()),
     );
-    process_file(
-        "clippy_lints/src/lib.deprecated.rs",
-        update_mode,
-        &gen_deprecated(deprecated_lints),
-    );
 
     let content = gen_deprecated_lints_test(deprecated_lints);
     process_file("tests/ui/deprecated.rs", update_mode, &content);
@@ -205,7 +195,7 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
             let ext = f.path().extension();
             (ext == Some(OsStr::new("rs")) || ext == Some(OsStr::new("fixed")))
                 && name != Some(OsStr::new("rename.rs"))
-                && name != Some(OsStr::new("renamed_lints.rs"))
+                && name != Some(OsStr::new("deprecated_lints.rs"))
         })
     {
         rewrite_file(file.path(), |s| {
@@ -213,6 +203,19 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
         });
     }
 
+    let version = crate::new_lint::get_stabilization_version();
+    rewrite_file(Path::new("clippy_lints/src/deprecated_lints.rs"), |s| {
+        insert_at_marker(
+            s,
+            "// end renamed lints. used by `cargo dev rename_lint`",
+            &format!(
+                "#[clippy::version = \"{version}\"]\n    \
+                (\"{}\", \"{}\"),\n    ",
+                lint.old_name, lint.new_name,
+            ),
+        )
+    });
+
     renamed_lints.push(lint);
     renamed_lints.sort_by(|lhs, rhs| {
         lhs.new_name
@@ -222,11 +225,6 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
             .then_with(|| lhs.old_name.cmp(&rhs.old_name))
     });
 
-    write_file(
-        Path::new("clippy_lints/src/renamed_lints.rs"),
-        &gen_renamed_lints_list(&renamed_lints),
-    );
-
     if uplift {
         write_file(Path::new("tests/ui/rename.rs"), &gen_renamed_lints_test(&renamed_lints));
         println!(
@@ -293,7 +291,8 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
 
         // Don't change `clippy_utils/src/renamed_lints.rs` here as it would try to edit the lint being
         // renamed.
-        for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("renamed_lints.rs")) {
+        for (_, file) in clippy_lints_src_files().filter(|(rel_path, _)| rel_path != OsStr::new("deprecated_lints.rs"))
+        {
             rewrite_file(file.path(), |s| replace_ident_like(s, replacements));
         }
 
@@ -304,7 +303,6 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
     println!("note: `cargo uitest` still needs to be run to update the test results");
 }
 
-const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note";
 /// Runs the `deprecate` command
 ///
 /// This does the following:
@@ -314,33 +312,16 @@ const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note";
 /// # Panics
 ///
 /// If a file path could not read from or written to
-pub fn deprecate(name: &str, reason: Option<&str>) {
-    fn finish(
-        (lints, mut deprecated_lints, renamed_lints): (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>),
-        name: &str,
-        reason: &str,
-    ) {
-        deprecated_lints.push(DeprecatedLint {
-            name: name.to_string(),
-            reason: reason.to_string(),
-            declaration_range: Range::default(),
-        });
-
-        generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
-        println!("info: `{name}` has successfully been deprecated");
-
-        if reason == DEFAULT_DEPRECATION_REASON {
-            println!("note: the deprecation reason must be updated in `clippy_lints/src/deprecated_lints.rs`");
-        }
-        println!("note: you must run `cargo uitest` to update the test results");
-    }
-
-    let reason = reason.unwrap_or(DEFAULT_DEPRECATION_REASON);
-    let name_lower = name.to_lowercase();
-    let name_upper = name.to_uppercase();
+pub fn deprecate(name: &str, reason: &str) {
+    let prefixed_name = if name.starts_with("clippy::") {
+        name.to_owned()
+    } else {
+        format!("clippy::{name}")
+    };
+    let stripped_name = &prefixed_name[8..];
 
-    let (mut lints, deprecated_lints, renamed_lints) = gather_all();
-    let Some(lint) = lints.iter().find(|l| l.name == name_lower) else {
+    let (mut lints, mut deprecated_lints, renamed_lints) = gather_all();
+    let Some(lint) = lints.iter().find(|l| l.name == stripped_name) else {
         eprintln!("error: failed to find lint `{name}`");
         return;
     };
@@ -357,13 +338,27 @@ pub fn deprecate(name: &str, reason: Option<&str>) {
 
     let deprecated_lints_path = &*clippy_project_root().join("clippy_lints/src/deprecated_lints.rs");
 
-    if remove_lint_declaration(&name_lower, &mod_path, &mut lints).unwrap_or(false) {
-        declare_deprecated(&name_upper, deprecated_lints_path, reason).unwrap();
-        finish((lints, deprecated_lints, renamed_lints), name, reason);
-        return;
-    }
+    if remove_lint_declaration(stripped_name, &mod_path, &mut lints).unwrap_or(false) {
+        let version = crate::new_lint::get_stabilization_version();
+        rewrite_file(deprecated_lints_path, |s| {
+            insert_at_marker(
+                s,
+                "// end deprecated lints. used by `cargo dev deprecate_lint`",
+                &format!("#[clippy::version = \"{version}\"]\n    (\"{prefixed_name}\", \"{reason}\"),\n    ",),
+            )
+        });
+
+        deprecated_lints.push(DeprecatedLint {
+            name: prefixed_name,
+            reason: reason.into(),
+        });
 
-    eprintln!("error: lint not found");
+        generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
+        println!("info: `{name}` has successfully been deprecated");
+        println!("note: you must run `cargo uitest` to update the test results");
+    } else {
+        eprintln!("error: lint not found");
+    }
 }
 
 fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io::Result<bool> {
@@ -377,14 +372,14 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
 
         // Some lints have their own directories, delete them
         if path.is_dir() {
-            fs::remove_dir_all(path).ok();
+            let _ = fs::remove_dir_all(path);
             return;
         }
 
         // Remove all related test files
-        fs::remove_file(path.with_extension("rs")).ok();
-        fs::remove_file(path.with_extension("stderr")).ok();
-        fs::remove_file(path.with_extension("fixed")).ok();
+        let _ = fs::remove_file(path.with_extension("rs"));
+        let _ = fs::remove_file(path.with_extension("stderr"));
+        let _ = fs::remove_file(path.with_extension("fixed"));
     }
 
     fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) {
@@ -427,7 +422,7 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
                     lint_mod_path.set_file_name(name);
                     lint_mod_path.set_extension("rs");
 
-                    fs::remove_file(lint_mod_path).ok();
+                    let _ = fs::remove_file(lint_mod_path);
                 }
 
                 let mut content =
@@ -465,37 +460,6 @@ fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io
     Ok(false)
 }
 
-fn declare_deprecated(name: &str, path: &Path, reason: &str) -> io::Result<()> {
-    let mut file = OpenOptions::new().write(true).open(path)?;
-
-    file.seek(SeekFrom::End(0))?;
-
-    let version = crate::new_lint::get_stabilization_version();
-    let deprecation_reason = if reason == DEFAULT_DEPRECATION_REASON {
-        "TODO"
-    } else {
-        reason
-    };
-
-    writedoc!(
-        file,
-        "
-
-        declare_deprecated_lint! {{
-            /// ### What it does
-            /// Nothing. This lint has been deprecated.
-            ///
-            /// ### Deprecation reason
-            /// {deprecation_reason}
-            #[clippy::version = \"{version}\"]
-            pub {name},
-            \"{reason}\"
-        }}
-
-        "
-    )
-}
-
 /// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there
 /// were no replacements.
 fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option<String> {
@@ -604,14 +568,12 @@ impl Lint {
 struct DeprecatedLint {
     name: String,
     reason: String,
-    declaration_range: Range<usize>,
 }
 impl DeprecatedLint {
-    fn new(name: &str, reason: &str, declaration_range: Range<usize>) -> Self {
+    fn new(name: &str, reason: &str) -> Self {
         Self {
-            name: name.to_lowercase(),
+            name: remove_line_splices(name),
             reason: remove_line_splices(reason),
-            declaration_range,
         }
     }
 }
@@ -629,28 +591,6 @@ impl RenamedLint {
     }
 }
 
-/// Generates the `register_removed` code
-#[must_use]
-fn gen_deprecated(lints: &[DeprecatedLint]) -> String {
-    let mut output = GENERATED_FILE_COMMENT.to_string();
-    output.push_str("{\n");
-    for lint in lints {
-        let _: fmt::Result = write!(
-            output,
-            concat!(
-                "    store.register_removed(\n",
-                "        \"clippy::{}\",\n",
-                "        \"{}\",\n",
-                "    );\n"
-            ),
-            lint.name, lint.reason,
-        );
-    }
-    output.push_str("}\n");
-
-    output
-}
-
 /// Generates the code for registering lints
 #[must_use]
 fn gen_declared_lints<'a>(
@@ -680,7 +620,7 @@ fn gen_declared_lints<'a>(
 fn gen_deprecated_lints_test(lints: &[DeprecatedLint]) -> String {
     let mut res: String = GENERATED_FILE_COMMENT.into();
     for lint in lints {
-        writeln!(res, "#![warn(clippy::{})]", lint.name).unwrap();
+        writeln!(res, "#![warn({})] //~ ERROR: lint `{}`", lint.name, lint.name).unwrap();
     }
     res.push_str("\nfn main() {}\n");
     res
@@ -699,27 +639,13 @@ fn gen_renamed_lints_test(lints: &[RenamedLint]) -> String {
     seen_lints.clear();
     for lint in lints {
         if seen_lints.insert(&lint.old_name) {
-            writeln!(res, "#![warn({})]", lint.old_name).unwrap();
+            writeln!(res, "#![warn({})] //~ ERROR: lint `{}`", lint.old_name, lint.old_name).unwrap();
         }
     }
     res.push_str("\nfn main() {}\n");
     res
 }
 
-fn gen_renamed_lints_list(lints: &[RenamedLint]) -> String {
-    const HEADER: &str = "\
-        // This file is managed by `cargo dev rename_lint`. Prefer using that when possible.\n\n\
-        #[rustfmt::skip]\n\
-        pub static RENAMED_LINTS: &[(&str, &str)] = &[\n";
-
-    let mut res = String::from(HEADER);
-    for lint in lints {
-        writeln!(res, "    (\"{}\", \"{}\"),", lint.old_name, lint.new_name).unwrap();
-    }
-    res.push_str("];\n");
-    res
-}
-
 /// Gathers all lints defined in `clippy_lints/src`
 fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) {
     let mut lints = Vec::with_capacity(1000);
@@ -744,10 +670,10 @@ fn gather_all() -> (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>) {
             module.strip_suffix(".rs").unwrap_or(&module)
         };
 
-        match module {
-            "deprecated_lints" => parse_deprecated_contents(&contents, &mut deprecated_lints),
-            "renamed_lints" => parse_renamed_contents(&contents, &mut renamed_lints),
-            _ => parse_contents(&contents, module, &mut lints),
+        if module == "deprecated_lints" {
+            parse_deprecated_contents(&contents, &mut deprecated_lints, &mut renamed_lints);
+        } else {
+            parse_contents(&contents, module, &mut lints);
         }
     }
     (lints, deprecated_lints, renamed_lints)
@@ -848,54 +774,37 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
 }
 
 /// Parse a source file looking for `declare_deprecated_lint` macro invocations.
-fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) {
-    let mut offset = 0usize;
-    let mut iter = tokenize(contents).map(|t| {
-        let range = offset..offset + t.len as usize;
-        offset = range.end;
-
-        LintDeclSearchResult {
-            token_kind: t.kind,
-            content: &contents[range.clone()],
-            range,
-        }
-    });
+fn parse_deprecated_contents(contents: &str, deprecated: &mut Vec<DeprecatedLint>, renamed: &mut Vec<RenamedLint>) {
+    let Some((_, contents)) = contents.split_once("\ndeclare_with_version! { DEPRECATED") else {
+        return;
+    };
+    let Some((deprecated_src, renamed_src)) = contents.split_once("\ndeclare_with_version! { RENAMED") else {
+        return;
+    };
 
-    while let Some(LintDeclSearchResult { range, .. }) = iter.find(
-        |LintDeclSearchResult {
-             token_kind, content, ..
-         }| token_kind == &TokenKind::Ident && *content == "declare_deprecated_lint",
-    ) {
-        let start = range.start;
+    for line in deprecated_src.lines() {
+        let mut offset = 0usize;
+        let mut iter = tokenize(line).map(|t| {
+            let range = offset..offset + t.len as usize;
+            offset = range.end;
 
-        let mut iter = iter.by_ref().filter(|LintDeclSearchResult { ref token_kind, .. }| {
-            !matches!(token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })
+            LintDeclSearchResult {
+                token_kind: t.kind,
+                content: &line[range.clone()],
+                range,
+            }
         });
+
         let (name, reason) = match_tokens!(
             iter,
-            // !{
-            Bang OpenBrace
-            // #[clippy::version = "version"]
-            Pound OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket
-            // pub LINT_NAME,
-            Ident Ident(name) Comma
-            // "description"
-            Literal{kind: LiteralKind::Str{..},..}(reason)
+            // ("old_name",
+            Whitespace OpenParen Literal{kind: LiteralKind::Str{..},..}(name) Comma
+            // "new_name"),
+            Whitespace Literal{kind: LiteralKind::Str{..},..}(reason) CloseParen Comma
         );
-
-        if let Some(LintDeclSearchResult {
-            token_kind: TokenKind::CloseBrace,
-            range,
-            ..
-        }) = iter.next()
-        {
-            lints.push(DeprecatedLint::new(name, reason, start..range.end));
-        }
+        deprecated.push(DeprecatedLint::new(name, reason));
     }
-}
-
-fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) {
-    for line in contents.lines() {
+    for line in renamed_src.lines() {
         let mut offset = 0usize;
         let mut iter = tokenize(line).map(|t| {
             let range = offset..offset + t.len as usize;
@@ -915,7 +824,7 @@ fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) {
             // "new_name"),
             Whitespace Literal{kind: LiteralKind::Str{..},..}(new_name) CloseParen Comma
         );
-        lints.push(RenamedLint::new(old_name, new_name));
+        renamed.push(RenamedLint::new(old_name, new_name));
     }
 }
 
@@ -1015,6 +924,12 @@ fn panic_file(error: io::Error, name: &Path, action: &str) -> ! {
     panic!("failed to {action} file `{}`: {error}", name.display())
 }
 
+fn insert_at_marker(text: &str, marker: &str, new_text: &str) -> Option<String> {
+    let i = text.find(marker)?;
+    let (pre, post) = text.split_at(i);
+    Some([pre, new_text, post].into_iter().collect())
+}
+
 fn rewrite_file(path: &Path, f: impl FnOnce(&str) -> Option<String>) {
     let mut file = OpenOptions::new()
         .write(true)
@@ -1084,31 +999,6 @@ mod tests {
         assert_eq!(expected, result);
     }
 
-    #[test]
-    fn test_parse_deprecated_contents() {
-        static DEPRECATED_CONTENTS: &str = r#"
-            /// some doc comment
-            declare_deprecated_lint! {
-                #[clippy::version = "I'm a version"]
-                pub SHOULD_ASSERT_EQ,
-                "`assert!()` will be more flexible with RFC 2011"
-            }
-        "#;
-
-        let mut result = Vec::new();
-        parse_deprecated_contents(DEPRECATED_CONTENTS, &mut result);
-        for r in &mut result {
-            r.declaration_range = Range::default();
-        }
-
-        let expected = vec![DeprecatedLint::new(
-            "should_assert_eq",
-            "\"`assert!()` will be more flexible with RFC 2011\"",
-            Range::default(),
-        )];
-        assert_eq!(expected, result);
-    }
-
     #[test]
     fn test_usable_lints() {
         let lints = vec![
@@ -1177,34 +1067,4 @@ mod tests {
         );
         assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
     }
-
-    #[test]
-    fn test_gen_deprecated() {
-        let lints = vec![
-            DeprecatedLint::new(
-                "should_assert_eq",
-                "\"has been superseded by should_assert_eq2\"",
-                Range::default(),
-            ),
-            DeprecatedLint::new("another_deprecated", "\"will be removed\"", Range::default()),
-        ];
-
-        let expected = GENERATED_FILE_COMMENT.to_string()
-            + &[
-                "{",
-                "    store.register_removed(",
-                "        \"clippy::should_assert_eq\",",
-                "        \"has been superseded by should_assert_eq2\",",
-                "    );",
-                "    store.register_removed(",
-                "        \"clippy::another_deprecated\",",
-                "        \"will be removed\",",
-                "    );",
-                "}",
-            ]
-            .join("\n")
-            + "\n";
-
-        assert_eq!(expected, gen_deprecated(&lints));
-    }
 }
diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml
index eb04c006f89fd..99ed93468a01a 100644
--- a/clippy_lints/Cargo.toml
+++ b/clippy_lints/Cargo.toml
@@ -32,7 +32,6 @@ url = "2.2"
 walkdir = "2.3"
 
 [features]
-deny-warnings = ["clippy_config/deny-warnings", "clippy_utils/deny-warnings"]
 # build clippy with internal lints enabled, off by default
 internal = ["serde_json", "tempfile", "regex"]
 
diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs
index e6d52bcef717c..3b4cc1134802d 100644
--- a/clippy_lints/src/approx_const.rs
+++ b/clippy_lints/src/approx_const.rs
@@ -97,7 +97,7 @@ impl ApproxConstant {
                         cx,
                         APPROX_CONSTANT,
                         e.span,
-                        format!("approximate value of `{module}::consts::{}` found", &name),
+                        format!("approximate value of `{module}::consts::{name}` found"),
                         None,
                         "consider using the constant directly",
                     );
diff --git a/clippy_lints/src/as_conversions.rs b/clippy_lints/src/as_conversions.rs
index cfa25005a05ed..fefd8195f8e7e 100644
--- a/clippy_lints/src/as_conversions.rs
+++ b/clippy_lints/src/as_conversions.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_from_proc_macro;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -52,13 +52,15 @@ impl<'tcx> LateLintPass<'tcx> for AsConversions {
             && !in_external_macro(cx.sess(), expr.span)
             && !is_from_proc_macro(cx, expr)
         {
-            span_lint_and_help(
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(
                 cx,
                 AS_CONVERSIONS,
                 expr.span,
                 "using a potentially dangerous silent `as` conversion",
-                None,
-                "consider using a safe wrapper for this conversion",
+                |diag| {
+                    diag.help("consider using a safe wrapper for this conversion");
+                },
             );
         }
     }
diff --git a/clippy_lints/src/asm_syntax.rs b/clippy_lints/src/asm_syntax.rs
index 0db1456d40bfc..69a8eb7d94e74 100644
--- a/clippy_lints/src/asm_syntax.rs
+++ b/clippy_lints/src/asm_syntax.rs
@@ -1,6 +1,6 @@
 use std::fmt;
 
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions};
 use rustc_ast::{InlineAsm, Item, ItemKind};
 use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext};
@@ -49,14 +49,10 @@ fn check_asm_syntax(
         };
 
         if style == check_for {
-            span_lint_and_help(
-                cx,
-                lint,
-                span,
-                format!("{style} x86 assembly syntax used"),
-                None,
-                format!("use {} x86 assembly syntax", !style),
-            );
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(cx, lint, span, format!("{style} x86 assembly syntax used"), |diag| {
+                diag.help(format!("use {} x86 assembly syntax", !style));
+            });
         }
     }
 }
@@ -98,13 +94,13 @@ declare_lint_pass!(InlineAsmX86IntelSyntax => [INLINE_ASM_X86_INTEL_SYNTAX]);
 impl EarlyLintPass for InlineAsmX86IntelSyntax {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
         if let ExprKind::InlineAsm(inline_asm) = &expr.kind {
-            check_asm_syntax(Self::get_lints()[0], cx, inline_asm, expr.span, AsmStyle::Intel);
+            check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, expr.span, AsmStyle::Intel);
         }
     }
 
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
         if let ItemKind::GlobalAsm(inline_asm) = &item.kind {
-            check_asm_syntax(Self::get_lints()[0], cx, inline_asm, item.span, AsmStyle::Intel);
+            check_asm_syntax(INLINE_ASM_X86_INTEL_SYNTAX, cx, inline_asm, item.span, AsmStyle::Intel);
         }
     }
 }
@@ -146,13 +142,13 @@ declare_lint_pass!(InlineAsmX86AttSyntax => [INLINE_ASM_X86_ATT_SYNTAX]);
 impl EarlyLintPass for InlineAsmX86AttSyntax {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
         if let ExprKind::InlineAsm(inline_asm) = &expr.kind {
-            check_asm_syntax(Self::get_lints()[0], cx, inline_asm, expr.span, AsmStyle::Att);
+            check_asm_syntax(INLINE_ASM_X86_ATT_SYNTAX, cx, inline_asm, expr.span, AsmStyle::Att);
         }
     }
 
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &Item) {
         if let ItemKind::GlobalAsm(inline_asm) = &item.kind {
-            check_asm_syntax(Self::get_lints()[0], cx, inline_asm, item.span, AsmStyle::Att);
+            check_asm_syntax(INLINE_ASM_X86_ATT_SYNTAX, cx, inline_asm, item.span, AsmStyle::Att);
         }
     }
 }
diff --git a/clippy_lints/src/assertions_on_constants.rs b/clippy_lints/src/assertions_on_constants.rs
index ed4cdce8cb886..7eaac80f969f7 100644
--- a/clippy_lints/src/assertions_on_constants.rs
+++ b/clippy_lints/src/assertions_on_constants.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::is_inside_always_const_context;
 use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn};
@@ -43,7 +43,7 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants {
         let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else {
             return;
         };
-        let Some(Constant::Bool(val)) = constant(cx, cx.typeck_results(), condition) else {
+        let Some(Constant::Bool(val)) = ConstEvalCtxt::new(cx).eval(condition) else {
             return;
         };
 
diff --git a/clippy_lints/src/assertions_on_result_states.rs b/clippy_lints/src/assertions_on_result_states.rs
index 7217686dcca5b..f1cb4a05af86c 100644
--- a/clippy_lints/src/assertions_on_result_states.rs
+++ b/clippy_lints/src/assertions_on_result_states.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn};
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::{has_debug_impl, is_copy, is_type_diagnostic_item};
@@ -68,39 +68,28 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnResultStates {
                     return;
                 }
             }
-            let semicolon = if is_expr_final_block_expr(cx.tcx, e) { ";" } else { "" };
-            let mut app = Applicability::MachineApplicable;
-            match method_segment.ident.as_str() {
+            let (message, replacement) = match method_segment.ident.as_str() {
                 "is_ok" if type_suitable_to_unwrap(cx, args.type_at(1)) => {
-                    span_lint_and_sugg(
-                        cx,
-                        ASSERTIONS_ON_RESULT_STATES,
-                        macro_call.span,
-                        "called `assert!` with `Result::is_ok`",
-                        "replace with",
-                        format!(
-                            "{}.unwrap(){semicolon}",
-                            snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0
-                        ),
-                        app,
-                    );
+                    ("called `assert!` with `Result::is_ok`", "unwrap")
                 },
                 "is_err" if type_suitable_to_unwrap(cx, args.type_at(0)) => {
-                    span_lint_and_sugg(
-                        cx,
-                        ASSERTIONS_ON_RESULT_STATES,
-                        macro_call.span,
-                        "called `assert!` with `Result::is_err`",
-                        "replace with",
-                        format!(
-                            "{}.unwrap_err(){semicolon}",
-                            snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0
-                        ),
-                        app,
-                    );
+                    ("called `assert!` with `Result::is_err`", "unwrap_err")
                 },
-                _ => (),
+                _ => return,
             };
+            span_lint_and_then(cx, ASSERTIONS_ON_RESULT_STATES, macro_call.span, message, |diag| {
+                let semicolon = if is_expr_final_block_expr(cx.tcx, e) { ";" } else { "" };
+                let mut app = Applicability::MachineApplicable;
+                diag.span_suggestion(
+                    macro_call.span,
+                    "replace with",
+                    format!(
+                        "{}.{replacement}(){semicolon}",
+                        snippet_with_context(cx, recv.span, condition.span.ctxt(), "..", &mut app).0
+                    ),
+                    app,
+                );
+            });
         }
     }
 }
diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs
index 03f777600f084..6e336efbb90a2 100644
--- a/clippy_lints/src/assigning_clones.rs
+++ b/clippy_lints/src/assigning_clones.rs
@@ -227,9 +227,22 @@ fn build_sugg<'tcx>(
             match call_kind {
                 CallKind::Method => {
                     let receiver_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind {
-                        // `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)`
-                        Sugg::hir_with_applicability(cx, ref_expr, "_", app)
+                        // If `ref_expr` is a reference, we can remove the dereference operator (`*`) to make
+                        // the generated code a bit simpler. In other cases, we don't do this special case, to avoid
+                        // having to deal with Deref (https://github.com/rust-lang/rust-clippy/issues/12437).
+
+                        let ty = cx.typeck_results().expr_ty(ref_expr);
+                        if ty.is_ref() {
+                            // Apply special case, remove `*`
+                            // `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)`
+                            Sugg::hir_with_applicability(cx, ref_expr, "_", app)
+                        } else {
+                            // Keep the original lhs
+                            // `*lhs = self_expr.clone();` -> `(*lhs).clone_from(self_expr)`
+                            Sugg::hir_with_applicability(cx, lhs, "_", app)
+                        }
                     } else {
+                        // Keep the original lhs
                         // `lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)`
                         Sugg::hir_with_applicability(cx, lhs, "_", app)
                     }
@@ -249,8 +262,16 @@ fn build_sugg<'tcx>(
                 },
                 CallKind::Ufcs => {
                     let self_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind {
-                        // `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)`
-                        Sugg::hir_with_applicability(cx, ref_expr, "_", app)
+                        // See special case of removing `*` in method handling above
+                        let ty = cx.typeck_results().expr_ty(ref_expr);
+                        if ty.is_ref() {
+                            // `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)`
+                            Sugg::hir_with_applicability(cx, ref_expr, "_", app)
+                        } else {
+                            // `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut *lhs, self_expr)`
+                            // mut_addr_deref is used to avoid unnecessary parentheses around `*lhs`
+                            Sugg::hir_with_applicability(cx, ref_expr, "_", app).mut_addr_deref()
+                        }
                     } else {
                         // `lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut lhs, self_expr)`
                         Sugg::hir_with_applicability(cx, lhs, "_", app).mut_addr()
diff --git a/clippy_lints/src/attrs/allow_attributes.rs b/clippy_lints/src/attrs/allow_attributes.rs
index df9994086cd48..a5a7b9f74a693 100644
--- a/clippy_lints/src/attrs/allow_attributes.rs
+++ b/clippy_lints/src/attrs/allow_attributes.rs
@@ -1,5 +1,5 @@
 use super::ALLOW_ATTRIBUTES;
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_from_proc_macro;
 use rustc_ast::{AttrStyle, Attribute};
 use rustc_errors::Applicability;
@@ -13,14 +13,14 @@ pub fn check<'cx>(cx: &LateContext<'cx>, attr: &'cx Attribute) {
         && let Some(ident) = attr.ident()
         && !is_from_proc_macro(cx, attr)
     {
-        span_lint_and_sugg(
-            cx,
-            ALLOW_ATTRIBUTES,
-            ident.span,
-            "#[allow] attribute found",
-            "replace it with",
-            "expect".into(),
-            Applicability::MachineApplicable,
-        );
+        #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+        span_lint_and_then(cx, ALLOW_ATTRIBUTES, ident.span, "#[allow] attribute found", |diag| {
+            diag.span_suggestion(
+                ident.span,
+                "replace it with",
+                "expect",
+                Applicability::MachineApplicable,
+            );
+        });
     }
 }
diff --git a/clippy_lints/src/attrs/allow_attributes_without_reason.rs b/clippy_lints/src/attrs/allow_attributes_without_reason.rs
index 4b42616a636bc..4ab97118df1d4 100644
--- a/clippy_lints/src/attrs/allow_attributes_without_reason.rs
+++ b/clippy_lints/src/attrs/allow_attributes_without_reason.rs
@@ -1,5 +1,5 @@
 use super::{Attribute, ALLOW_ATTRIBUTES_WITHOUT_REASON};
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_from_proc_macro;
 use rustc_ast::{MetaItemKind, NestedMetaItem};
 use rustc_lint::{LateContext, LintContext};
@@ -21,12 +21,14 @@ pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[NestedMet
         return;
     }
 
-    span_lint_and_help(
+    #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+    span_lint_and_then(
         cx,
         ALLOW_ATTRIBUTES_WITHOUT_REASON,
         attr.span,
         format!("`{}` attribute without specifying a reason", name.as_str()),
-        None,
-        "try adding a reason at the end with `, reason = \"..\"`",
+        |diag| {
+            diag.help("try adding a reason at the end with `, reason = \"..\"`");
+        },
     );
 }
diff --git a/clippy_lints/src/bool_to_int_with_if.rs b/clippy_lints/src/bool_to_int_with_if.rs
index 561ca9bd9866d..612712d16843c 100644
--- a/clippy_lints/src/bool_to_int_with_if.rs
+++ b/clippy_lints/src/bool_to_int_with_if.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{in_constant, is_else_clause};
+use clippy_utils::{is_else_clause, is_in_const_context};
 use rustc_ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
@@ -52,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf {
             && let Some(else_lit) = as_int_bool_lit(else_)
             && then_lit != else_lit
             && !expr.span.from_expansion()
-            && !in_constant(cx, expr.hir_id)
+            && !is_in_const_context(cx)
         {
             let ty = cx.typeck_results().expr_ty(then);
             let mut applicability = Applicability::MachineApplicable;
diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs
index a1c6c0b608f72..a2f48c18170a6 100644
--- a/clippy_lints/src/booleans.rs
+++ b/clippy_lints/src/booleans.rs
@@ -477,14 +477,12 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
             cx: self.cx,
         };
         if let Ok(expr) = h2q.run(e) {
-            if h2q.terminals.len() > 8 {
-                // QMC has exponentially slow behavior as the number of terminals increases
-                // 8 is reasonable, it takes approximately 0.2 seconds.
-                // See #825
+            let stats = terminal_stats(&expr);
+            if stats.ops > 7 {
+                // QMC has exponentially slow behavior as the number of ops increases.
+                // See #825, #13206
                 return;
             }
-
-            let stats = terminal_stats(&expr);
             let mut simplified = expr.simplify();
             for simple in Bool::Not(Box::new(expr)).simplify() {
                 match simple {
diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs
index ff460a3fd8e39..bd3acc06f4b2b 100644
--- a/clippy_lints/src/casts/cast_lossless.rs
+++ b/clippy_lints/src/casts/cast_lossless.rs
@@ -1,6 +1,6 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::in_constant;
+use clippy_utils::is_in_const_context;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_isize_or_usize;
@@ -21,7 +21,7 @@ pub(super) fn check(
     cast_to_hir: &rustc_hir::Ty<'_>,
     msrv: &Msrv,
 ) {
-    if !should_lint(cx, expr, cast_from, cast_to, msrv) {
+    if !should_lint(cx, cast_from, cast_to, msrv) {
         return;
     }
 
@@ -70,9 +70,9 @@ pub(super) fn check(
     );
 }
 
-fn should_lint(cx: &LateContext<'_>, expr: &Expr<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool {
+fn should_lint(cx: &LateContext<'_>, cast_from: Ty<'_>, cast_to: Ty<'_>, msrv: &Msrv) -> bool {
     // Do not suggest using From in consts/statics until it is valid to do so (see #2267).
-    if in_constant(cx, expr.hir_id) {
+    if is_in_const_context(cx) {
         return false;
     }
 
diff --git a/clippy_lints/src/casts/cast_nan_to_int.rs b/clippy_lints/src/casts/cast_nan_to_int.rs
index 5bc8692c289f7..464eabe5d9ab7 100644
--- a/clippy_lints/src/casts/cast_nan_to_int.rs
+++ b/clippy_lints/src/casts/cast_nan_to_int.rs
@@ -1,6 +1,6 @@
 use super::CAST_NAN_TO_INT;
 
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_note;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
@@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
 }
 
 fn is_known_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
-    match constant(cx, cx.typeck_results(), e) {
+    match ConstEvalCtxt::new(cx).eval(e) {
         // FIXME(f16_f128): add these types when nan checks are available on all platforms
         Some(Constant::F64(n)) => n.is_nan(),
         Some(Constant::F32(n)) => n.is_nan(),
diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs
index 7c5acd1a678d7..102fe25fc67b0 100644
--- a/clippy_lints/src/casts/cast_possible_truncation.rs
+++ b/clippy_lints/src/casts/cast_possible_truncation.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::expr_or_init;
 use clippy_utils::source::snippet;
@@ -15,7 +15,7 @@ use rustc_target::abi::IntegerType;
 use super::{utils, CAST_ENUM_TRUNCATION, CAST_POSSIBLE_TRUNCATION};
 
 fn constant_int(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
-    if let Some(Constant::Int(c)) = constant(cx, cx.typeck_results(), expr) {
+    if let Some(Constant::Int(c)) = ConstEvalCtxt::new(cx).eval(expr) {
         Some(c)
     } else {
         None
diff --git a/clippy_lints/src/casts/cast_sign_loss.rs b/clippy_lints/src/casts/cast_sign_loss.rs
index 8bbd41b0db1ee..9daf237344a4b 100644
--- a/clippy_lints/src/casts/cast_sign_loss.rs
+++ b/clippy_lints/src/casts/cast_sign_loss.rs
@@ -1,7 +1,7 @@
 use std::convert::Infallible;
 use std::ops::ControlFlow;
 
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::visitors::{for_each_expr_without_closures, Descend};
 use clippy_utils::{method_chain_args, sext};
@@ -88,7 +88,7 @@ fn get_const_signed_int_eval<'cx>(
 ) -> Option<i128> {
     let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr));
 
-    if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)?
+    if let Constant::Int(n) = ConstEvalCtxt::new(cx).eval(expr)?
         && let ty::Int(ity) = *ty.kind()
     {
         return Some(sext(cx.tcx, n, ity));
@@ -103,7 +103,7 @@ fn get_const_unsigned_int_eval<'cx>(
 ) -> Option<u128> {
     let ty = ty.into().unwrap_or_else(|| cx.typeck_results().expr_ty(expr));
 
-    if let Constant::Int(n) = constant(cx, cx.typeck_results(), expr)?
+    if let Constant::Int(n) = ConstEvalCtxt::new(cx).eval(expr)?
         && let ty::Uint(_ity) = *ty.kind()
     {
         return Some(n);
diff --git a/clippy_lints/src/casts/fn_to_numeric_cast_any.rs b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs
index 826589bf303b5..75de53f73ee7a 100644
--- a/clippy_lints/src/casts/fn_to_numeric_cast_any.rs
+++ b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs
@@ -1,6 +1,6 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_with_applicability;
-use rustc_errors::Applicability;
+use rustc_errors::{Applicability, SuggestionStyle};
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
@@ -14,21 +14,24 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
         _ => { /* continue to checks */ },
     }
 
-    match cast_from.kind() {
-        ty::FnDef(..) | ty::FnPtr(_) => {
-            let mut applicability = Applicability::MaybeIncorrect;
-            let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability);
+    if let ty::FnDef(..) | ty::FnPtr(_) = cast_from.kind() {
+        let mut applicability = Applicability::MaybeIncorrect;
+        let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability);
 
-            span_lint_and_sugg(
-                cx,
-                FN_TO_NUMERIC_CAST_ANY,
-                expr.span,
-                format!("casting function pointer `{from_snippet}` to `{cast_to}`"),
-                "did you mean to invoke the function?",
-                format!("{from_snippet}() as {cast_to}"),
-                applicability,
-            );
-        },
-        _ => {},
+        span_lint_and_then(
+            cx,
+            FN_TO_NUMERIC_CAST_ANY,
+            expr.span,
+            format!("casting function pointer `{from_snippet}` to `{cast_to}`"),
+            |diag| {
+                diag.span_suggestion_with_style(
+                    expr.span,
+                    "did you mean to invoke the function?",
+                    format!("{from_snippet}() as {cast_to}"),
+                    applicability,
+                    SuggestionStyle::ShowAlways,
+                );
+            },
+        );
     }
 }
diff --git a/clippy_lints/src/casts/zero_ptr.rs b/clippy_lints/src/casts/zero_ptr.rs
index 5071af5ecb986..3c1c7d2dc3a54 100644
--- a/clippy_lints/src/casts/zero_ptr.rs
+++ b/clippy_lints/src/casts/zero_ptr.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_opt;
-use clippy_utils::{in_constant, is_integer_literal, std_or_core};
+use clippy_utils::{is_in_const_context, is_integer_literal, std_or_core};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, Mutability, Ty, TyKind};
 use rustc_lint::LateContext;
@@ -10,7 +10,7 @@ use super::ZERO_PTR;
 pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>) {
     if let TyKind::Ptr(ref mut_ty) = to.kind
         && is_integer_literal(from, 0)
-        && !in_constant(cx, from.hir_id)
+        && !is_in_const_context(cx)
         && let Some(std_or_core) = std_or_core(cx)
     {
         let (msg, sugg_fn) = match mut_ty.mutbl {
diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs
index 0b1ab5411bf18..1711565fca891 100644
--- a/clippy_lints/src/checked_conversions.rs
+++ b/clippy_lints/src/checked_conversions.rs
@@ -4,7 +4,7 @@ use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::{in_constant, is_integer_literal, SpanlessEq};
+use clippy_utils::{is_in_const_context, is_integer_literal, SpanlessEq};
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, QPath, TyKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -67,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for CheckedConversions {
                 _ => return,
             }
             && !in_external_macro(cx.sess(), item.span)
-            && !in_constant(cx, item.hir_id)
+            && !is_in_const_context(cx)
             && self.msrv.meets(msrvs::TRY_FROM)
             && let Some(cv) = match op2 {
                 // todo: check for case signed -> larger unsigned == only x >= 0
diff --git a/clippy_lints/src/comparison_chain.rs b/clippy_lints/src/comparison_chain.rs
index 2c23c0b4f154c..5d78744e9b5e2 100644
--- a/clippy_lints/src/comparison_chain.rs
+++ b/clippy_lints/src/comparison_chain.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::ty::implements_trait;
-use clippy_utils::{if_sequence, in_constant, is_else_clause, SpanlessEq};
+use clippy_utils::{if_sequence, is_else_clause, is_in_const_context, SpanlessEq};
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
@@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain {
             return;
         }
 
-        if in_constant(cx, expr.hir_id) {
+        if is_in_const_context(cx) {
             return;
         }
 
diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs
index 27c00948a8f2b..b49a977dbeaf1 100644
--- a/clippy_lints/src/create_dir.rs
+++ b/clippy_lints/src/create_dir.rs
@@ -1,6 +1,6 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet;
-use rustc_errors::Applicability;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_with_applicability;
+use rustc_errors::{Applicability, SuggestionStyle};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
@@ -39,14 +39,24 @@ impl LateLintPass<'_> for CreateDir {
             && let Some(def_id) = cx.qpath_res(path, func.hir_id).opt_def_id()
             && cx.tcx.is_diagnostic_item(sym::fs_create_dir, def_id)
         {
-            span_lint_and_sugg(
+            span_lint_and_then(
                 cx,
                 CREATE_DIR,
                 expr.span,
                 "calling `std::fs::create_dir` where there may be a better way",
-                "consider calling `std::fs::create_dir_all` instead",
-                format!("create_dir_all({})", snippet(cx, arg.span, "..")),
-                Applicability::MaybeIncorrect,
+                |diag| {
+                    let mut app = Applicability::MaybeIncorrect;
+                    diag.span_suggestion_with_style(
+                        expr.span,
+                        "consider calling `std::fs::create_dir_all` instead",
+                        format!(
+                            "create_dir_all({})",
+                            snippet_with_applicability(cx, arg.span, "..", &mut app)
+                        ),
+                        app,
+                        SuggestionStyle::ShowAlways,
+                    );
+                },
             );
         }
     }
diff --git a/clippy_lints/src/dbg_macro.rs b/clippy_lints/src/dbg_macro.rs
index 788c6f3ada297..93c8fff05e9ed 100644
--- a/clippy_lints/src/dbg_macro.rs
+++ b/clippy_lints/src/dbg_macro.rs
@@ -1,5 +1,5 @@
 use clippy_config::Conf;
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_in_test;
 use clippy_utils::macros::{macro_backtrace, MacroCall};
 use clippy_utils::source::snippet_with_applicability;
@@ -65,61 +65,67 @@ impl LateLintPass<'_> for DbgMacro {
             // allows `dbg!` in test code if allow-dbg-in-test is set to true in clippy.toml
             !(self.allow_dbg_in_tests && is_in_test(cx.tcx, expr.hir_id))
         {
-            let mut applicability = Applicability::MachineApplicable;
-
-            let (sugg_span, suggestion) = match expr.peel_drop_temps().kind {
-                // dbg!()
-                ExprKind::Block(..) => {
-                    // If the `dbg!` macro is a "free" statement and not contained within other expressions,
-                    // remove the whole statement.
-                    if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id)
-                        && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span)
-                    {
-                        (macro_call.span.to(semi_span), String::new())
-                    } else {
-                        (macro_call.span, String::from("()"))
-                    }
-                },
-                // dbg!(1)
-                ExprKind::Match(val, ..) => (
-                    macro_call.span,
-                    snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability).to_string(),
-                ),
-                // dbg!(2, 3)
-                ExprKind::Tup(
-                    [
-                        Expr {
-                            kind: ExprKind::Match(first, ..),
-                            ..
-                        },
-                        ..,
-                        Expr {
-                            kind: ExprKind::Match(last, ..),
-                            ..
-                        },
-                    ],
-                ) => {
-                    let snippet = snippet_with_applicability(
-                        cx,
-                        first.span.source_callsite().to(last.span.source_callsite()),
-                        "..",
-                        &mut applicability,
-                    );
-                    (macro_call.span, format!("({snippet})"))
-                },
-                _ => return,
-            };
-
             self.prev_ctxt = cur_syntax_ctxt;
 
-            span_lint_and_sugg(
+            span_lint_and_then(
                 cx,
                 DBG_MACRO,
-                sugg_span,
+                macro_call.span,
                 "the `dbg!` macro is intended as a debugging tool",
-                "remove the invocation before committing it to a version control system",
-                suggestion,
-                applicability,
+                |diag| {
+                    let mut applicability = Applicability::MachineApplicable;
+
+                    let (sugg_span, suggestion) = match expr.peel_drop_temps().kind {
+                        // dbg!()
+                        ExprKind::Block(..) => {
+                            // If the `dbg!` macro is a "free" statement and not contained within other expressions,
+                            // remove the whole statement.
+                            if let Node::Stmt(_) = cx.tcx.parent_hir_node(expr.hir_id)
+                                && let Some(semi_span) = cx.sess().source_map().mac_call_stmt_semi_span(macro_call.span)
+                            {
+                                (macro_call.span.to(semi_span), String::new())
+                            } else {
+                                (macro_call.span, String::from("()"))
+                            }
+                        },
+                        // dbg!(1)
+                        ExprKind::Match(val, ..) => (
+                            macro_call.span,
+                            snippet_with_applicability(cx, val.span.source_callsite(), "..", &mut applicability)
+                                .to_string(),
+                        ),
+                        // dbg!(2, 3)
+                        ExprKind::Tup(
+                            [
+                                Expr {
+                                    kind: ExprKind::Match(first, ..),
+                                    ..
+                                },
+                                ..,
+                                Expr {
+                                    kind: ExprKind::Match(last, ..),
+                                    ..
+                                },
+                            ],
+                        ) => {
+                            let snippet = snippet_with_applicability(
+                                cx,
+                                first.span.source_callsite().to(last.span.source_callsite()),
+                                "..",
+                                &mut applicability,
+                            );
+                            (macro_call.span, format!("({snippet})"))
+                        },
+                        _ => unreachable!(),
+                    };
+
+                    diag.span_suggestion(
+                        sugg_span,
+                        "remove the invocation before committing it to a version control system",
+                        suggestion,
+                        applicability,
+                    );
+                },
             );
         }
     }
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index 69f9eb6842bcd..3fb083dd83310 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -14,8 +14,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     #[cfg(feature = "internal")]
     crate::utils::internal_lints::invalid_paths::INVALID_PATHS_INFO,
     #[cfg(feature = "internal")]
-    crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_DEPRECATION_REASON_INFO,
-    #[cfg(feature = "internal")]
     crate::utils::internal_lints::lint_without_lint_pass::DEFAULT_LINT_INFO,
     #[cfg(feature = "internal")]
     crate::utils::internal_lints::lint_without_lint_pass::INVALID_CLIPPY_VERSION_ATTRIBUTE_INFO,
@@ -741,6 +739,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::unused_async::UNUSED_ASYNC_INFO,
     crate::unused_io_amount::UNUSED_IO_AMOUNT_INFO,
     crate::unused_peekable::UNUSED_PEEKABLE_INFO,
+    crate::unused_result_ok::UNUSED_RESULT_OK_INFO,
     crate::unused_rounding::UNUSED_ROUNDING_INFO,
     crate::unused_self::UNUSED_SELF_INFO,
     crate::unused_unit::UNUSED_UNIT_INFO,
diff --git a/clippy_lints/src/default.rs b/clippy_lints/src/default.rs
index 72fa05be3cc60..0b7279f2b360d 100644
--- a/clippy_lints/src/default.rs
+++ b/clippy_lints/src/default.rs
@@ -221,7 +221,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
                         .map(ToString::to_string)
                         .collect::<Vec<_>>()
                         .join(", ");
-                    format!("{adt_def_ty_name}::<{}>", &tys_str)
+                    format!("{adt_def_ty_name}::<{tys_str}>")
                 } else {
                     binding_type.to_string()
                 };
diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs
index 9af73db6849f4..a74b3a8c8362c 100644
--- a/clippy_lints/src/default_numeric_fallback.rs
+++ b/clippy_lints/src/default_numeric_fallback.rs
@@ -92,20 +92,8 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
             let (suffix, is_float) = match lit_ty.kind() {
                 ty::Int(IntTy::I32) => ("i32", false),
                 ty::Float(FloatTy::F64) => ("f64", true),
-                // Default numeric fallback never results in other types.
                 _ => return,
             };
-
-            let src = if let Some(src) = snippet_opt(self.cx, lit.span) {
-                src
-            } else {
-                match lit.node {
-                    LitKind::Int(src, _) => format!("{src}"),
-                    LitKind::Float(src, _) => format!("{src}"),
-                    _ => return,
-                }
-            };
-            let sugg = numeric_literal::format(&src, Some(suffix), is_float);
             span_lint_hir_and_then(
                 self.cx,
                 DEFAULT_NUMERIC_FALLBACK,
@@ -113,6 +101,17 @@ impl<'a, 'tcx> NumericFallbackVisitor<'a, 'tcx> {
                 lit.span,
                 "default numeric fallback might occur",
                 |diag| {
+                    let src = if let Some(src) = snippet_opt(self.cx, lit.span) {
+                        src
+                    } else {
+                        match lit.node {
+                            LitKind::Int(src, _) => format!("{src}"),
+                            LitKind::Float(src, _) => format!("{src}"),
+                            _ => unreachable!("Default numeric fallback never results in other types"),
+                        }
+                    };
+
+                    let sugg = numeric_literal::format(&src, Some(suffix), is_float);
                     diag.span_suggestion(lit.span, "consider adding suffix", sugg, Applicability::MaybeIncorrect);
                 },
             );
diff --git a/clippy_lints/src/default_union_representation.rs b/clippy_lints/src/default_union_representation.rs
index 3fa9bad0d03da..9f020d3081c40 100644
--- a/clippy_lints/src/default_union_representation.rs
+++ b/clippy_lints/src/default_union_representation.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_hir::{HirId, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::layout::LayoutOf;
@@ -56,16 +56,18 @@ impl<'tcx> LateLintPass<'tcx> for DefaultUnionRepresentation {
             && is_union_with_two_non_zst_fields(cx, item)
             && !has_c_repr_attr(cx, item.hir_id())
         {
-            span_lint_and_help(
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(
                 cx,
                 DEFAULT_UNION_REPRESENTATION,
                 item.span,
                 "this union has the default representation",
-                None,
-                format!(
-                    "consider annotating `{}` with `#[repr(C)]` to explicitly specify memory layout",
-                    cx.tcx.def_path_str(item.owner_id)
-                ),
+                |diag| {
+                    diag.help(format!(
+                        "consider annotating `{}` with `#[repr(C)]` to explicitly specify memory layout",
+                        cx.tcx.def_path_str(item.owner_id)
+                    ));
+                },
             );
         }
     }
diff --git a/clippy_lints/src/deprecated_lints.rs b/clippy_lints/src/deprecated_lints.rs
index a0900f46f6aa6..0066ed643251a 100644
--- a/clippy_lints/src/deprecated_lints.rs
+++ b/clippy_lints/src/deprecated_lints.rs
@@ -1,243 +1,181 @@
-// NOTE: Entries should be created with `cargo dev deprecate`
-
-/// This struct fakes the `Lint` declaration that is usually created by `declare_lint!`. This
-/// enables the simple extraction of the metadata without changing the current deprecation
-/// declaration.
-pub struct ClippyDeprecatedLint {
-    #[allow(dead_code)]
-    pub desc: &'static str,
-}
-
-#[macro_export]
-macro_rules! declare_deprecated_lint {
-    { $(#[$attr:meta])* pub $name: ident, $reason: literal} => {
-        $(#[$attr])*
-        #[allow(dead_code)]
-        pub static $name: ClippyDeprecatedLint = ClippyDeprecatedLint {
-            desc: $reason
-        };
-    }
-}
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// This used to check for `assert!(a == b)` and recommend
-    /// replacement with `assert_eq!(a, b)`, but this is no longer needed after RFC 2011.
+// This file is managed by `cargo dev rename_lint` and `cargo dev deprecate_lint`.
+// Prefer to use those when possible.
+
+macro_rules! declare_with_version {
+    ($name:ident($name_version:ident): &[$ty:ty] = &[$(
+        #[clippy::version = $version:literal]
+        $e:expr,
+    )*]) => {
+        pub static $name: &[$ty] = &[$($e),*];
+        #[allow(unused)]
+        pub static $name_version: &[&str] = &[$($version),*];
+    };
+}
+
+#[rustfmt::skip]
+declare_with_version! { DEPRECATED(DEPRECATED_VERSION): &[(&str, &str)] = &[
     #[clippy::version = "pre 1.29.0"]
-    pub SHOULD_ASSERT_EQ,
-    "`assert!()` will be more flexible with RFC 2011"
-}
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// This used to check for `Vec::extend`, which was slower than
-    /// `Vec::extend_from_slice`. Thanks to specialization, this is no longer true.
+    ("clippy::should_assert_eq", "`assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can"),
     #[clippy::version = "pre 1.29.0"]
-    pub EXTEND_FROM_SLICE,
-    "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice"
-}
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// `Range::step_by(0)` used to be linted since it's
-    /// an infinite iterator, which is better expressed by `iter::repeat`,
-    /// but the method has been removed for `Iterator::step_by` which panics
-    /// if given a zero
+    ("clippy::extend_from_slice", "`Vec::extend_from_slice` is no longer faster than `Vec::extend` due to specialization"),
     #[clippy::version = "pre 1.29.0"]
-    pub RANGE_STEP_BY_ZERO,
-    "`iterator.step_by(0)` panics nowadays"
-}
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// This used to check for `Vec::as_slice`, which was unstable with good
-    /// stable alternatives. `Vec::as_slice` has now been stabilized.
+    ("clippy::range_step_by_zero", "`Iterator::step_by(0)` now panics and is no longer an infinite iterator"),
     #[clippy::version = "pre 1.29.0"]
-    pub UNSTABLE_AS_SLICE,
-    "`Vec::as_slice` has been stabilized in 1.7"
-}
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// This used to check for `Vec::as_mut_slice`, which was unstable with good
-    /// stable alternatives. `Vec::as_mut_slice` has now been stabilized.
+    ("clippy::unstable_as_slice", "`Vec::as_slice` is now stable"),
     #[clippy::version = "pre 1.29.0"]
-    pub UNSTABLE_AS_MUT_SLICE,
-    "`Vec::as_mut_slice` has been stabilized in 1.7"
-}
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// This lint should never have applied to non-pointer types, as transmuting
-    /// between non-pointer types of differing alignment is well-defined behavior (it's semantically
-    /// equivalent to a memcpy). This lint has thus been refactored into two separate lints:
-    /// cast_ptr_alignment and transmute_ptr_to_ptr.
+    ("clippy::unstable_as_mut_slice", "`Vec::as_mut_slice` is now stable"),
     #[clippy::version = "pre 1.29.0"]
-    pub MISALIGNED_TRANSMUTE,
-    "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr"
-}
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// This lint is too subjective, not having a good reason for being in clippy.
-    /// Additionally, compound assignment operators may be overloaded separately from their non-assigning
-    /// counterparts, so this lint may suggest a change in behavior or the code may not compile.
+    ("clippy::misaligned_transmute", "split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr`"),
     #[clippy::version = "1.30.0"]
-    pub ASSIGN_OPS,
-    "using compound assignment operators (e.g., `+=`) is harmless"
-}
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// The original rule will only lint for `if let`. After
-    /// making it support to lint `match`, naming as `if let` is not suitable for it.
-    /// So, this lint is deprecated.
+    ("clippy::assign_ops", "compound operators are harmless and linting on them is not in scope for clippy"),
     #[clippy::version = "pre 1.29.0"]
-    pub IF_LET_REDUNDANT_PATTERN_MATCHING,
-    "this lint has been changed to redundant_pattern_matching"
-}
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// This lint used to suggest replacing `let mut vec =
-    /// Vec::with_capacity(n); vec.set_len(n);` with `let vec = vec![0; n];`. The
-    /// replacement has very different performance characteristics so the lint is
-    /// deprecated.
-    #[clippy::version = "pre 1.29.0"]
-    pub UNSAFE_VECTOR_INITIALIZATION,
-    "the replacement suggested by this lint had substantially different behavior"
-}
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// This lint has been superseded by #[must_use] in rustc.
+    ("clippy::unsafe_vector_initialization", "the suggested alternative could be substantially slower"),
     #[clippy::version = "1.39.0"]
-    pub UNUSED_COLLECT,
-    "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint"
-}
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// Associated-constants are now preferred.
+    ("clippy::unused_collect", "`Iterator::collect` is now marked as `#[must_use]`"),
     #[clippy::version = "1.44.0"]
-    pub REPLACE_CONSTS,
-    "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants"
-}
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// The regex! macro does not exist anymore.
+    ("clippy::replace_consts", "`min_value` and `max_value` are now deprecated"),
     #[clippy::version = "1.47.0"]
-    pub REGEX_MACRO,
-    "the regex! macro has been removed from the regex crate in 2018"
-}
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// This lint has been replaced by `manual_find_map`, a
-    /// more specific lint.
-    #[clippy::version = "1.51.0"]
-    pub FIND_MAP,
-    "this lint has been replaced by `manual_find_map`, a more specific lint"
-}
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// This lint has been replaced by `manual_filter_map`, a
-    /// more specific lint.
-    #[clippy::version = "1.53.0"]
-    pub FILTER_MAP,
-    "this lint has been replaced by `manual_filter_map`, a more specific lint"
-}
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// The `avoid_breaking_exported_api` config option was added, which
-    /// enables the `enum_variant_names` lint for public items.
+    ("clippy::regex_macro", "the `regex!` macro was removed from the regex crate in 2018"),
     #[clippy::version = "1.54.0"]
-    pub PUB_ENUM_VARIANT_NAMES,
-    "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items"
-}
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// The `avoid_breaking_exported_api` config option was added, which
-    /// enables the `wrong_self_conversion` lint for public items.
+    ("clippy::pub_enum_variant_names", "`clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config"),
     #[clippy::version = "1.54.0"]
-    pub WRONG_PUB_SELF_CONVENTION,
-    "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items"
-}
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// This lint has been superseded by rustc's own [`unexpected_cfgs`] lint that is able to detect the `#[cfg(features)]` and `#[cfg(tests)]` typos.
-    ///
-    /// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs
+    ("clippy::wrong_pub_self_convention", "`clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config"),
+    // end deprecated lints. used by `cargo dev deprecate_lint`
+]}
+
+#[rustfmt::skip]
+declare_with_version! { RENAMED(RENAMED_VERSION): &[(&str, &str)] = &[
+    #[clippy::version = ""]
+    ("clippy::almost_complete_letter_range", "clippy::almost_complete_range"),
+    #[clippy::version = ""]
+    ("clippy::blacklisted_name", "clippy::disallowed_names"),
+    #[clippy::version = ""]
+    ("clippy::block_in_if_condition_expr", "clippy::blocks_in_conditions"),
+    #[clippy::version = ""]
+    ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_conditions"),
+    #[clippy::version = ""]
+    ("clippy::blocks_in_if_conditions", "clippy::blocks_in_conditions"),
+    #[clippy::version = ""]
+    ("clippy::box_vec", "clippy::box_collection"),
+    #[clippy::version = ""]
+    ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"),
+    #[clippy::version = ""]
+    ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"),
+    #[clippy::version = ""]
+    ("clippy::derive_hash_xor_eq", "clippy::derived_hash_with_manual_eq"),
+    #[clippy::version = ""]
+    ("clippy::disallowed_method", "clippy::disallowed_methods"),
+    #[clippy::version = ""]
+    ("clippy::disallowed_type", "clippy::disallowed_types"),
+    #[clippy::version = ""]
+    ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"),
+    #[clippy::version = "1.51.0"]
+    ("clippy::find_map", "clippy::manual_find_map"),
+    #[clippy::version = "1.53.0"]
+    ("clippy::filter_map", "clippy::manual_filter_map"),
+    #[clippy::version = ""]
+    ("clippy::identity_conversion", "clippy::useless_conversion"),
+    #[clippy::version = "pre 1.29.0"]
+    ("clippy::if_let_redundant_pattern_matching", "clippy::redundant_pattern_matching"),
+    #[clippy::version = ""]
+    ("clippy::if_let_some_result", "clippy::match_result_ok"),
+    #[clippy::version = ""]
+    ("clippy::incorrect_clone_impl_on_copy_type", "clippy::non_canonical_clone_impl"),
+    #[clippy::version = ""]
+    ("clippy::incorrect_partial_ord_impl_on_ord_type", "clippy::non_canonical_partial_ord_impl"),
+    #[clippy::version = ""]
+    ("clippy::integer_arithmetic", "clippy::arithmetic_side_effects"),
+    #[clippy::version = ""]
+    ("clippy::logic_bug", "clippy::overly_complex_bool_expr"),
+    #[clippy::version = ""]
+    ("clippy::new_without_default_derive", "clippy::new_without_default"),
+    #[clippy::version = ""]
+    ("clippy::option_and_then_some", "clippy::bind_instead_of_map"),
+    #[clippy::version = ""]
+    ("clippy::option_expect_used", "clippy::expect_used"),
+    #[clippy::version = ""]
+    ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"),
+    #[clippy::version = ""]
+    ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"),
+    #[clippy::version = ""]
+    ("clippy::option_unwrap_used", "clippy::unwrap_used"),
+    #[clippy::version = ""]
+    ("clippy::overflow_check_conditional", "clippy::panicking_overflow_checks"),
+    #[clippy::version = ""]
+    ("clippy::ref_in_deref", "clippy::needless_borrow"),
+    #[clippy::version = ""]
+    ("clippy::result_expect_used", "clippy::expect_used"),
+    #[clippy::version = ""]
+    ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"),
+    #[clippy::version = ""]
+    ("clippy::result_unwrap_used", "clippy::unwrap_used"),
+    #[clippy::version = ""]
+    ("clippy::single_char_push_str", "clippy::single_char_add_str"),
+    #[clippy::version = ""]
+    ("clippy::stutter", "clippy::module_name_repetitions"),
+    #[clippy::version = ""]
+    ("clippy::thread_local_initializer_can_be_made_const", "clippy::missing_const_for_thread_local"),
+    #[clippy::version = ""]
+    ("clippy::to_string_in_display", "clippy::recursive_format_impl"),
+    #[clippy::version = ""]
+    ("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"),
+    #[clippy::version = ""]
+    ("clippy::zero_width_space", "clippy::invisible_characters"),
+    #[clippy::version = ""]
+    ("clippy::cast_ref_to_mut", "invalid_reference_casting"),
+    #[clippy::version = ""]
+    ("clippy::clone_double_ref", "suspicious_double_ref_op"),
+    #[clippy::version = ""]
+    ("clippy::cmp_nan", "invalid_nan_comparisons"),
+    #[clippy::version = ""]
+    ("clippy::drop_bounds", "drop_bounds"),
+    #[clippy::version = ""]
+    ("clippy::drop_copy", "dropping_copy_types"),
+    #[clippy::version = ""]
+    ("clippy::drop_ref", "dropping_references"),
+    #[clippy::version = ""]
+    ("clippy::fn_null_check", "useless_ptr_null_checks"),
+    #[clippy::version = ""]
+    ("clippy::for_loop_over_option", "for_loops_over_fallibles"),
+    #[clippy::version = ""]
+    ("clippy::for_loop_over_result", "for_loops_over_fallibles"),
+    #[clippy::version = ""]
+    ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"),
+    #[clippy::version = ""]
+    ("clippy::forget_copy", "forgetting_copy_types"),
+    #[clippy::version = ""]
+    ("clippy::forget_ref", "forgetting_references"),
+    #[clippy::version = ""]
+    ("clippy::into_iter_on_array", "array_into_iter"),
+    #[clippy::version = ""]
+    ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"),
+    #[clippy::version = ""]
+    ("clippy::invalid_ref", "invalid_value"),
+    #[clippy::version = ""]
+    ("clippy::invalid_utf8_in_unchecked", "invalid_from_utf8_unchecked"),
+    #[clippy::version = ""]
+    ("clippy::let_underscore_drop", "let_underscore_drop"),
     #[clippy::version = "1.80.0"]
-    pub MAYBE_MISUSED_CFG,
-    "this lint has been replaced by `unexpected_cfgs`"
-}
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// This lint has been superseded by rustc's own [`unexpected_cfgs`] lint that is able to detect invalid `#[cfg(linux)]` attributes.
-    ///
-    /// [`unexpected_cfgs`]: https://doc.rust-lang.org/rustc/lints/listing/warn-by-default.html#unexpected-cfgs
+    ("clippy::maybe_misused_cfg", "unexpected_cfgs"),
+    #[clippy::version = ""]
+    ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"),
     #[clippy::version = "1.80.0"]
-    pub MISMATCHED_TARGET_OS,
-    "this lint has been replaced by `unexpected_cfgs`"
-}
+    ("clippy::mismatched_target_os", "unexpected_cfgs"),
+    #[clippy::version = ""]
+    ("clippy::panic_params", "non_fmt_panics"),
+    #[clippy::version = ""]
+    ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"),
+    #[clippy::version = ""]
+    ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"),
+    #[clippy::version = ""]
+    ("clippy::undropped_manually_drops", "undropped_manually_drops"),
+    #[clippy::version = ""]
+    ("clippy::unknown_clippy_lints", "unknown_lints"),
+    #[clippy::version = ""]
+    ("clippy::unused_label", "unused_labels"),
+    #[clippy::version = ""]
+    ("clippy::vtable_address_comparisons", "ambiguous_wide_pointer_comparisons"),
+    #[clippy::version = ""]
+    ("clippy::reverse_range_loop", "clippy::reversed_empty_ranges"),
+    // end renamed lints. used by `cargo dev rename_lint`
+]}
diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs
index 253f9959e13e7..d0cb24884686a 100644
--- a/clippy_lints/src/dereference.rs
+++ b/clippy_lints/src/dereference.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
-use clippy_utils::ty::{implements_trait, is_manually_drop, peel_mid_ty_refs};
+use clippy_utils::ty::{implements_trait, is_manually_drop};
 use clippy_utils::{
     expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, peel_middle_ty_refs, DefinedTy,
     ExprUseNode,
@@ -947,7 +947,7 @@ fn report<'tcx>(
             let (expr_str, _expr_is_macro_call) =
                 snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app);
             let ty = typeck.expr_ty(expr);
-            let (_, ref_count) = peel_mid_ty_refs(ty);
+            let (_, ref_count) = peel_middle_ty_refs(ty);
             let deref_str = if ty_changed_count >= ref_count && ref_count != 0 {
                 // a deref call changing &T -> &U requires two deref operators the first time
                 // this occurs. One to remove the reference, a second to call the deref impl.
diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs
index 5b6a5b08aa94c..d7b3a7c74f3ce 100644
--- a/clippy_lints/src/doc/mod.rs
+++ b/clippy_lints/src/doc/mod.rs
@@ -5,7 +5,7 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 use clippy_utils::macros::{is_panic, root_macro_call_first_node};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::visitors::Visitable;
-use clippy_utils::{in_constant, is_entrypoint_fn, is_trait_impl_item, method_chain_args};
+use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args};
 use pulldown_cmark::Event::{
     Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start,
     TaskListMarker, Text,
@@ -768,7 +768,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
                     );
                 }
             },
-            FootnoteReference(text) | Text(text) => {
+            Text(text) => {
                 paragraph_range.end = range.end;
                 let range_ = range.clone();
                 ticks_unbalanced |= text.contains('`')
@@ -812,7 +812,8 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
                     }
                     text_to_check.push((text, range, code_level));
                 }
-            },
+            }
+            FootnoteReference(_) => {}
         }
     }
     headers
@@ -857,7 +858,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
                     "assert" | "assert_eq" | "assert_ne"
                 )
             {
-                self.is_const = in_constant(self.cx, expr.hir_id);
+                self.is_const = self.cx.tcx.hir().is_inside_const_context(expr.hir_id);
                 self.panic_span = Some(macro_call.span);
             }
         }
diff --git a/clippy_lints/src/drop_forget_ref.rs b/clippy_lints/src/drop_forget_ref.rs
index 4a6ffcd9a7888..c7dd7292a14be 100644
--- a/clippy_lints/src/drop_forget_ref.rs
+++ b/clippy_lints/src/drop_forget_ref.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_must_use_func_call;
 use clippy_utils::ty::{is_copy, is_must_use_ty, is_type_lang_item};
 use rustc_hir::{Arm, Expr, ExprKind, LangItem, Node};
@@ -126,14 +126,14 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
                 },
                 _ => return,
             };
-            span_lint_and_note(
-                cx,
-                lint,
-                expr.span,
-                msg,
-                note_span,
-                format!("argument has type `{arg_ty}`"),
-            );
+            span_lint_and_then(cx, lint, expr.span, msg, |diag| {
+                let note = format!("argument has type `{arg_ty}`");
+                if let Some(span) = note_span {
+                    diag.span_note(span, note);
+                } else {
+                    diag.note(note);
+                }
+            });
         }
     }
 }
diff --git a/clippy_lints/src/else_if_without_else.rs b/clippy_lints/src/else_if_without_else.rs
index 7a6dc46972767..02f9c2c36488d 100644
--- a/clippy_lints/src/else_if_without_else.rs
+++ b/clippy_lints/src/else_if_without_else.rs
@@ -1,6 +1,6 @@
 //! Lint on if expressions with an else if, but without a final else branch.
 
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_ast::ast::{Expr, ExprKind};
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -54,13 +54,15 @@ impl EarlyLintPass for ElseIfWithoutElse {
             && let ExprKind::If(_, _, None) = els.kind
             && !in_external_macro(cx.sess(), item.span)
         {
-            span_lint_and_help(
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(
                 cx,
                 ELSE_IF_WITHOUT_ELSE,
                 els.span,
                 "`if` expression with an `else if`, but without a final `else`",
-                None,
-                "add an `else` block here",
+                |diag| {
+                    diag.help("add an `else` block here");
+                },
             );
         }
     }
diff --git a/clippy_lints/src/empty_drop.rs b/clippy_lints/src/empty_drop.rs
index c5fc72b5e2d8e..b66dd2108fc9b 100644
--- a/clippy_lints/src/empty_drop.rs
+++ b/clippy_lints/src/empty_drop.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::peel_blocks;
 use rustc_errors::Applicability;
 use rustc_hir::{Body, ExprKind, Impl, ImplItemKind, Item, ItemKind, Node};
@@ -50,15 +50,14 @@ impl LateLintPass<'_> for EmptyDrop {
             && block.stmts.is_empty()
             && block.expr.is_none()
         {
-            span_lint_and_sugg(
-                cx,
-                EMPTY_DROP,
-                item.span,
-                "empty drop implementation",
-                "try removing this impl",
-                String::new(),
-                Applicability::MaybeIncorrect,
-            );
+            span_lint_and_then(cx, EMPTY_DROP, item.span, "empty drop implementation", |diag| {
+                diag.span_suggestion_hidden(
+                    item.span,
+                    "try removing this impl",
+                    String::new(),
+                    Applicability::MaybeIncorrect,
+                );
+            });
         }
     }
 }
diff --git a/clippy_lints/src/endian_bytes.rs b/clippy_lints/src/endian_bytes.rs
index 5bba9c562b931..209104c5385c9 100644
--- a/clippy_lints/src/endian_bytes.rs
+++ b/clippy_lints/src/endian_bytes.rs
@@ -7,7 +7,6 @@ use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::Ty;
 use rustc_session::declare_lint_pass;
 use rustc_span::Symbol;
-use std::borrow::Cow;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -141,52 +140,6 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix
         _ => return,
     };
 
-    let mut help = None;
-
-    'build_help: {
-        // all lints disallowed, don't give help here
-        if [&[lint], other_lints.as_slice()]
-            .concat()
-            .iter()
-            .all(|lint| !lint.allowed(cx, expr))
-        {
-            break 'build_help;
-        }
-
-        // ne_bytes and all other lints allowed
-        if lint.as_name(prefix) == ne && other_lints.iter().all(|lint| lint.allowed(cx, expr)) {
-            help = Some(Cow::Borrowed("specify the desired endianness explicitly"));
-            break 'build_help;
-        }
-
-        // le_bytes where ne_bytes allowed but be_bytes is not, or le_bytes where ne_bytes allowed but
-        // le_bytes is not
-        if (lint.as_name(prefix) == le || lint.as_name(prefix) == be) && LintKind::Host.allowed(cx, expr) {
-            help = Some(Cow::Borrowed("use the native endianness instead"));
-            break 'build_help;
-        }
-
-        let allowed_lints = other_lints.iter().filter(|lint| lint.allowed(cx, expr));
-        let len = allowed_lints.clone().count();
-
-        let mut help_str = "use ".to_owned();
-
-        for (i, lint) in allowed_lints.enumerate() {
-            let only_one = len == 1;
-            if !only_one {
-                help_str.push_str("either of ");
-            }
-
-            help_str.push_str(&format!("`{ty}::{}` ", lint.as_name(prefix)));
-
-            if i != len && !only_one {
-                help_str.push_str("or ");
-            }
-        }
-
-        help = Some(Cow::Owned(help_str + "instead"));
-    }
-
     span_lint_and_then(
         cx,
         lint.as_lint(),
@@ -198,9 +151,47 @@ fn maybe_lint_endian_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, prefix: Prefix
             if prefix == Prefix::To { " method" } else { "" },
         ),
         move |diag| {
-            if let Some(help) = help {
-                diag.help(help);
+            // all lints disallowed, don't give help here
+            if [&[lint], other_lints.as_slice()]
+                .concat()
+                .iter()
+                .all(|lint| !lint.allowed(cx, expr))
+            {
+                return;
+            }
+
+            // ne_bytes and all other lints allowed
+            if lint.as_name(prefix) == ne && other_lints.iter().all(|lint| lint.allowed(cx, expr)) {
+                diag.help("specify the desired endianness explicitly");
+                return;
+            }
+
+            // le_bytes where ne_bytes allowed but be_bytes is not, or le_bytes where ne_bytes allowed but
+            // le_bytes is not
+            if (lint.as_name(prefix) == le || lint.as_name(prefix) == be) && LintKind::Host.allowed(cx, expr) {
+                diag.help("use the native endianness instead");
+                return;
+            }
+
+            let allowed_lints = other_lints.iter().filter(|lint| lint.allowed(cx, expr));
+            let len = allowed_lints.clone().count();
+
+            let mut help_str = "use ".to_owned();
+
+            for (i, lint) in allowed_lints.enumerate() {
+                let only_one = len == 1;
+                if !only_one {
+                    help_str.push_str("either of ");
+                }
+
+                help_str.push_str(&format!("`{ty}::{}` ", lint.as_name(prefix)));
+
+                if i != len && !only_one {
+                    help_str.push_str("or ");
+                }
             }
+            help_str.push_str("instead");
+            diag.help(help_str);
         },
     );
 }
diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs
index 30eb643c42ece..e54cd248ead29 100644
--- a/clippy_lints/src/enum_clike.rs
+++ b/clippy_lints/src/enum_clike.rs
@@ -51,7 +51,7 @@ impl<'tcx> LateLintPass<'tcx> for UnportableVariant {
                         .const_eval_poly(def_id.to_def_id())
                         .ok()
                         .map(|val| rustc_middle::mir::Const::from_value(val, ty));
-                    if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx, c)) {
+                    if let Some(Constant::Int(val)) = constant.and_then(|c| mir_to_const(cx.tcx, c)) {
                         if let ty::Adt(adt, _) = ty.kind() {
                             if adt.is_enum() {
                                 ty = adt.repr().discr_type().to_ty(cx.tcx);
diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs
index 0ed7859418bc5..5a7226d590c4d 100644
--- a/clippy_lints/src/eta_reduction.rs
+++ b/clippy_lints/src/eta_reduction.rs
@@ -9,8 +9,7 @@ use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Saf
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{
-    self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, RegionKind, Ty, TyCtxt,
-    TypeVisitableExt, TypeckResults,
+    self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, TypeckResults,
 };
 use rustc_session::declare_lint_pass;
 use rustc_span::symbol::sym;
@@ -74,159 +73,184 @@ declare_clippy_lint! {
 declare_lint_pass!(EtaReduction => [REDUNDANT_CLOSURE, REDUNDANT_CLOSURE_FOR_METHOD_CALLS]);
 
 impl<'tcx> LateLintPass<'tcx> for EtaReduction {
-    #[allow(clippy::too_many_lines)]
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        let body = if let ExprKind::Closure(c) = expr.kind
-            && c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer))
-            && matches!(c.fn_decl.output, FnRetTy::DefaultReturn(_))
-            && !expr.span.from_expansion()
-        {
-            cx.tcx.hir().body(c.body)
-        } else {
-            return;
-        };
-
-        if body.value.span.from_expansion() {
-            if body.params.is_empty() {
-                if let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) {
-                    // replace `|| vec![]` with `Vec::new`
-                    span_lint_and_sugg(
-                        cx,
-                        REDUNDANT_CLOSURE,
-                        expr.span,
-                        "redundant closure",
-                        "replace the closure with `Vec::new`",
-                        "std::vec::Vec::new".into(),
-                        Applicability::MachineApplicable,
-                    );
-                }
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
+        if let ExprKind::MethodCall(_method, receiver, args, _) = expr.kind {
+            for arg in args {
+                check_clousure(cx, Some(receiver), arg);
             }
-            // skip `foo(|| macro!())`
-            return;
         }
+        if let ExprKind::Call(func, args) = expr.kind {
+            check_clousure(cx, None, func);
+            for arg in args {
+                check_clousure(cx, None, arg);
+            }
+        }
+    }
+}
 
-        let typeck = cx.typeck_results();
-        let closure = if let ty::Closure(_, closure_subs) = typeck.expr_ty(expr).kind() {
-            closure_subs.as_closure()
-        } else {
-            return;
-        };
+#[allow(clippy::too_many_lines)]
+fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tcx>>, expr: &Expr<'tcx>) {
+    let body = if let ExprKind::Closure(c) = expr.kind
+        && c.fn_decl.inputs.iter().all(|ty| matches!(ty.kind, TyKind::Infer))
+        && matches!(c.fn_decl.output, FnRetTy::DefaultReturn(_))
+        && !expr.span.from_expansion()
+    {
+        cx.tcx.hir().body(c.body)
+    } else {
+        return;
+    };
 
-        if is_adjusted(cx, body.value) {
-            return;
+    if body.value.span.from_expansion() {
+        if body.params.is_empty() {
+            if let Some(VecArgs::Vec(&[])) = VecArgs::hir(cx, body.value) {
+                // replace `|| vec![]` with `Vec::new`
+                span_lint_and_sugg(
+                    cx,
+                    REDUNDANT_CLOSURE,
+                    expr.span,
+                    "redundant closure",
+                    "replace the closure with `Vec::new`",
+                    "std::vec::Vec::new".into(),
+                    Applicability::MachineApplicable,
+                );
+            }
         }
+        // skip `foo(|| macro!())`
+        return;
+    }
 
-        match body.value.kind {
-            ExprKind::Call(callee, args)
-                if matches!(
-                    callee.kind,
-                    ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..))
-                ) =>
+    if is_adjusted(cx, body.value) {
+        return;
+    }
+
+    let typeck = cx.typeck_results();
+    let closure = if let ty::Closure(_, closure_subs) = typeck.expr_ty(expr).kind() {
+        closure_subs.as_closure()
+    } else {
+        return;
+    };
+    let closure_sig = cx.tcx.signature_unclosure(closure.sig(), Safety::Safe).skip_binder();
+    match body.value.kind {
+        ExprKind::Call(callee, args)
+            if matches!(
+                callee.kind,
+                ExprKind::Path(QPath::Resolved(..) | QPath::TypeRelative(..))
+            ) =>
+        {
+            let callee_ty_raw = typeck.expr_ty(callee);
+            let callee_ty = callee_ty_raw.peel_refs();
+            if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc))
+                || !check_inputs(typeck, body.params, None, args)
             {
-                let callee_ty_raw = typeck.expr_ty(callee);
-                let callee_ty = callee_ty_raw.peel_refs();
-                if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc))
-                    || !check_inputs(typeck, body.params, None, args)
-                {
-                    return;
-                }
-                let callee_ty_adjusted = typeck
-                    .expr_adjustments(callee)
-                    .last()
-                    .map_or(callee_ty, |a| a.target.peel_refs());
+                return;
+            }
+            let callee_ty_adjusted = typeck
+                .expr_adjustments(callee)
+                .last()
+                .map_or(callee_ty, |a| a.target.peel_refs());
 
-                let sig = match callee_ty_adjusted.kind() {
-                    ty::FnDef(def, _) => {
-                        // Rewriting `x(|| f())` to `x(f)` where f is marked `#[track_caller]` moves the `Location`
-                        if cx.tcx.has_attr(*def, sym::track_caller) {
-                            return;
-                        }
+            let sig = match callee_ty_adjusted.kind() {
+                ty::FnDef(def, _) => {
+                    // Rewriting `x(|| f())` to `x(f)` where f is marked `#[track_caller]` moves the `Location`
+                    if cx.tcx.has_attr(*def, sym::track_caller) {
+                        return;
+                    }
 
-                        cx.tcx.fn_sig(def).skip_binder().skip_binder()
-                    },
-                    ty::FnPtr(sig) => sig.skip_binder(),
-                    ty::Closure(_, subs) => cx
-                        .tcx
-                        .signature_unclosure(subs.as_closure().sig(), Safety::Safe)
-                        .skip_binder(),
-                    _ => {
-                        if typeck.type_dependent_def_id(body.value.hir_id).is_some()
-                            && let subs = typeck.node_args(body.value.hir_id)
-                            && let output = typeck.expr_ty(body.value)
-                            && let ty::Tuple(tys) = *subs.type_at(1).kind()
-                        {
-                            cx.tcx.mk_fn_sig(tys, output, false, Safety::Safe, Abi::Rust)
-                        } else {
-                            return;
-                        }
-                    },
-                };
-                if check_sig(cx, closure, sig)
-                    && let generic_args = typeck.node_args(callee.hir_id)
-                    // Given some trait fn `fn f() -> ()` and some type `T: Trait`, `T::f` is not
-                    // `'static` unless `T: 'static`. The cast `T::f as fn()` will, however, result
-                    // in a type which is `'static`.
-                    // For now ignore all callee types which reference a type parameter.
-                    && !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_)))
-                {
-                    span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
-                        if let Some(mut snippet) = snippet_opt(cx, callee.span) {
-                            if path_to_local(callee).map_or(false, |l| {
-                                // FIXME: Do we really need this `local_used_in` check?
-                                // Isn't it checking something like... `callee(callee)`?
-                                // If somehow this check is needed, add some test for it,
-                                // 'cuz currently nothing changes after deleting this check.
-                                local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr)
-                            }) {
-                                match cx.tcx.infer_ctxt().build().err_ctxt().type_implements_fn_trait(
-                                    cx.param_env,
-                                    Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
-                                    ty::PredicatePolarity::Positive,
-                                ) {
-                                    // Mutable closure is used after current expr; we cannot consume it.
-                                    Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"),
-                                    Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => {
-                                        snippet = format!("&{snippet}");
-                                    },
-                                    _ => (),
-                                }
+                    cx.tcx.fn_sig(def).skip_binder().skip_binder()
+                },
+                ty::FnPtr(sig) => sig.skip_binder(),
+                ty::Closure(_, subs) => cx
+                    .tcx
+                    .signature_unclosure(subs.as_closure().sig(), Safety::Safe)
+                    .skip_binder(),
+                _ => {
+                    if typeck.type_dependent_def_id(body.value.hir_id).is_some()
+                        && let subs = typeck.node_args(body.value.hir_id)
+                        && let output = typeck.expr_ty(body.value)
+                        && let ty::Tuple(tys) = *subs.type_at(1).kind()
+                    {
+                        cx.tcx.mk_fn_sig(tys, output, false, Safety::Safe, Abi::Rust)
+                    } else {
+                        return;
+                    }
+                },
+            };
+            if let Some(outer) = outer_receiver
+                && ty_has_static(sig.output())
+                && let generic_args = typeck.node_args(outer.hir_id)
+                // HACK: Given a closure in `T.method(|| f())`, where `fn f() -> U where U: 'static`, `T.method(f)`
+                // will succeed iff `T: 'static`. But the region of `T` is always erased by `typeck.expr_ty()` when
+                // T is a generic type. For example, return type of `Option<String>::as_deref()` is a generic.
+                // So we have a hack like this.
+                && generic_args.len() > 0
+            {
+                return;
+            }
+            if check_sig(closure_sig, sig)
+                && let generic_args = typeck.node_args(callee.hir_id)
+                // Given some trait fn `fn f() -> ()` and some type `T: Trait`, `T::f` is not
+                // `'static` unless `T: 'static`. The cast `T::f as fn()` will, however, result
+                // in a type which is `'static`.
+                // For now ignore all callee types which reference a type parameter.
+                && !generic_args.types().any(|t| matches!(t.kind(), ty::Param(_)))
+            {
+                span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
+                    if let Some(mut snippet) = snippet_opt(cx, callee.span) {
+                        if path_to_local(callee).map_or(false, |l| {
+                            // FIXME: Do we really need this `local_used_in` check?
+                            // Isn't it checking something like... `callee(callee)`?
+                            // If somehow this check is needed, add some test for it,
+                            // 'cuz currently nothing changes after deleting this check.
+                            local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr)
+                        }) {
+                            match cx.tcx.infer_ctxt().build().err_ctxt().type_implements_fn_trait(
+                                cx.param_env,
+                                Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
+                                ty::PredicatePolarity::Positive,
+                            ) {
+                                // Mutable closure is used after current expr; we cannot consume it.
+                                Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"),
+                                Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => {
+                                    snippet = format!("&{snippet}");
+                                },
+                                _ => (),
                             }
-                            diag.span_suggestion(
-                                expr.span,
-                                "replace the closure with the function itself",
-                                snippet,
-                                Applicability::MachineApplicable,
-                            );
                         }
-                    });
-                }
-            },
-            ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => {
-                if let Some(method_def_id) = typeck.type_dependent_def_id(body.value.hir_id)
-                    && !cx.tcx.has_attr(method_def_id, sym::track_caller)
-                    && check_sig(cx, closure, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder())
-                {
-                    span_lint_and_then(
-                        cx,
-                        REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
-                        expr.span,
-                        "redundant closure",
-                        |diag| {
-                            let args = typeck.node_args(body.value.hir_id);
-                            let caller = self_.hir_id.owner.def_id;
-                            let type_name = get_path_from_caller_to_method_type(cx.tcx, caller, method_def_id, args);
-                            diag.span_suggestion(
-                                expr.span,
-                                "replace the closure with the method itself",
-                                format!("{}::{}", type_name, path.ident.name),
-                                Applicability::MachineApplicable,
-                            );
-                        },
-                    );
-                }
-            },
-            _ => (),
-        }
+                        diag.span_suggestion(
+                            expr.span,
+                            "replace the closure with the function itself",
+                            snippet,
+                            Applicability::MachineApplicable,
+                        );
+                    }
+                });
+            }
+        },
+        ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => {
+            if let Some(method_def_id) = typeck.type_dependent_def_id(body.value.hir_id)
+                && !cx.tcx.has_attr(method_def_id, sym::track_caller)
+                && check_sig(closure_sig, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder())
+            {
+                span_lint_and_then(
+                    cx,
+                    REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
+                    expr.span,
+                    "redundant closure",
+                    |diag| {
+                        let args = typeck.node_args(body.value.hir_id);
+                        let caller = self_.hir_id.owner.def_id;
+                        let type_name = get_path_from_caller_to_method_type(cx.tcx, caller, method_def_id, args);
+                        diag.span_suggestion(
+                            expr.span,
+                            "replace the closure with the method itself",
+                            format!("{}::{}", type_name, path.ident.name),
+                            Applicability::MachineApplicable,
+                        );
+                    },
+                );
+            }
+        },
+        _ => (),
     }
 }
 
@@ -251,12 +275,8 @@ fn check_inputs(
         })
 }
 
-fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure: ClosureArgs<TyCtxt<'tcx>>, call_sig: FnSig<'_>) -> bool {
-    call_sig.safety == Safety::Safe
-        && !has_late_bound_to_non_late_bound_regions(
-            cx.tcx.signature_unclosure(closure.sig(), Safety::Safe).skip_binder(),
-            call_sig,
-        )
+fn check_sig<'tcx>(closure_sig: FnSig<'tcx>, call_sig: FnSig<'tcx>) -> bool {
+    call_sig.safety == Safety::Safe && !has_late_bound_to_non_late_bound_regions(closure_sig, call_sig)
 }
 
 /// This walks through both signatures and checks for any time a late-bound region is expected by an
@@ -265,7 +285,7 @@ fn check_sig<'tcx>(cx: &LateContext<'tcx>, closure: ClosureArgs<TyCtxt<'tcx>>, c
 /// This is needed because rustc is unable to late bind early-bound regions in a function signature.
 fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<'_>) -> bool {
     fn check_region(from_region: Region<'_>, to_region: Region<'_>) -> bool {
-        matches!(from_region.kind(), RegionKind::ReBound(..)) && !matches!(to_region.kind(), RegionKind::ReBound(..))
+        from_region.is_bound() && !to_region.is_bound()
     }
 
     fn check_subs(from_subs: &[GenericArg<'_>], to_subs: &[GenericArg<'_>]) -> bool {
@@ -318,3 +338,8 @@ fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<'
         .zip(to_sig.inputs_and_output)
         .any(|(from_ty, to_ty)| check_ty(from_ty, to_ty))
 }
+
+fn ty_has_static(ty: Ty<'_>) -> bool {
+    ty.walk()
+        .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if re.is_static()))
+}
diff --git a/clippy_lints/src/exhaustive_items.rs b/clippy_lints/src/exhaustive_items.rs
index 0f4176ec73bb2..9bf3baba4b597 100644
--- a/clippy_lints/src/exhaustive_items.rs
+++ b/clippy_lints/src/exhaustive_items.rs
@@ -88,11 +88,11 @@ impl LateLintPass<'_> for ExhaustiveItems {
             && !attrs.iter().any(|a| a.has_name(sym::non_exhaustive))
             && fields.iter().all(|f| cx.tcx.visibility(f.def_id).is_public())
         {
-            let suggestion_span = item.span.shrink_to_lo();
-            let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0));
             span_lint_and_then(cx, lint, item.span, msg, |diag| {
+                let suggestion_span = item.span.shrink_to_lo();
+                let indent = " ".repeat(indent_of(cx, item.span).unwrap_or(0));
                 let sugg = format!("#[non_exhaustive]\n{indent}");
-                diag.span_suggestion(
+                diag.span_suggestion_verbose(
                     suggestion_span,
                     "try adding #[non_exhaustive]",
                     sugg,
diff --git a/clippy_lints/src/field_scoped_visibility_modifiers.rs b/clippy_lints/src/field_scoped_visibility_modifiers.rs
index bb74e345703ff..95b8e882da792 100644
--- a/clippy_lints/src/field_scoped_visibility_modifiers.rs
+++ b/clippy_lints/src/field_scoped_visibility_modifiers.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_ast::ast::{Item, ItemKind, VisibilityKind};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::declare_lint_pass;
@@ -62,13 +62,15 @@ impl EarlyLintPass for FieldScopedVisibilityModifiers {
                 // pub(self) is equivalent to not using pub at all, so we ignore it
                 continue;
             }
-            span_lint_and_help(
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(
                 cx,
                 FIELD_SCOPED_VISIBILITY_MODIFIERS,
                 field.vis.span,
                 "scoped visibility modifier on a field",
-                None,
-                "consider making the field private and adding a scoped visibility method for it",
+                |diag| {
+                    diag.help("consider making the field private and adding a scoped visibility method for it");
+                },
             );
         }
     }
diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs
index 6adcd2235dc52..f095c1add91f8 100644
--- a/clippy_lints/src/float_literal.rs
+++ b/clippy_lints/src/float_literal.rs
@@ -1,7 +1,7 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::numeric_literal;
 use rustc_ast::ast::{self, LitFloatType, LitKind};
-use rustc_errors::Applicability;
+use rustc_errors::{Applicability, SuggestionStyle};
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, FloatTy};
@@ -105,32 +105,43 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
             if is_whole && !sym_str.contains(['e', 'E']) {
                 // Normalize the literal by stripping the fractional portion
                 if sym_str.split('.').next().unwrap() != float_str {
-                    // If the type suffix is missing the suggestion would be
-                    // incorrectly interpreted as an integer so adding a `.0`
-                    // suffix to prevent that.
-                    if type_suffix.is_none() {
-                        float_str.push_str(".0");
-                    }
-
-                    span_lint_and_sugg(
+                    span_lint_and_then(
                         cx,
                         LOSSY_FLOAT_LITERAL,
                         expr.span,
                         "literal cannot be represented as the underlying type without loss of precision",
-                        "consider changing the type or replacing it with",
-                        numeric_literal::format(&float_str, type_suffix, true),
-                        Applicability::MachineApplicable,
+                        |diag| {
+                            // If the type suffix is missing the suggestion would be
+                            // incorrectly interpreted as an integer so adding a `.0`
+                            // suffix to prevent that.
+                            if type_suffix.is_none() {
+                                float_str.push_str(".0");
+                            }
+                            diag.span_suggestion_with_style(
+                                expr.span,
+                                "consider changing the type or replacing it with",
+                                numeric_literal::format(&float_str, type_suffix, true),
+                                Applicability::MachineApplicable,
+                                SuggestionStyle::ShowAlways,
+                            );
+                        },
                     );
                 }
             } else if digits > max as usize && float_str.len() < sym_str.len() {
-                span_lint_and_sugg(
+                span_lint_and_then(
                     cx,
                     EXCESSIVE_PRECISION,
                     expr.span,
                     "float has excessive precision",
-                    "consider changing the type or truncating it to",
-                    numeric_literal::format(&float_str, type_suffix, true),
-                    Applicability::MachineApplicable,
+                    |diag| {
+                        diag.span_suggestion_with_style(
+                            expr.span,
+                            "consider changing the type or truncating it to",
+                            numeric_literal::format(&float_str, type_suffix, true),
+                            Applicability::MachineApplicable,
+                            SuggestionStyle::ShowAlways,
+                        );
+                    },
                 );
             }
         }
diff --git a/clippy_lints/src/floating_point_arithmetic.rs b/clippy_lints/src/floating_point_arithmetic.rs
index 68bdf88d0a7e4..bf4bcabfe8911 100644
--- a/clippy_lints/src/floating_point_arithmetic.rs
+++ b/clippy_lints/src/floating_point_arithmetic.rs
@@ -1,9 +1,9 @@
 use clippy_utils::consts::Constant::{Int, F32, F64};
-use clippy_utils::consts::{constant, constant_simple, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::{
-    eq_expr_value, get_parent_expr, higher, in_constant, is_inherent_method_call, is_no_std_crate, numeric_literal,
-    peel_blocks, sugg,
+    eq_expr_value, get_parent_expr, higher, is_in_const_context, is_inherent_method_call, is_no_std_crate,
+    numeric_literal, peel_blocks, sugg,
 };
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, PathSegment, UnOp};
@@ -112,7 +112,7 @@ declare_lint_pass!(FloatingPointArithmetic => [
 // Returns the specialized log method for a given base if base is constant
 // and is one of 2, 10 and e
 fn get_specialized_log_method(cx: &LateContext<'_>, base: &Expr<'_>) -> Option<&'static str> {
-    if let Some(value) = constant(cx, cx.typeck_results(), base) {
+    if let Some(value) = ConstEvalCtxt::new(cx).eval(base) {
         if F32(2.0) == value || F64(2.0) == value {
             return Some("log2");
         } else if F32(10.0) == value || F64(10.0) == value {
@@ -182,10 +182,8 @@ fn check_ln1p(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>) {
         rhs,
     ) = receiver.kind
     {
-        let recv = match (
-            constant(cx, cx.typeck_results(), lhs),
-            constant(cx, cx.typeck_results(), rhs),
-        ) {
+        let ecx = ConstEvalCtxt::new(cx);
+        let recv = match (ecx.eval(lhs), ecx.eval(rhs)) {
             (Some(value), _) if F32(1.0) == value || F64(1.0) == value => rhs,
             (_, Some(value)) if F32(1.0) == value || F64(1.0) == value => lhs,
             _ => return,
@@ -230,7 +228,7 @@ fn get_integer_from_float_constant(value: &Constant<'_>) -> Option<i32> {
 
 fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
     // Check receiver
-    if let Some(value) = constant(cx, cx.typeck_results(), receiver) {
+    if let Some(value) = ConstEvalCtxt::new(cx).eval(receiver) {
         if let Some(method) = if F32(f32_consts::E) == value || F64(f64_consts::E) == value {
             Some("exp")
         } else if F32(2.0) == value || F64(2.0) == value {
@@ -251,7 +249,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
     }
 
     // Check argument
-    if let Some(value) = constant(cx, cx.typeck_results(), &args[0]) {
+    if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) {
         let (lint, help, suggestion) = if F32(1.0 / 2.0) == value || F64(1.0 / 2.0) == value {
             (
                 SUBOPTIMAL_FLOPS,
@@ -291,7 +289,7 @@ fn check_powf(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args:
 }
 
 fn check_powi(cx: &LateContext<'_>, expr: &Expr<'_>, receiver: &Expr<'_>, args: &[Expr<'_>]) {
-    if let Some(value) = constant(cx, cx.typeck_results(), &args[0]) {
+    if let Some(value) = ConstEvalCtxt::new(cx).eval(&args[0]) {
         if value == Int(2) {
             if let Some(parent) = get_parent_expr(cx, expr) {
                 if let Some(grandparent) = get_parent_expr(cx, parent) {
@@ -397,8 +395,9 @@ fn detect_hypot(cx: &LateContext<'_>, receiver: &Expr<'_>) -> Option<String> {
             ) = &add_rhs.kind
             && lmethod_name.as_str() == "powi"
             && rmethod_name.as_str() == "powi"
-            && let Some(lvalue) = constant(cx, cx.typeck_results(), largs_1)
-            && let Some(rvalue) = constant(cx, cx.typeck_results(), rargs_1)
+            && let ecx = ConstEvalCtxt::new(cx)
+            && let Some(lvalue) = ecx.eval(largs_1)
+            && let Some(rvalue) = ecx.eval(rargs_1)
             && Int(2) == lvalue
             && Int(2) == rvalue
         {
@@ -438,7 +437,7 @@ fn check_expm1(cx: &LateContext<'_>, expr: &Expr<'_>) {
         rhs,
     ) = expr.kind
         && cx.typeck_results().expr_ty(lhs).is_floating_point()
-        && let Some(value) = constant(cx, cx.typeck_results(), rhs)
+        && let Some(value) = ConstEvalCtxt::new(cx).eval(rhs)
         && (F32(1.0) == value || F64(1.0) == value)
         && let ExprKind::MethodCall(path, self_arg, ..) = &lhs.kind
         && cx.typeck_results().expr_ty(self_arg).is_floating_point()
@@ -552,7 +551,7 @@ fn is_testing_negative(cx: &LateContext<'_>, expr: &Expr<'_>, test: &Expr<'_>) -
 
 /// Returns true iff expr is some zero literal
 fn is_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    match constant_simple(cx, cx.typeck_results(), expr) {
+    match ConstEvalCtxt::new(cx).eval_simple(expr) {
         Some(Int(i)) => i == 0,
         Some(F32(f)) => f == 0.0,
         Some(F64(f)) => f == 0.0,
@@ -696,8 +695,9 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
             mul_lhs,
             mul_rhs,
         ) = &div_lhs.kind
-        && let Some(rvalue) = constant(cx, cx.typeck_results(), div_rhs)
-        && let Some(lvalue) = constant(cx, cx.typeck_results(), mul_rhs)
+        && let ecx = ConstEvalCtxt::new(cx)
+        && let Some(rvalue) = ecx.eval(div_rhs)
+        && let Some(lvalue) = ecx.eval(mul_rhs)
     {
         // TODO: also check for constant values near PI/180 or 180/PI
         if (F32(f32_consts::PI) == rvalue || F64(f64_consts::PI) == rvalue)
@@ -753,7 +753,7 @@ fn check_radians(cx: &LateContext<'_>, expr: &Expr<'_>) {
 impl<'tcx> LateLintPass<'tcx> for FloatingPointArithmetic {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         // All of these operations are currently not const and are in std.
-        if in_constant(cx, expr.hir_id) {
+        if is_in_const_context(cx) {
             return;
         }
 
diff --git a/clippy_lints/src/format_push_string.rs b/clippy_lints/src/format_push_string.rs
index a75538dd329b5..d05c5a01f41ce 100644
--- a/clippy_lints/src/format_push_string.rs
+++ b/clippy_lints/src/format_push_string.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::ty::is_type_lang_item;
 use clippy_utils::{higher, match_def_path, paths};
 use rustc_hir::{BinOpKind, Expr, ExprKind, LangItem, MatchSource};
@@ -81,13 +81,15 @@ impl<'tcx> LateLintPass<'tcx> for FormatPushString {
             _ => return,
         };
         if is_format(cx, arg) {
-            span_lint_and_help(
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(
                 cx,
                 FORMAT_PUSH_STRING,
                 expr.span,
                 "`format!(..)` appended to existing `String`",
-                None,
-                "consider using `write!` to avoid the extra allocation",
+                |diag| {
+                    diag.help("consider using `write!` to avoid the extra allocation");
+                },
             );
         }
     }
diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs
index 9acb72b2e3729..6ab7bbc2dfc0c 100644
--- a/clippy_lints/src/from_str_radix_10.rs
+++ b/clippy_lints/src/from_str_radix_10.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
-use clippy_utils::{in_constant, is_integer_literal};
+use clippy_utils::{is_in_const_context, is_integer_literal};
 use rustc_errors::Applicability;
 use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -62,8 +62,8 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
             && matches!(prim_ty, PrimTy::Int(_) | PrimTy::Uint(_))
 
             // do not lint in constant context, because the suggestion won't work.
-            // NB: keep this check until a new `const_trait_impl` is available and stablized.
-            && !in_constant(cx, exp.hir_id)
+            // NB: keep this check until a new `const_trait_impl` is available and stabilized.
+            && !is_in_const_context(cx)
         {
             let expr = if let ExprKind::AddrOf(_, _, expr) = &src.kind {
                 let ty = cx.typeck_results().expr_ty(expr);
diff --git a/clippy_lints/src/if_let_mutex.rs b/clippy_lints/src/if_let_mutex.rs
index b38cc7b36a123..1c52514a330d4 100644
--- a/clippy_lints/src/if_let_mutex.rs
+++ b/clippy_lints/src/if_let_mutex.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::visitors::for_each_expr_without_closures;
-use clippy_utils::{higher, SpanlessEq};
+use clippy_utils::{eq_expr_value, higher};
 use core::ops::ControlFlow;
 use rustc_errors::Diag;
 use rustc_hir::{Expr, ExprKind};
@@ -51,53 +51,45 @@ impl<'tcx> LateLintPass<'tcx> for IfLetMutex {
             if_else: Some(if_else),
             ..
         }) = higher::IfLet::hir(cx, expr)
+            && let Some(op_mutex) = for_each_expr_without_closures(let_expr, |e| mutex_lock_call(cx, e, None))
+            && let Some(arm_mutex) =
+                for_each_expr_without_closures((if_then, if_else), |e| mutex_lock_call(cx, e, Some(op_mutex)))
         {
-            let is_mutex_lock = |e: &'tcx Expr<'tcx>| {
-                if let Some(mutex) = is_mutex_lock_call(cx, e) {
-                    ControlFlow::Break(mutex)
-                } else {
-                    ControlFlow::Continue(())
-                }
+            let diag = |diag: &mut Diag<'_, ()>| {
+                diag.span_label(
+                    op_mutex.span,
+                    "this Mutex will remain locked for the entire `if let`-block...",
+                );
+                diag.span_label(
+                    arm_mutex.span,
+                    "... and is tried to lock again here, which will always deadlock.",
+                );
+                diag.help("move the lock call outside of the `if let ...` expression");
             };
-
-            let op_mutex = for_each_expr_without_closures(let_expr, is_mutex_lock);
-            if let Some(op_mutex) = op_mutex {
-                let arm_mutex = for_each_expr_without_closures((if_then, if_else), is_mutex_lock);
-                if let Some(arm_mutex) = arm_mutex
-                    && SpanlessEq::new(cx).eq_expr(op_mutex, arm_mutex)
-                {
-                    let diag = |diag: &mut Diag<'_, ()>| {
-                        diag.span_label(
-                            op_mutex.span,
-                            "this Mutex will remain locked for the entire `if let`-block...",
-                        );
-                        diag.span_label(
-                            arm_mutex.span,
-                            "... and is tried to lock again here, which will always deadlock.",
-                        );
-                        diag.help("move the lock call outside of the `if let ...` expression");
-                    };
-                    span_lint_and_then(
-                        cx,
-                        IF_LET_MUTEX,
-                        expr.span,
-                        "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
-                        diag,
-                    );
-                }
-            }
+            span_lint_and_then(
+                cx,
+                IF_LET_MUTEX,
+                expr.span,
+                "calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock",
+                diag,
+            );
         }
     }
 }
 
-fn is_mutex_lock_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+fn mutex_lock_call<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    op_mutex: Option<&'tcx Expr<'_>>,
+) -> ControlFlow<&'tcx Expr<'tcx>> {
     if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind
         && path.ident.as_str() == "lock"
         && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs()
         && is_type_diagnostic_item(cx, ty, sym::Mutex)
+        && op_mutex.map_or(true, |op| eq_expr_value(cx, self_arg, op))
     {
-        Some(self_arg)
+        ControlFlow::Break(self_arg)
     } else {
-        None
+        ControlFlow::Continue(())
     }
 }
diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs
index 2f6daeeb90d9d..0ebd8d0c237b6 100644
--- a/clippy_lints/src/if_not_else.rs
+++ b/clippy_lints/src/if_not_else.rs
@@ -1,7 +1,7 @@
 //! lint on if branches that could be swapped so no `!` operation is necessary
 //! on the condition
 
-use clippy_utils::consts::{constant_simple, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::is_else_clause;
 use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
@@ -49,7 +49,7 @@ declare_clippy_lint! {
 declare_lint_pass!(IfNotElse => [IF_NOT_ELSE]);
 
 fn is_zero_const(expr: &Expr<'_>, cx: &LateContext<'_>) -> bool {
-    if let Some(value) = constant_simple(cx, cx.typeck_results(), expr) {
+    if let Some(value) = ConstEvalCtxt::new(cx).eval_simple(expr) {
         return Constant::Int(0) == value;
     }
     false
diff --git a/clippy_lints/src/if_then_some_else_none.rs b/clippy_lints/src/if_then_some_else_none.rs
index 39ea16b05d1ad..0bca53c1536de 100644
--- a/clippy_lints/src/if_then_some_else_none.rs
+++ b/clippy_lints/src/if_then_some_else_none.rs
@@ -1,10 +1,12 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::eager_or_lazy::switch_to_eager_eval;
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{contains_return, higher, in_constant, is_else_clause, is_res_lang_ctor, path_res, peel_blocks};
+use clippy_utils::{
+    contains_return, higher, is_else_clause, is_in_const_context, is_res_lang_ctor, path_res, peel_blocks,
+};
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::{Expr, ExprKind};
@@ -76,37 +78,44 @@ impl<'tcx> LateLintPass<'tcx> for IfThenSomeElseNone {
             && is_res_lang_ctor(cx, path_res(cx, then_call), OptionSome)
             && is_res_lang_ctor(cx, path_res(cx, peel_blocks(els)), OptionNone)
             && !is_else_clause(cx.tcx, expr)
-            && !in_constant(cx, expr.hir_id)
+            && !is_in_const_context(cx)
             && !in_external_macro(cx.sess(), expr.span)
             && self.msrv.meets(msrvs::BOOL_THEN)
             && !contains_return(then_block.stmts)
         {
-            let mut app = Applicability::Unspecified;
-            let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app)
-                .maybe_par()
-                .to_string();
-            let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0;
-            let mut method_body = if then_block.stmts.is_empty() {
-                arg_snip.into_owned()
-            } else {
-                format!("{{ /* snippet */ {arg_snip} }}")
-            };
             let method_name = if switch_to_eager_eval(cx, expr) && self.msrv.meets(msrvs::BOOL_THEN_SOME) {
                 "then_some"
             } else {
-                method_body.insert_str(0, "|| ");
                 "then"
             };
 
-            let help =
-                format!("consider using `bool::{method_name}` like: `{cond_snip}.{method_name}({method_body})`",);
-            span_lint_and_help(
+            span_lint_and_then(
                 cx,
                 IF_THEN_SOME_ELSE_NONE,
                 expr.span,
                 format!("this could be simplified with `bool::{method_name}`"),
-                None,
-                help,
+                |diag| {
+                    let mut app = Applicability::Unspecified;
+                    let cond_snip = Sugg::hir_with_context(cx, cond, expr.span.ctxt(), "[condition]", &mut app)
+                        .maybe_par()
+                        .to_string();
+                    let arg_snip = snippet_with_context(cx, then_arg.span, ctxt, "[body]", &mut app).0;
+                    let method_body = if let Some(first_stmt) = then_block.stmts.first() {
+                        let (block_snippet, _) =
+                            snippet_with_context(cx, first_stmt.span.until(then_arg.span), ctxt, "..", &mut app);
+                        let closure = if method_name == "then" { "|| " } else { "" };
+                        format!("{closure} {{ {block_snippet}; {arg_snip} }}")
+                    } else {
+                        arg_snip.into_owned()
+                    };
+
+                    diag.span_suggestion(
+                        expr.span,
+                        "try",
+                        format!("{cond_snip}.{method_name}({method_body})"),
+                        app,
+                    );
+                },
             );
         }
     }
diff --git a/clippy_lints/src/implicit_hasher.rs b/clippy_lints/src/implicit_hasher.rs
index 344a04e6e7e82..e56f33f8dcfe2 100644
--- a/clippy_lints/src/implicit_hasher.rs
+++ b/clippy_lints/src/implicit_hasher.rs
@@ -1,7 +1,7 @@
 use std::borrow::Cow;
 use std::collections::BTreeMap;
 
-use rustc_errors::Diag;
+use rustc_errors::{Applicability, Diag};
 use rustc_hir as hir;
 use rustc_hir::intravisit::{walk_body, walk_expr, walk_inf, walk_ty, Visitor};
 use rustc_hir::{Body, Expr, ExprKind, GenericArg, Item, ItemKind, QPath, TyKind};
@@ -13,7 +13,7 @@ use rustc_session::declare_lint_pass;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 
-use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::{snippet, IntoSpan, SpanRangeExt};
 use clippy_utils::ty::is_type_diagnostic_item;
 
@@ -77,33 +77,32 @@ impl<'tcx> LateLintPass<'tcx> for ImplicitHasher {
                 &generics_snip[1..generics_snip.len() - 1]
             };
 
-            multispan_sugg(
-                diag,
-                "consider adding a type parameter",
-                vec![
-                    (
-                        generics_suggestion_span,
-                        format!(
-                            "<{generics_snip}{}S: ::std::hash::BuildHasher{}>",
-                            if generics_snip.is_empty() { "" } else { ", " },
-                            if vis.suggestions.is_empty() {
-                                ""
-                            } else {
-                                // request users to add `Default` bound so that generic constructors can be used
-                                " + Default"
-                            },
-                        ),
-                    ),
-                    (
-                        target.span(),
-                        format!("{}<{}, S>", target.type_name(), target.type_arguments(),),
+            let mut suggestions = vec![
+                (
+                    generics_suggestion_span,
+                    format!(
+                        "<{generics_snip}{}S: ::std::hash::BuildHasher{}>",
+                        if generics_snip.is_empty() { "" } else { ", " },
+                        if vis.suggestions.is_empty() {
+                            ""
+                        } else {
+                            // request users to add `Default` bound so that generic constructors can be used
+                            " + Default"
+                        },
                     ),
-                ],
+                ),
+                (
+                    target.span(),
+                    format!("{}<{}, S>", target.type_name(), target.type_arguments(),),
+                ),
+            ];
+            suggestions.extend(vis.suggestions);
+
+            diag.multipart_suggestion(
+                "add a type parameter for `BuildHasher`",
+                suggestions,
+                Applicability::MaybeIncorrect,
             );
-
-            if !vis.suggestions.is_empty() {
-                multispan_sugg(diag, "...and use generic constructor", vis.suggestions);
-            }
         }
 
         if !cx.effective_visibilities.is_exported(item.owner_id.def_id) {
diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs
index a102b434cfabb..b926e1e62ba0c 100644
--- a/clippy_lints/src/implicit_return.rs
+++ b/clippy_lints/src/implicit_return.rs
@@ -3,7 +3,7 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context, wal
 use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{get_async_fn_body, is_async_fn, is_from_proc_macro};
 use core::ops::ControlFlow;
-use rustc_errors::Applicability;
+use rustc_errors::{Applicability, SuggestionStyle};
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -45,8 +45,6 @@ declare_clippy_lint! {
 declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]);
 
 fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) {
-    let mut app = Applicability::MachineApplicable;
-    let snip = snippet_with_applicability(cx, span, "..", &mut app);
     span_lint_hir_and_then(
         cx,
         IMPLICIT_RETURN,
@@ -54,14 +52,20 @@ fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) {
         span,
         "missing `return` statement",
         |diag| {
-            diag.span_suggestion(span, "add `return` as shown", format!("return {snip}"), app);
+            let mut app = Applicability::MachineApplicable;
+            let snip = snippet_with_applicability(cx, span, "..", &mut app);
+            diag.span_suggestion_with_style(
+                span,
+                "add `return` as shown",
+                format!("return {snip}"),
+                app,
+                SuggestionStyle::ShowAlways,
+            );
         },
     );
 }
 
 fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, expr_span: Span) {
-    let mut app = Applicability::MachineApplicable;
-    let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0;
     span_lint_hir_and_then(
         cx,
         IMPLICIT_RETURN,
@@ -69,11 +73,14 @@ fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, exp
         break_span,
         "missing `return` statement",
         |diag| {
-            diag.span_suggestion(
+            let mut app = Applicability::MachineApplicable;
+            let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0;
+            diag.span_suggestion_with_style(
                 break_span,
                 "change `break` to `return` as shown",
                 format!("return {snip}"),
                 app,
+                SuggestionStyle::ShowAlways,
             );
         },
     );
diff --git a/clippy_lints/src/implicit_saturating_add.rs b/clippy_lints/src/implicit_saturating_add.rs
index f225c6e7f049f..dd5908553e59b 100644
--- a/clippy_lints/src/implicit_saturating_add.rs
+++ b/clippy_lints/src/implicit_saturating_add.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::get_parent_expr;
 use clippy_utils::source::snippet_with_context;
@@ -117,11 +117,11 @@ fn get_int_max(ty: Ty<'_>) -> Option<u128> {
 
 fn get_const<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) -> Option<(u128, BinOpKind, &'tcx Expr<'tcx>)> {
     if let ExprKind::Binary(op, l, r) = expr.kind {
-        let tr = cx.typeck_results();
-        if let Some(Constant::Int(c)) = constant(cx, tr, r) {
+        let ecx = ConstEvalCtxt::new(cx);
+        if let Some(Constant::Int(c)) = ecx.eval(r) {
             return Some((c, op.node, l));
         };
-        if let Some(Constant::Int(c)) = constant(cx, tr, l) {
+        if let Some(Constant::Int(c)) = ecx.eval(l) {
             return Some((c, invert_op(op.node)?, r));
         }
     }
diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs
index 12ca6d43b27bc..0ef5b803a89d6 100644
--- a/clippy_lints/src/incompatible_msrv.rs
+++ b/clippy_lints/src/incompatible_msrv.rs
@@ -55,7 +55,6 @@ impl IncompatibleMsrv {
         }
     }
 
-    #[allow(clippy::cast_lossless)]
     fn get_def_id_version(&mut self, tcx: TyCtxt<'_>, def_id: DefId) -> RustcVersion {
         if let Some(version) = self.is_above_msrv.get(&def_id) {
             return *version;
@@ -67,9 +66,9 @@ impl IncompatibleMsrv {
                     since: StableSince::Version(version),
                     ..
                 } => Some(RustcVersion::new(
-                    version.major as _,
-                    version.minor as _,
-                    version.patch as _,
+                    version.major.into(),
+                    version.minor.into(),
+                    version.patch.into(),
                 )),
                 _ => None,
             }) {
diff --git a/clippy_lints/src/index_refutable_slice.rs b/clippy_lints/src/index_refutable_slice.rs
index 526b4e1fba0e9..2f9661c9ea385 100644
--- a/clippy_lints/src/index_refutable_slice.rs
+++ b/clippy_lints/src/index_refutable_slice.rs
@@ -1,6 +1,6 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::IfLet;
 use clippy_utils::ty::is_copy;
@@ -246,7 +246,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SliceIndexLintingVisitor<'a, 'tcx> {
                 && let parent_id = cx.tcx.parent_hir_id(expr.hir_id)
                 && let hir::Node::Expr(parent_expr) = cx.tcx.hir_node(parent_id)
                 && let hir::ExprKind::Index(_, index_expr, _) = parent_expr.kind
-                && let Some(Constant::Int(index_value)) = constant(cx, cx.typeck_results(), index_expr)
+                && let Some(Constant::Int(index_value)) = ConstEvalCtxt::new(cx).eval(index_expr)
                 && let Ok(index_value) = index_value.try_into()
                 && index_value < max_suggested_slice
 
diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs
index 6729c7c8d1018..3ac50b8f1fba5 100644
--- a/clippy_lints/src/indexing_slicing.rs
+++ b/clippy_lints/src/indexing_slicing.rs
@@ -1,7 +1,7 @@
 //! lint on indexing and slicing operations
 
 use clippy_config::Conf;
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::ty::{deref_chain, get_adt_inherent_method};
 use clippy_utils::{higher, is_from_proc_macro};
@@ -70,8 +70,6 @@ declare_clippy_lint! {
     ///
     /// Use instead:
     /// ```no_run
-    /// # #![allow(unused)]
-    ///
     /// # let x = vec![0; 5];
     /// # let y = [0, 1, 2, 3];
     /// x.get(2);
@@ -179,7 +177,7 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
                         return;
                     }
                     // Index is a constant uint.
-                    if let Some(constant) = constant(cx, cx.typeck_results(), index) {
+                    if let Some(constant) = ConstEvalCtxt::new(cx).eval(index) {
                         // only `usize` index is legal in rust array index
                         // leave other type to rustc
                         if let Constant::Int(off) = constant
@@ -217,14 +215,15 @@ impl<'tcx> LateLintPass<'tcx> for IndexingSlicing {
 /// Returns a tuple of options with the start and end (exclusive) values of
 /// the range. If the start or end is not constant, None is returned.
 fn to_const_range(cx: &LateContext<'_>, range: higher::Range<'_>, array_size: u128) -> (Option<u128>, Option<u128>) {
-    let s = range.start.map(|expr| constant(cx, cx.typeck_results(), expr));
+    let ecx = ConstEvalCtxt::new(cx);
+    let s = range.start.map(|expr| ecx.eval(expr));
     let start = match s {
         Some(Some(Constant::Int(x))) => Some(x),
         Some(_) => None,
         None => Some(0),
     };
 
-    let e = range.end.map(|expr| constant(cx, cx.typeck_results(), expr));
+    let e = range.end.map(|expr| ecx.eval(expr));
     let end = match e {
         Some(Some(Constant::Int(x))) => {
             if range.limits == RangeLimits::Closed {
diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs
index fa7e7f6b76d16..676d50c4951b1 100644
--- a/clippy_lints/src/infinite_iter.rs
+++ b/clippy_lints/src/infinite_iter.rs
@@ -41,7 +41,6 @@ declare_clippy_lint! {
     /// ### Example
     /// ```no_run
     /// let infinite_iter = 0..;
-    /// # #[allow(unused)]
     /// [0..].iter().zip(infinite_iter.take_while(|x| *x > 5));
     /// ```
     #[clippy::version = "pre 1.29.0"]
diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs
index 0d3786dad4b1e..9eed7aa924339 100644
--- a/clippy_lints/src/inherent_impl.rs
+++ b/clippy_lints/src/inherent_impl.rs
@@ -1,6 +1,6 @@
 //! lint on inherent implementations
 
-use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_lint_allowed;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def_id::LocalDefId;
@@ -105,13 +105,14 @@ impl<'tcx> LateLintPass<'tcx> for MultipleInherentImpl {
         // `TyCtxt::crate_inherent_impls` doesn't have a defined order. Sort the lint output first.
         lint_spans.sort_by_key(|x| x.0.lo());
         for (span, first_span) in lint_spans {
-            span_lint_and_note(
+            span_lint_and_then(
                 cx,
                 MULTIPLE_INHERENT_IMPL,
                 span,
                 "multiple implementations of this structure",
-                Some(first_span),
-                "first implementation here",
+                |diag| {
+                    diag.span_note(first_span, "first implementation here");
+                },
             );
         }
     }
diff --git a/clippy_lints/src/invalid_upcast_comparisons.rs b/clippy_lints/src/invalid_upcast_comparisons.rs
index 30f2285bdd235..1929fbded3b56 100644
--- a/clippy_lints/src/invalid_upcast_comparisons.rs
+++ b/clippy_lints/src/invalid_upcast_comparisons.rs
@@ -7,7 +7,7 @@ use rustc_span::Span;
 
 use clippy_utils::comparisons;
 use clippy_utils::comparisons::Rel;
-use clippy_utils::consts::{constant_full_int, FullInt};
+use clippy_utils::consts::{ConstEvalCtxt, FullInt};
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::source::snippet;
 
@@ -95,7 +95,7 @@ fn upcast_comparison_bounds_err<'tcx>(
     invert: bool,
 ) {
     if let Some((lb, ub)) = lhs_bounds {
-        if let Some(norm_rhs_val) = constant_full_int(cx, cx.typeck_results(), rhs) {
+        if let Some(norm_rhs_val) = ConstEvalCtxt::new(cx).eval_full_int(rhs) {
             if rel == Rel::Eq || rel == Rel::Ne {
                 if norm_rhs_val < lb || norm_rhs_val > ub {
                     err_upcast_comparison(cx, span, lhs, rel == Rel::Ne);
diff --git a/clippy_lints/src/large_include_file.rs b/clippy_lints/src/large_include_file.rs
index c67da689aaee0..f2f841dcec33d 100644
--- a/clippy_lints/src/large_include_file.rs
+++ b/clippy_lints/src/large_include_file.rs
@@ -1,5 +1,5 @@
 use clippy_config::Conf;
-use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::root_macro_call_first_node;
 use rustc_ast::LitKind;
 use rustc_hir::{Expr, ExprKind};
@@ -66,16 +66,18 @@ impl LateLintPass<'_> for LargeIncludeFile {
             && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id)
                 || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id))
         {
-            span_lint_and_note(
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(
                 cx,
                 LARGE_INCLUDE_FILE,
                 expr.span.source_callsite(),
                 "attempted to include a large file",
-                None,
-                format!(
-                    "the configuration allows a maximum size of {} bytes",
-                    self.max_file_size
-                ),
+                |diag| {
+                    diag.note(format!(
+                        "the configuration allows a maximum size of {} bytes",
+                        self.max_file_size
+                    ));
+                },
             );
         }
     }
diff --git a/clippy_lints/src/let_underscore.rs b/clippy_lints/src/let_underscore.rs
index 8fa63f3e8fde0..b522c22a44d70 100644
--- a/clippy_lints/src/let_underscore.rs
+++ b/clippy_lints/src/let_underscore.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::ty::{implements_trait, is_must_use_ty, match_type};
 use clippy_utils::{is_from_proc_macro, is_must_use_func_call, paths};
 use rustc_hir::{LetStmt, LocalSource, PatKind};
@@ -149,43 +149,53 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
                 GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => false,
             });
             if contains_sync_guard {
-                span_lint_and_help(
+                #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+                span_lint_and_then(
                     cx,
                     LET_UNDERSCORE_LOCK,
                     local.span,
                     "non-binding `let` on a synchronization lock",
-                    None,
-                    "consider using an underscore-prefixed named \
-                            binding or dropping explicitly with `std::mem::drop`",
+                    |diag| {
+                        diag.help(
+                            "consider using an underscore-prefixed named \
+                                binding or dropping explicitly with `std::mem::drop`",
+                        );
+                    },
                 );
             } else if let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait()
                 && implements_trait(cx, cx.typeck_results().expr_ty(init), future_trait_def_id, &[])
             {
-                span_lint_and_help(
+                #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+                span_lint_and_then(
                     cx,
                     LET_UNDERSCORE_FUTURE,
                     local.span,
                     "non-binding `let` on a future",
-                    None,
-                    "consider awaiting the future or dropping explicitly with `std::mem::drop`",
+                    |diag| {
+                        diag.help("consider awaiting the future or dropping explicitly with `std::mem::drop`");
+                    },
                 );
             } else if is_must_use_ty(cx, cx.typeck_results().expr_ty(init)) {
-                span_lint_and_help(
+                #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+                span_lint_and_then(
                     cx,
                     LET_UNDERSCORE_MUST_USE,
                     local.span,
                     "non-binding `let` on an expression with `#[must_use]` type",
-                    None,
-                    "consider explicitly using expression value",
+                    |diag| {
+                        diag.help("consider explicitly using expression value");
+                    },
                 );
             } else if is_must_use_func_call(cx, init) {
-                span_lint_and_help(
+                #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+                span_lint_and_then(
                     cx,
                     LET_UNDERSCORE_MUST_USE,
                     local.span,
                     "non-binding `let` on a result of a `#[must_use]` function",
-                    None,
-                    "consider explicitly using function result",
+                    |diag| {
+                        diag.help("consider explicitly using function result");
+                    },
                 );
             }
 
@@ -204,18 +214,22 @@ impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
                     return;
                 }
 
-                span_lint_and_help(
+                span_lint_and_then(
                     cx,
                     LET_UNDERSCORE_UNTYPED,
                     local.span,
                     "non-binding `let` without a type annotation",
-                    Some(Span::new(
-                        local.pat.span.hi(),
-                        local.pat.span.hi() + BytePos(1),
-                        local.pat.span.ctxt(),
-                        local.pat.span.parent(),
-                    )),
-                    "consider adding a type annotation",
+                    |diag| {
+                        diag.span_help(
+                            Span::new(
+                                local.pat.span.hi(),
+                                local.pat.span.hi() + BytePos(1),
+                                local.pat.span.ctxt(),
+                                local.pat.span.parent(),
+                            ),
+                            "consider adding a type annotation",
+                        );
+                    },
                 );
             }
         }
diff --git a/clippy_lints/src/lib.deprecated.rs b/clippy_lints/src/lib.deprecated.rs
deleted file mode 100644
index 0d21261822dd0..0000000000000
--- a/clippy_lints/src/lib.deprecated.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-// This file was generated by `cargo dev update_lints`.
-// Use that command to update this file and do not edit by hand.
-// Manual edits will be overwritten.
-
-{
-    store.register_removed(
-        "clippy::should_assert_eq",
-        "`assert!()` will be more flexible with RFC 2011",
-    );
-    store.register_removed(
-        "clippy::extend_from_slice",
-        "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice",
-    );
-    store.register_removed(
-        "clippy::range_step_by_zero",
-        "`iterator.step_by(0)` panics nowadays",
-    );
-    store.register_removed(
-        "clippy::unstable_as_slice",
-        "`Vec::as_slice` has been stabilized in 1.7",
-    );
-    store.register_removed(
-        "clippy::unstable_as_mut_slice",
-        "`Vec::as_mut_slice` has been stabilized in 1.7",
-    );
-    store.register_removed(
-        "clippy::misaligned_transmute",
-        "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr",
-    );
-    store.register_removed(
-        "clippy::assign_ops",
-        "using compound assignment operators (e.g., `+=`) is harmless",
-    );
-    store.register_removed(
-        "clippy::if_let_redundant_pattern_matching",
-        "this lint has been changed to redundant_pattern_matching",
-    );
-    store.register_removed(
-        "clippy::unsafe_vector_initialization",
-        "the replacement suggested by this lint had substantially different behavior",
-    );
-    store.register_removed(
-        "clippy::unused_collect",
-        "`collect` has been marked as #[must_use] in rustc and that covers all cases of this lint",
-    );
-    store.register_removed(
-        "clippy::replace_consts",
-        "associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants",
-    );
-    store.register_removed(
-        "clippy::regex_macro",
-        "the regex! macro has been removed from the regex crate in 2018",
-    );
-    store.register_removed(
-        "clippy::find_map",
-        "this lint has been replaced by `manual_find_map`, a more specific lint",
-    );
-    store.register_removed(
-        "clippy::filter_map",
-        "this lint has been replaced by `manual_filter_map`, a more specific lint",
-    );
-    store.register_removed(
-        "clippy::pub_enum_variant_names",
-        "set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items",
-    );
-    store.register_removed(
-        "clippy::wrong_pub_self_convention",
-        "set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items",
-    );
-    store.register_removed(
-        "clippy::maybe_misused_cfg",
-        "this lint has been replaced by `unexpected_cfgs`",
-    );
-    store.register_removed(
-        "clippy::mismatched_target_os",
-        "this lint has been replaced by `unexpected_cfgs`",
-    );
-}
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index a388b6b2eaab3..ce13a9afef5b0 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -13,7 +13,6 @@
 #![feature(stmt_expr_attributes)]
 #![feature(unwrap_infallible)]
 #![recursion_limit = "512"]
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![allow(
     clippy::missing_docs_in_private_items,
     clippy::must_use_candidate,
@@ -64,13 +63,11 @@ extern crate clippy_utils;
 #[macro_use]
 extern crate declare_clippy_lint;
 
-#[cfg(feature = "internal")]
-pub mod deprecated_lints;
 #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
 mod utils;
 
 mod declared_lints;
-mod renamed_lints;
+mod deprecated_lints;
 
 // begin lints modules, do not remove this comment, it’s used in `update_lints`
 mod absolute_paths;
@@ -372,6 +369,7 @@ mod unsafe_removed_from_name;
 mod unused_async;
 mod unused_io_amount;
 mod unused_peekable;
+mod unused_result_ok;
 mod unused_rounding;
 mod unused_self;
 mod unused_unit;
@@ -495,7 +493,7 @@ pub fn explain(name: &str) -> i32 {
         // Check if the lint has configuration
         let mut mdconf = get_configuration_metadata();
         let name = name.to_ascii_lowercase();
-        mdconf.retain(|cconf| cconf.lints.contains(&name));
+        mdconf.retain(|cconf| cconf.lints.contains(&&*name));
         if !mdconf.is_empty() {
             println!("### Configuration for {}:\n", info.lint.name_lower());
             for conf in mdconf {
@@ -531,10 +529,14 @@ fn register_categories(store: &mut rustc_lint::LintStore) {
 /// Used in `./src/driver.rs`.
 #[expect(clippy::too_many_lines)]
 pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
-    register_removed_non_tool_lints(store);
     register_categories(store);
 
-    include!("lib.deprecated.rs");
+    for (old_name, new_name) in deprecated_lints::RENAMED {
+        store.register_renamed(old_name, new_name);
+    }
+    for (name, reason) in deprecated_lints::DEPRECATED {
+        store.register_removed(name, reason);
+    }
 
     #[cfg(feature = "internal")]
     {
@@ -669,6 +671,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(move |_| Box::new(missing_doc::MissingDoc::new(conf)));
     store.register_late_pass(|_| Box::new(missing_inline::MissingInline));
     store.register_late_pass(move |_| Box::new(exhaustive_items::ExhaustiveItems));
+    store.register_late_pass(|_| Box::new(unused_result_ok::UnusedResultOk));
     store.register_late_pass(|_| Box::new(match_result_ok::MatchResultOk));
     store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl));
     store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount));
@@ -818,7 +821,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(move |_| Box::new(manual_retain::ManualRetain::new(conf)));
     store.register_late_pass(move |_| Box::new(manual_rotate::ManualRotate));
     store.register_late_pass(move |_| Box::new(operators::Operators::new(conf)));
-    store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default());
+    store.register_late_pass(move |_| Box::new(std_instead_of_core::StdReexports::new(conf)));
     store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(conf)));
     store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone));
     store.register_late_pass(move |_| Box::new(manual_clamp::ManualClamp::new(conf)));
@@ -907,68 +910,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(move |_| Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe::new(conf)));
     store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(conf)));
     store.register_early_pass(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers));
-    store.register_late_pass(|_| Box::new(set_contains_or_insert::HashsetInsertAfterContains));
+    store.register_late_pass(|_| Box::new(set_contains_or_insert::SetContainsOrInsert));
     store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice));
     store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
-
-#[rustfmt::skip]
-fn register_removed_non_tool_lints(store: &mut rustc_lint::LintStore) {
-    store.register_removed(
-        "should_assert_eq",
-        "`assert!()` will be more flexible with RFC 2011",
-    );
-    store.register_removed(
-        "extend_from_slice",
-        "`.extend_from_slice(_)` is a faster way to extend a Vec by a slice",
-    );
-    store.register_removed(
-        "range_step_by_zero",
-        "`iterator.step_by(0)` panics nowadays",
-    );
-    store.register_removed(
-        "unstable_as_slice",
-        "`Vec::as_slice` has been stabilized in 1.7",
-    );
-    store.register_removed(
-        "unstable_as_mut_slice",
-        "`Vec::as_mut_slice` has been stabilized in 1.7",
-    );
-    store.register_removed(
-        "misaligned_transmute",
-        "this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr",
-    );
-    store.register_removed(
-        "assign_ops",
-        "using compound assignment operators (e.g., `+=`) is harmless",
-    );
-    store.register_removed(
-        "if_let_redundant_pattern_matching",
-        "this lint has been changed to redundant_pattern_matching",
-    );
-    store.register_removed(
-        "unsafe_vector_initialization",
-        "the replacement suggested by this lint had substantially different behavior",
-    );
-    store.register_removed(
-        "reverse_range_loop",
-        "this lint is now included in reversed_empty_ranges",
-    );
-}
-
-/// Register renamed lints.
-///
-/// Used in `./src/driver.rs`.
-pub fn register_renamed(ls: &mut rustc_lint::LintStore) {
-    for (old_name, new_name) in renamed_lints::RENAMED_LINTS {
-        ls.register_renamed(old_name, new_name);
-    }
-}
-
-// only exists to let the dogfood integration test works.
-// Don't run clippy as an executable directly
-#[allow(dead_code)]
-fn main() {
-    panic!("Please use the cargo-clippy executable");
-}
diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs
index b685d1dad1a75..259e4d6c08fb7 100644
--- a/clippy_lints/src/literal_representation.rs
+++ b/clippy_lints/src/literal_representation.rs
@@ -2,13 +2,13 @@
 //! floating-point literal expressions.
 
 use clippy_config::Conf;
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::numeric_literal::{NumericLiteral, Radix};
 use clippy_utils::source::snippet_opt;
 use rustc_ast::ast::{Expr, ExprKind, LitKind};
 use rustc_ast::token;
 use rustc_errors::Applicability;
-use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
+use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::impl_lint_pass;
 use rustc_span::Span;
@@ -159,63 +159,39 @@ enum WarningType {
 }
 
 impl WarningType {
-    fn display(&self, suggested_format: String, cx: &EarlyContext<'_>, span: Span) {
+    fn lint_and_text(&self) -> (&'static Lint, &'static str, &'static str) {
         match self {
-            Self::MistypedLiteralSuffix => span_lint_and_sugg(
-                cx,
+            Self::MistypedLiteralSuffix => (
                 MISTYPED_LITERAL_SUFFIXES,
-                span,
                 "mistyped literal suffix",
                 "did you mean to write",
-                suggested_format,
-                Applicability::MaybeIncorrect,
             ),
-            Self::UnreadableLiteral => span_lint_and_sugg(
-                cx,
-                UNREADABLE_LITERAL,
-                span,
-                "long literal lacking separators",
-                "consider",
-                suggested_format,
-                Applicability::MachineApplicable,
-            ),
-            Self::LargeDigitGroups => span_lint_and_sugg(
-                cx,
-                LARGE_DIGIT_GROUPS,
-                span,
-                "digit groups should be smaller",
-                "consider",
-                suggested_format,
-                Applicability::MachineApplicable,
-            ),
-            Self::InconsistentDigitGrouping => span_lint_and_sugg(
-                cx,
+            Self::UnreadableLiteral => (UNREADABLE_LITERAL, "long literal lacking separators", "consider"),
+            Self::LargeDigitGroups => (LARGE_DIGIT_GROUPS, "digit groups should be smaller", "consider"),
+            Self::InconsistentDigitGrouping => (
                 INCONSISTENT_DIGIT_GROUPING,
-                span,
                 "digits grouped inconsistently by underscores",
                 "consider",
-                suggested_format,
-                Applicability::MachineApplicable,
             ),
-            Self::DecimalRepresentation => span_lint_and_sugg(
-                cx,
+            Self::DecimalRepresentation => (
                 DECIMAL_LITERAL_REPRESENTATION,
-                span,
                 "integer literal has a better hexadecimal representation",
                 "consider",
-                suggested_format,
-                Applicability::MachineApplicable,
             ),
-            Self::UnusualByteGroupings => span_lint_and_sugg(
-                cx,
+            Self::UnusualByteGroupings => (
                 UNUSUAL_BYTE_GROUPINGS,
-                span,
                 "digits of hex, binary or octal literal not in groups of equal size",
                 "consider",
-                suggested_format,
-                Applicability::MachineApplicable,
             ),
-        };
+        }
+    }
+
+    fn display(&self, num_lit: &NumericLiteral<'_>, cx: &EarlyContext<'_>, span: Span) {
+        let (lint, message, try_msg) = self.lint_and_text();
+        #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+        span_lint_and_then(cx, lint, span, message, |diag| {
+            diag.span_suggestion(span, try_msg, num_lit.format(), Applicability::MaybeIncorrect);
+        });
     }
 }
 
@@ -293,7 +269,7 @@ impl LiteralDigitGrouping {
                     WarningType::DecimalRepresentation | WarningType::MistypedLiteralSuffix => true,
                 };
                 if should_warn {
-                    warning_type.display(num_lit.format(), cx, span);
+                    warning_type.display(&num_lit, cx, span);
                 }
             }
         }
@@ -346,11 +322,14 @@ impl LiteralDigitGrouping {
                 }
             }
             *part = main_part;
-            let mut sugg = num_lit.format();
-            sugg.push('_');
-            sugg.push(missing_char);
-            sugg.push_str(last_group);
-            WarningType::MistypedLiteralSuffix.display(sugg, cx, span);
+            let (lint, message, try_msg) = WarningType::MistypedLiteralSuffix.lint_and_text();
+            span_lint_and_then(cx, lint, span, message, |diag| {
+                let mut sugg = num_lit.format();
+                sugg.push('_');
+                sugg.push(missing_char);
+                sugg.push_str(last_group);
+                diag.span_suggestion(span, try_msg, sugg, Applicability::MaybeIncorrect);
+            });
             false
         } else {
             true
@@ -471,7 +450,7 @@ impl DecimalLiteralRepresentation {
             let hex = format!("{val:#X}");
             let num_lit = NumericLiteral::new(&hex, num_lit.suffix, false);
             let _: Result<(), ()> = Self::do_lint(num_lit.integer).map_err(|warning_type| {
-                warning_type.display(num_lit.format(), cx, span);
+                warning_type.display(&num_lit, cx, span);
             });
         }
     }
diff --git a/clippy_lints/src/loops/explicit_counter_loop.rs b/clippy_lints/src/loops/explicit_counter_loop.rs
index f0ee64d714e05..73a23615c2dc7 100644
--- a/clippy_lints/src/loops/explicit_counter_loop.rs
+++ b/clippy_lints/src/loops/explicit_counter_loop.rs
@@ -2,6 +2,7 @@ use super::{make_iterator_snippet, IncrementVisitor, InitializeVisitor, EXPLICIT
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::{get_enclosing_block, is_integer_const};
+use rustc_ast::Label;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_block, walk_expr};
 use rustc_hir::{Expr, Pat};
@@ -17,6 +18,7 @@ pub(super) fn check<'tcx>(
     arg: &'tcx Expr<'_>,
     body: &'tcx Expr<'_>,
     expr: &'tcx Expr<'_>,
+    label: Option<Label>,
 ) {
     // Look for variables that are incremented once per loop iteration.
     let mut increment_visitor = IncrementVisitor::new(cx);
@@ -34,7 +36,7 @@ pub(super) fn check<'tcx>(
             {
                 let mut applicability = Applicability::MaybeIncorrect;
                 let span = expr.span.with_hi(arg.span.hi());
-
+                let loop_label = label.map_or(String::new(), |l| format!("{}: ", l.ident.name));
                 let int_name = match ty.map(Ty::kind) {
                     // usize or inferred
                     Some(ty::Uint(UintTy::Usize)) | None => {
@@ -45,7 +47,7 @@ pub(super) fn check<'tcx>(
                             format!("the variable `{name}` is used as a loop counter"),
                             "consider using",
                             format!(
-                                "for ({name}, {}) in {}.enumerate()",
+                                "{loop_label}for ({name}, {}) in {}.enumerate()",
                                 snippet_with_applicability(cx, pat.span, "item", &mut applicability),
                                 make_iterator_snippet(cx, arg, &mut applicability),
                             ),
@@ -68,7 +70,7 @@ pub(super) fn check<'tcx>(
                             span,
                             "consider using",
                             format!(
-                                "for ({name}, {}) in (0_{int_name}..).zip({})",
+                                "{loop_label}for ({name}, {}) in (0_{int_name}..).zip({})",
                                 snippet_with_applicability(cx, pat.span, "item", &mut applicability),
                                 make_iterator_snippet(cx, arg, &mut applicability),
                             ),
diff --git a/clippy_lints/src/loops/for_kv_map.rs b/clippy_lints/src/loops/for_kv_map.rs
index 6922533fbe9d3..185d834becafc 100644
--- a/clippy_lints/src/loops/for_kv_map.rs
+++ b/clippy_lints/src/loops/for_kv_map.rs
@@ -1,8 +1,9 @@
 use super::FOR_KV_MAP;
-use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{pat_is_wild, sugg};
+use rustc_errors::Applicability;
 use rustc_hir::{BorrowKind, Expr, ExprKind, Mutability, Pat, PatKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
@@ -40,13 +41,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>, arg: &'tcx
                     format!("you seem to want to iterate on a map's {kind}s"),
                     |diag| {
                         let map = sugg::Sugg::hir(cx, arg, "map");
-                        multispan_sugg(
-                            diag,
+                        diag.multipart_suggestion(
                             "use the corresponding method",
                             vec![
                                 (pat_span, snippet(cx, new_pat_span, kind).into_owned()),
                                 (arg_span, format!("{}.{kind}s{mutbl}()", map.maybe_par())),
                             ],
+                            Applicability::MachineApplicable,
                         );
                     },
                 );
diff --git a/clippy_lints/src/loops/manual_while_let_some.rs b/clippy_lints/src/loops/manual_while_let_some.rs
index b00a082bb8cf9..57434f3554443 100644
--- a/clippy_lints/src/loops/manual_while_let_some.rs
+++ b/clippy_lints/src/loops/manual_while_let_some.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
 use clippy_utils::{match_def_path, paths, SpanlessEq};
 use rustc_errors::Applicability;
@@ -38,11 +38,10 @@ fn report_lint(cx: &LateContext<'_>, pop_span: Span, pop_stmt_kind: PopStmt<'_>,
             };
 
             let loop_replacement = format!("while let Some({}) = {}.pop()", pat, snippet(cx, receiver_span, ".."));
-            multispan_sugg_with_applicability(
-                diag,
+            diag.multipart_suggestion(
                 "consider using a `while..let` loop",
+                vec![(loop_span, loop_replacement), (pop_span, pop_replacement)],
                 Applicability::MachineApplicable,
-                [(loop_span, loop_replacement), (pop_span, pop_replacement)],
             );
         },
     );
diff --git a/clippy_lints/src/loops/mod.rs b/clippy_lints/src/loops/mod.rs
index 7c2a8098af287..92ccc0cc0a15e 100644
--- a/clippy_lints/src/loops/mod.rs
+++ b/clippy_lints/src/loops/mod.rs
@@ -25,6 +25,7 @@ mod while_let_on_iterator;
 use clippy_config::msrvs::Msrv;
 use clippy_config::Conf;
 use clippy_utils::higher;
+use rustc_ast::Label;
 use rustc_hir::{Expr, ExprKind, LoopSource, Pat};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
@@ -448,7 +449,7 @@ declare_clippy_lint! {
     #[clippy::version = "1.80.0"]
     pub WHILE_FLOAT,
     nursery,
-    "while loops comaparing floating point values"
+    "while loops comparing floating point values"
 }
 
 declare_clippy_lint! {
@@ -760,6 +761,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
             body,
             loop_id,
             span,
+            label,
         }) = for_loop
         {
             // we don't want to check expanded macros
@@ -768,7 +770,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
             if body.span.from_expansion() {
                 return;
             }
-            self.check_for_loop(cx, pat, arg, body, expr, span);
+            self.check_for_loop(cx, pat, arg, body, expr, span, label);
             if let ExprKind::Block(block, _) = body.kind {
                 never_loop::check(cx, block, loop_id, span, for_loop.as_ref());
             }
@@ -808,6 +810,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
 }
 
 impl Loops {
+    #[allow(clippy::too_many_arguments)]
     fn check_for_loop<'tcx>(
         &self,
         cx: &LateContext<'tcx>,
@@ -816,11 +819,12 @@ impl Loops {
         body: &'tcx Expr<'_>,
         expr: &'tcx Expr<'_>,
         span: Span,
+        label: Option<Label>,
     ) {
         let is_manual_memcpy_triggered = manual_memcpy::check(cx, pat, arg, body, expr);
         if !is_manual_memcpy_triggered {
             needless_range_loop::check(cx, pat, arg, body, expr);
-            explicit_counter_loop::check(cx, pat, arg, body, expr);
+            explicit_counter_loop::check(cx, pat, arg, body, expr, label);
         }
         self.check_for_loop_arg(cx, pat, arg);
         for_kv_map::check(cx, pat, arg, body);
diff --git a/clippy_lints/src/loops/needless_range_loop.rs b/clippy_lints/src/loops/needless_range_loop.rs
index de7ec81bc0108..e18e4374667de 100644
--- a/clippy_lints/src/loops/needless_range_loop.rs
+++ b/clippy_lints/src/loops/needless_range_loop.rs
@@ -1,11 +1,12 @@
 use super::NEEDLESS_RANGE_LOOP;
-use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::has_iter_method;
 use clippy_utils::visitors::is_local_used;
 use clippy_utils::{contains_name, higher, is_integer_const, sugg, SpanlessEq};
 use rustc_ast::ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::intravisit::{walk_expr, Visitor};
 use rustc_hir::{BinOpKind, BorrowKind, Closure, Expr, ExprKind, HirId, Mutability, Pat, PatKind, QPath};
@@ -145,8 +146,7 @@ pub(super) fn check<'tcx>(
                         arg.span,
                         format!("the loop variable `{}` is used to index `{indexed}`", ident.name),
                         |diag| {
-                            multispan_sugg(
-                                diag,
+                            diag.multipart_suggestion(
                                 "consider using an iterator and enumerate()",
                                 vec![
                                     (pat.span, format!("({}, <item>)", ident.name)),
@@ -155,6 +155,7 @@ pub(super) fn check<'tcx>(
                                         format!("{indexed}.{method}().enumerate(){method_1}{method_2}"),
                                     ),
                                 ],
+                                Applicability::HasPlaceholders,
                             );
                         },
                     );
@@ -171,10 +172,10 @@ pub(super) fn check<'tcx>(
                         arg.span,
                         format!("the loop variable `{}` is only used to index `{indexed}`", ident.name),
                         |diag| {
-                            multispan_sugg(
-                                diag,
+                            diag.multipart_suggestion(
                                 "consider using an iterator",
                                 vec![(pat.span, "<item>".to_string()), (arg.span, repl)],
+                                Applicability::HasPlaceholders,
                             );
                         },
                     );
diff --git a/clippy_lints/src/loops/unused_enumerate_index.rs b/clippy_lints/src/loops/unused_enumerate_index.rs
index 40ccfec02be58..51e21aa9734ed 100644
--- a/clippy_lints/src/loops/unused_enumerate_index.rs
+++ b/clippy_lints/src/loops/unused_enumerate_index.rs
@@ -1,7 +1,8 @@
 use super::UNUSED_ENUMERATE_INDEX;
-use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
 use clippy_utils::{pat_is_wild, sugg};
+use rustc_errors::Applicability;
 use rustc_hir::def::DefKind;
 use rustc_hir::{Expr, ExprKind, Pat, PatKind};
 use rustc_lint::LateContext;
@@ -28,13 +29,13 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, pat: &Pat<'tcx>, arg: &Expr<'_
             "you seem to use `.enumerate()` and immediately discard the index",
             |diag| {
                 let base_iter = sugg::Sugg::hir(cx, self_arg, "base iter");
-                multispan_sugg(
-                    diag,
+                diag.multipart_suggestion(
                     "remove the `.enumerate()` call",
                     vec![
                         (pat.span, snippet(cx, elem.span, "..").into_owned()),
                         (arg.span, base_iter.to_string()),
                     ],
+                    Applicability::MachineApplicable,
                 );
             },
         );
diff --git a/clippy_lints/src/loops/while_immutable_condition.rs b/clippy_lints/src/loops/while_immutable_condition.rs
index e7b3a2c4973cf..cc1bd5929d0fc 100644
--- a/clippy_lints/src/loops/while_immutable_condition.rs
+++ b/clippy_lints/src/loops/while_immutable_condition.rs
@@ -1,5 +1,5 @@
 use super::WHILE_IMMUTABLE_CONDITION;
-use clippy_utils::consts::constant;
+use clippy_utils::consts::ConstEvalCtxt;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::usage::mutated_variables;
 use rustc_hir::def::{DefKind, Res};
@@ -10,7 +10,7 @@ use rustc_lint::LateContext;
 use std::ops::ControlFlow;
 
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &'tcx Expr<'_>) {
-    if constant(cx, cx.typeck_results(), cond).is_some() {
+    if ConstEvalCtxt::new(cx).eval(cond).is_some() {
         // A pure constant condition (e.g., `while false`) is not linted.
         return;
     }
diff --git a/clippy_lints/src/loops/while_let_on_iterator.rs b/clippy_lints/src/loops/while_let_on_iterator.rs
index 194dd4752f91b..c171fa1c622af 100644
--- a/clippy_lints/src/loops/while_let_on_iterator.rs
+++ b/clippy_lints/src/loops/while_let_on_iterator.rs
@@ -14,7 +14,7 @@ use rustc_span::symbol::sym;
 use rustc_span::Symbol;
 
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-    if let Some(higher::WhileLet { if_then, let_pat, let_expr, .. }) = higher::WhileLet::hir(expr)
+    if let Some(higher::WhileLet { if_then, let_pat, let_expr, label, .. }) = higher::WhileLet::hir(expr)
         // check for `Some(..)` pattern
         && let PatKind::TupleStruct(ref pat_path, some_pat, _) = let_pat.kind
         && is_res_lang_ctor(cx, cx.qpath_res(pat_path, let_pat.hir_id), LangItem::OptionSome)
@@ -27,6 +27,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         && !uses_iter(cx, &iter_expr_struct, if_then)
     {
         let mut applicability = Applicability::MachineApplicable;
+
+        let loop_label = label.map_or(String::new(), |l| format!("{}: ", l.ident.name));
+
         let loop_var = if let Some(some_pat) = some_pat.first() {
             if is_refutable(cx, some_pat) {
                 // Refutable patterns don't work with for loops.
@@ -57,7 +60,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
             expr.span.with_hi(let_expr.span.hi()),
             "this loop could be written as a `for` loop",
             "try",
-            format!("for {loop_var} in {iterator}{by_ref}"),
+            format!("{loop_label}for {loop_var} in {iterator}{by_ref}"),
             applicability,
         );
     }
diff --git a/clippy_lints/src/manual_clamp.rs b/clippy_lints/src/manual_clamp.rs
index a79ad018a042f..4123c933660bc 100644
--- a/clippy_lints/src/manual_clamp.rs
+++ b/clippy_lints/src/manual_clamp.rs
@@ -1,13 +1,13 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::higher::If;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::implements_trait;
 use clippy_utils::visitors::is_const_evaluatable;
 use clippy_utils::{
-    eq_expr_value, in_constant, is_diag_trait_item, is_trait_method, path_res, path_to_local_id, peel_blocks,
+    eq_expr_value, is_diag_trait_item, is_in_const_context, is_trait_method, path_res, path_to_local_id, peel_blocks,
     peel_blocks_with_stmt, MaybePath,
 };
 use itertools::Itertools;
@@ -122,8 +122,9 @@ impl<'tcx> ClampSuggestion<'tcx> {
         if max_type != min_type {
             return false;
         }
-        if let Some(max) = constant(cx, cx.typeck_results(), self.params.max)
-            && let Some(min) = constant(cx, cx.typeck_results(), self.params.min)
+        let ecx = ConstEvalCtxt::new(cx);
+        if let Some(max) = ecx.eval(self.params.max)
+            && let Some(min) = ecx.eval(self.params.min)
             && let Some(ord) = Constant::partial_cmp(cx.tcx, max_type, &min, &max)
         {
             ord != Ordering::Greater
@@ -146,7 +147,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp {
         if !self.msrv.meets(msrvs::CLAMP) {
             return;
         }
-        if !expr.span.from_expansion() && !in_constant(cx, expr.hir_id) {
+        if !expr.span.from_expansion() && !is_in_const_context(cx) {
             let suggestion = is_if_elseif_else_pattern(cx, expr)
                 .or_else(|| is_max_min_pattern(cx, expr))
                 .or_else(|| is_call_max_min_pattern(cx, expr))
@@ -159,7 +160,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualClamp {
     }
 
     fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
-        if !self.msrv.meets(msrvs::CLAMP) || in_constant(cx, block.hir_id) {
+        if !self.msrv.meets(msrvs::CLAMP) || is_in_const_context(cx) {
             return;
         }
         for suggestion in is_two_if_pattern(cx, block) {
diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs
index 03416ba96de73..6bdc79129a9bc 100644
--- a/clippy_lints/src/manual_float_methods.rs
+++ b/clippy_lints/src/manual_float_methods.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::{is_from_proc_macro, path_to_local};
@@ -95,8 +95,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
                     || cx.tcx.features().declared(sym!(const_float_classify))
             )
             && let [first, second, const_1, const_2] = exprs
-            && let Some(const_1) = constant(cx, cx.typeck_results(), const_1)
-            && let Some(const_2) = constant(cx, cx.typeck_results(), const_2)
+            && let ecx = ConstEvalCtxt::new(cx)
+            && let Some(const_1) = ecx.eval(const_1)
+            && let Some(const_2) = ecx.eval(const_2)
             && path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s))
             // The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in
             // case somebody does that for some reason
diff --git a/clippy_lints/src/manual_is_ascii_check.rs b/clippy_lints/src/manual_is_ascii_check.rs
index a9f21d34e4cee..31c37c3bc3b14 100644
--- a/clippy_lints/src/manual_is_ascii_check.rs
+++ b/clippy_lints/src/manual_is_ascii_check.rs
@@ -3,7 +3,7 @@ use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::matching_root_macro_call;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{higher, in_constant, path_to_local, peel_ref_operators};
+use clippy_utils::{higher, is_in_const_context, path_to_local, peel_ref_operators};
 use rustc_ast::ast::RangeLimits;
 use rustc_ast::LitKind::{Byte, Char};
 use rustc_errors::Applicability;
@@ -95,7 +95,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
             return;
         }
 
-        if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::IS_ASCII_DIGIT_CONST) {
+        if is_in_const_context(cx) && !self.msrv.meets(msrvs::IS_ASCII_DIGIT_CONST) {
             return;
         }
 
diff --git a/clippy_lints/src/manual_rem_euclid.rs b/clippy_lints/src/manual_rem_euclid.rs
index 78a750f0dcd04..6cf5d272d7d82 100644
--- a/clippy_lints/src/manual_rem_euclid.rs
+++ b/clippy_lints/src/manual_rem_euclid.rs
@@ -1,9 +1,9 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
-use clippy_utils::consts::{constant_full_int, FullInt};
+use clippy_utils::consts::{ConstEvalCtxt, FullInt};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_context;
-use clippy_utils::{in_constant, path_to_local};
+use clippy_utils::{is_in_const_context, path_to_local};
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
             && add_rhs.span.ctxt() == ctxt
             && !in_external_macro(cx.sess(), expr.span)
             && self.msrv.meets(msrvs::REM_EUCLID)
-            && (self.msrv.meets(msrvs::REM_EUCLID_CONST) || !in_constant(cx, expr.hir_id))
+            && (self.msrv.meets(msrvs::REM_EUCLID_CONST) || !is_in_const_context(cx))
             && let Some(const1) = check_for_unsigned_int_constant(cx, rem_rhs)
             && let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, add_lhs, add_rhs)
             && let ExprKind::Binary(rem2_op, rem2_lhs, rem2_rhs) = add_other.kind
@@ -117,7 +117,7 @@ fn check_for_either_unsigned_int_constant<'a>(
 }
 
 fn check_for_unsigned_int_constant<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option<u128> {
-    let int_const = constant_full_int(cx, cx.typeck_results(), expr)?;
+    let int_const = ConstEvalCtxt::new(cx).eval_full_int(expr)?;
     match int_const {
         FullInt::S(s) => s.try_into().ok(),
         FullInt::U(u) => Some(u),
diff --git a/clippy_lints/src/manual_rotate.rs b/clippy_lints/src/manual_rotate.rs
index a517a4d507526..07537fc65c08c 100644
--- a/clippy_lints/src/manual_rotate.rs
+++ b/clippy_lints/src/manual_rotate.rs
@@ -1,6 +1,6 @@
 use std::fmt::Display;
 
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::sugg;
 use rustc_errors::Applicability;
@@ -66,7 +66,7 @@ fn parse_shift<'tcx>(
             BinOpKind::Shr => ShiftDirection::Right,
             _ => return None,
         };
-        let const_expr = constant(cx, cx.typeck_results(), r)?;
+        let const_expr = ConstEvalCtxt::new(cx).eval(r)?;
         if let Constant::Int(shift) = const_expr {
             return Some((dir, shift, l));
         }
diff --git a/clippy_lints/src/manual_slice_size_calculation.rs b/clippy_lints/src/manual_slice_size_calculation.rs
index 429ee2637c2fa..b24a0f4695a6c 100644
--- a/clippy_lints/src/manual_slice_size_calculation.rs
+++ b/clippy_lints/src/manual_slice_size_calculation.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_context;
-use clippy_utils::{expr_or_init, in_constant, std_or_core};
+use clippy_utils::{expr_or_init, is_in_const_context, std_or_core};
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -44,7 +44,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualSliceSizeCalculation {
             && BinOpKind::Mul == op.node
             && !expr.span.from_expansion()
             // Does not apply inside const because size_of_val is not cost in stable.
-            && !in_constant(cx, expr.hir_id)
+            && !is_in_const_context(cx)
             && let Some(receiver) = simplify(cx, left, right)
         {
             let ctxt = expr.span.ctxt();
diff --git a/clippy_lints/src/manual_strip.rs b/clippy_lints/src/manual_strip.rs
index 686ecccf8294c..85cabd2800a65 100644
--- a/clippy_lints/src/manual_strip.rs
+++ b/clippy_lints/src/manual_strip.rs
@@ -1,11 +1,12 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
-use clippy_utils::consts::{constant, Constant};
-use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
 use clippy_utils::usage::mutated_variables;
 use clippy_utils::{eq_expr_value, higher, match_def_path, paths};
 use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::intravisit::{walk_expr, Visitor};
 use rustc_hir::{BinOpKind, BorrowKind, Expr, ExprKind};
@@ -14,6 +15,7 @@ use rustc_middle::ty;
 use rustc_session::impl_lint_pass;
 use rustc_span::source_map::Spanned;
 use rustc_span::Span;
+use std::iter;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -108,19 +110,19 @@ impl<'tcx> LateLintPass<'tcx> for ManualStrip {
                     format!("stripping a {kind_word} manually"),
                     |diag| {
                         diag.span_note(test_span, format!("the {kind_word} was tested here"));
-                        multispan_sugg(
-                            diag,
+                        diag.multipart_suggestion(
                             format!("try using the `strip_{kind_word}` method"),
-                            vec![(
+                            iter::once((
                                 test_span,
                                 format!(
                                     "if let Some(<stripped>) = {}.strip_{kind_word}({}) ",
                                     snippet(cx, target_arg.span, ".."),
                                     snippet(cx, pattern.span, "..")
                                 ),
-                            )]
-                            .into_iter()
-                            .chain(strippings.into_iter().map(|span| (span, "<stripped>".into()))),
+                            ))
+                            .chain(strippings.into_iter().map(|span| (span, "<stripped>".into())))
+                            .collect(),
+                            Applicability::HasPlaceholders,
                         );
                     },
                 );
@@ -145,7 +147,7 @@ fn len_arg<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<&'tcx E
 
 // Returns the length of the `expr` if it's a constant string or char.
 fn constant_length(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<u128> {
-    let value = constant(cx, cx.typeck_results(), expr)?;
+    let value = ConstEvalCtxt::new(cx).eval(expr)?;
     match value {
         Constant::Str(value) => Some(value.len() as u128),
         Constant::Char(value) => Some(value.len_utf8() as u128),
@@ -183,9 +185,9 @@ fn peel_ref<'a>(expr: &'a Expr<'_>) -> &'a Expr<'a> {
     }
 }
 
-// Find expressions where `target` is stripped using the length of `pattern`.
-// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}`
-// method.
+/// Find expressions where `target` is stripped using the length of `pattern`.
+/// We'll suggest replacing these expressions with the result of the `strip_{prefix,suffix}`
+/// method.
 fn find_stripping<'tcx>(
     cx: &LateContext<'tcx>,
     strip_kind: StripKind,
diff --git a/clippy_lints/src/manual_unwrap_or_default.rs b/clippy_lints/src/manual_unwrap_or_default.rs
index f1acc4b217480..8f8390b6f3f9e 100644
--- a/clippy_lints/src/manual_unwrap_or_default.rs
+++ b/clippy_lints/src/manual_unwrap_or_default.rs
@@ -10,7 +10,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher::IfLetOrMatch;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::implements_trait;
-use clippy_utils::{in_constant, is_default_equivalent, peel_blocks, span_contains_comment};
+use clippy_utils::{is_default_equivalent, is_in_const_context, peel_blocks, span_contains_comment};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -174,7 +174,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, expr)
             && !expr.span.from_expansion()
-            && !in_constant(cx, expr.hir_id)
+            && !is_in_const_context(cx)
         {
             handle(cx, if_let_or_match, expr);
         }
diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs
index 85a08f81c2f34..2d4c8daf5cb66 100644
--- a/clippy_lints/src/matches/manual_unwrap_or.rs
+++ b/clippy_lints/src/matches/manual_unwrap_or.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::constant_simple;
+use clippy_utils::consts::ConstEvalCtxt;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
 use clippy_utils::ty::is_type_diagnostic_item;
@@ -69,7 +69,7 @@ fn check_and_lint<'tcx>(
         && let Some(ty_name) = find_type_name(cx, ty)
         && let Some(or_body_snippet) = snippet_opt(cx, else_expr.span)
         && let Some(indent) = indent_of(cx, expr.span)
-        && constant_simple(cx, cx.typeck_results(), else_expr).is_some()
+        && ConstEvalCtxt::new(cx).eval_simple(else_expr).is_some()
     {
         lint(cx, expr, let_expr, ty_name, or_body_snippet, indent);
     }
diff --git a/clippy_lints/src/matches/match_ref_pats.rs b/clippy_lints/src/matches/match_ref_pats.rs
index aba4c85c59e28..5445ee1f04296 100644
--- a/clippy_lints/src/matches/match_ref_pats.rs
+++ b/clippy_lints/src/matches/match_ref_pats.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::{snippet, walk_span_to_context};
 use clippy_utils::sugg::Sugg;
 use core::iter::once;
@@ -54,7 +54,11 @@ where
 
     span_lint_and_then(cx, MATCH_REF_PATS, expr.span, title, |diag| {
         if !expr.span.from_expansion() {
-            multispan_sugg(diag, msg, first_sugg.chain(remaining_suggs));
+            diag.multipart_suggestion(
+                msg,
+                first_sugg.chain(remaining_suggs).collect(),
+                Applicability::MachineApplicable,
+            );
         }
     });
 }
diff --git a/clippy_lints/src/matches/match_wild_enum.rs b/clippy_lints/src/matches/match_wild_enum.rs
index 8d22ceb47f859..91e40e4275c07 100644
--- a/clippy_lints/src/matches/match_wild_enum.rs
+++ b/clippy_lints/src/matches/match_wild_enum.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{is_refutable, peel_hir_pat_refs, recurse_or_patterns};
 use rustc_errors::Applicability;
@@ -148,23 +148,27 @@ pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>]) {
             Applicability::MaybeIncorrect,
         ),
         variants => {
-            let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
-            let message = if adt_def.is_variant_list_non_exhaustive() || has_external_hidden {
-                suggestions.push("_".into());
-                "wildcard matches known variants and will also match future added variants"
+            let (message, add_wildcard) = if adt_def.is_variant_list_non_exhaustive() || has_external_hidden {
+                (
+                    "wildcard matches known variants and will also match future added variants",
+                    true,
+                )
             } else {
-                "wildcard match will also match any future added variants"
+                ("wildcard match will also match any future added variants", false)
             };
 
-            span_lint_and_sugg(
-                cx,
-                WILDCARD_ENUM_MATCH_ARM,
-                wildcard_span,
-                message,
-                "try",
-                suggestions.join(" | "),
-                Applicability::MaybeIncorrect,
-            );
+            span_lint_and_then(cx, WILDCARD_ENUM_MATCH_ARM, wildcard_span, message, |diag| {
+                let mut suggestions: Vec<_> = variants.iter().copied().map(format_suggestion).collect();
+                if add_wildcard {
+                    suggestions.push("_".into());
+                }
+                diag.span_suggestion(
+                    wildcard_span,
+                    "try",
+                    suggestions.join(" | "),
+                    Applicability::MaybeIncorrect,
+                );
+            });
         },
     };
 }
@@ -176,9 +180,8 @@ enum CommonPrefixSearcher<'a> {
 }
 impl<'a> CommonPrefixSearcher<'a> {
     fn with_path(&mut self, path: &'a [PathSegment<'a>]) {
-        match path {
-            [path @ .., _] => self.with_prefix(path),
-            [] => (),
+        if let [path @ .., _] = path {
+            self.with_prefix(path);
         }
     }
 
diff --git a/clippy_lints/src/matches/match_wild_err_arm.rs b/clippy_lints/src/matches/match_wild_err_arm.rs
index 310675d01a204..d0d2025878e48 100644
--- a/clippy_lints/src/matches/match_wild_err_arm.rs
+++ b/clippy_lints/src/matches/match_wild_err_arm.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_note;
 use clippy_utils::macros::{is_panic, root_macro_call};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::visitors::is_local_used;
-use clippy_utils::{in_constant, is_wild, peel_blocks_with_stmt};
+use clippy_utils::{is_in_const_context, is_wild, peel_blocks_with_stmt};
 use rustc_hir::{Arm, Expr, PatKind};
 use rustc_lint::LateContext;
 use rustc_span::symbol::{kw, sym};
@@ -11,7 +11,7 @@ use super::MATCH_WILD_ERR_ARM;
 
 pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &Expr<'tcx>, arms: &[Arm<'tcx>]) {
     // `unwrap`/`expect` is not (yet) const, so we want to allow this in const contexts for now
-    if in_constant(cx, ex.hir_id) {
+    if is_in_const_context(cx) {
         return;
     }
 
diff --git a/clippy_lints/src/matches/mod.rs b/clippy_lints/src/matches/mod.rs
index cec3504ed8d09..cf5377e0725dd 100644
--- a/clippy_lints/src/matches/mod.rs
+++ b/clippy_lints/src/matches/mod.rs
@@ -27,7 +27,7 @@ mod wild_in_or_pats;
 use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
 use clippy_utils::source::walk_span_to_context;
-use clippy_utils::{higher, in_constant, is_direct_expn_of, is_span_match, span_contains_cfg};
+use clippy_utils::{higher, is_direct_expn_of, is_in_const_context, is_span_match, span_contains_cfg};
 use rustc_hir::{Arm, Expr, ExprKind, LetStmt, MatchSource, Pat, PatKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -1069,7 +1069,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
                     match_str_case_mismatch::check(cx, ex, arms);
                     redundant_guards::check(cx, arms, &self.msrv);
 
-                    if !in_constant(cx, expr.hir_id) {
+                    if !is_in_const_context(cx) {
                         manual_unwrap_or::check_match(cx, expr, ex, arms);
                         manual_map::check_match(cx, expr, ex, arms);
                         manual_filter::check_match(cx, ex, arms, expr);
@@ -1098,7 +1098,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
                             else_expr,
                         );
                     }
-                    if !in_constant(cx, expr.hir_id) {
+                    if !is_in_const_context(cx) {
                         manual_unwrap_or::check_if_let(
                             cx,
                             expr,
diff --git a/clippy_lints/src/matches/overlapping_arms.rs b/clippy_lints/src/matches/overlapping_arms.rs
index 45b375dbe3d72..71f211be925bf 100644
--- a/clippy_lints/src/matches/overlapping_arms.rs
+++ b/clippy_lints/src/matches/overlapping_arms.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant, constant_full_int, mir_to_const, FullInt};
+use clippy_utils::consts::{mir_to_const, ConstEvalCtxt, FullInt};
 use clippy_utils::diagnostics::span_lint_and_note;
 use core::cmp::Ordering;
 use rustc_hir::{Arm, Expr, PatKind, RangeEnd};
@@ -33,22 +33,20 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
         .filter_map(|arm| {
             if let Arm { pat, guard: None, .. } = *arm {
                 if let PatKind::Range(ref lhs, ref rhs, range_end) = pat.kind {
-                    let lhs_const = match lhs {
-                        Some(lhs) => constant(cx, cx.typeck_results(), lhs)?,
-                        None => {
-                            let min_val_const = ty.numeric_min_val(cx.tcx)?;
-                            mir_to_const(cx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))?
-                        },
+                    let lhs_const = if let Some(lhs) = lhs {
+                        ConstEvalCtxt::new(cx).eval(lhs)?
+                    } else {
+                        let min_val_const = ty.numeric_min_val(cx.tcx)?;
+                        mir_to_const(cx.tcx, mir::Const::from_ty_const(min_val_const, ty, cx.tcx))?
                     };
-                    let rhs_const = match rhs {
-                        Some(rhs) => constant(cx, cx.typeck_results(), rhs)?,
-                        None => {
-                            let max_val_const = ty.numeric_max_val(cx.tcx)?;
-                            mir_to_const(cx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))?
-                        },
+                    let rhs_const = if let Some(rhs) = rhs {
+                        ConstEvalCtxt::new(cx).eval(rhs)?
+                    } else {
+                        let max_val_const = ty.numeric_max_val(cx.tcx)?;
+                        mir_to_const(cx.tcx, mir::Const::from_ty_const(max_val_const, ty, cx.tcx))?
                     };
-                    let lhs_val = lhs_const.int_value(cx, ty)?;
-                    let rhs_val = rhs_const.int_value(cx, ty)?;
+                    let lhs_val = lhs_const.int_value(cx.tcx, ty)?;
+                    let rhs_val = rhs_const.int_value(cx.tcx, ty)?;
                     let rhs_bound = match range_end {
                         RangeEnd::Included => EndBound::Included(rhs_val),
                         RangeEnd::Excluded => EndBound::Excluded(rhs_val),
@@ -60,7 +58,7 @@ fn all_ranges<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>], ty: Ty<'tcx>)
                 }
 
                 if let PatKind::Lit(value) = pat.kind {
-                    let value = constant_full_int(cx, cx.typeck_results(), value)?;
+                    let value = ConstEvalCtxt::new(cx).eval_full_int(value)?;
                     return Some(SpannedRange {
                         span: pat.span,
                         node: (value, EndBound::Included(value)),
diff --git a/clippy_lints/src/matches/redundant_guards.rs b/clippy_lints/src/matches/redundant_guards.rs
index c2c0fbf439d51..8222c3057f73e 100644
--- a/clippy_lints/src/matches/redundant_guards.rs
+++ b/clippy_lints/src/matches/redundant_guards.rs
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::matching_root_macro_call;
 use clippy_utils::source::snippet;
 use clippy_utils::visitors::{for_each_expr_without_closures, is_local_used};
-use clippy_utils::{in_constant, path_to_local};
+use clippy_utils::{is_in_const_context, path_to_local};
 use rustc_ast::{BorrowKind, LitKind};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
@@ -116,7 +116,7 @@ fn check_method_calls<'tcx>(
         // `s if s.is_empty()` becomes ""
         // `arr if arr.is_empty()` becomes []
 
-        if ty.is_str() && !in_constant(cx, if_expr.hir_id) {
+        if ty.is_str() && !is_in_const_context(cx) {
             r#""""#.into()
         } else if slice_like {
             "[]".into()
diff --git a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs
index 316b2f63e4eaa..2154cd5b24a54 100644
--- a/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs
+++ b/clippy_lints/src/matches/rest_pat_in_fully_bound_struct.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_hir::{Pat, PatKind, QPath};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
@@ -15,13 +15,15 @@ pub(crate) fn check(cx: &LateContext<'_>, pat: &Pat<'_>) {
         && fields.len() == def.non_enum_variant().fields.len()
         && !def.non_enum_variant().is_field_list_non_exhaustive()
     {
-        span_lint_and_help(
+        #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+        span_lint_and_then(
             cx,
             REST_PAT_IN_FULLY_BOUND_STRUCTS,
             pat.span,
             "unnecessary use of `..` pattern in struct binding. All fields were already bound",
-            None,
-            "consider removing `..` from this binding",
+            |diag| {
+                diag.help("consider removing `..` from this binding");
+            },
         );
     }
 }
diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs
index 99fdbcff890b4..24dea03601c5f 100644
--- a/clippy_lints/src/matches/single_match.rs
+++ b/clippy_lints/src/matches/single_match.rs
@@ -1,12 +1,17 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::{expr_block, snippet, SpanRangeExt};
-use clippy_utils::ty::{implements_trait, is_type_diagnostic_item, peel_mid_ty_refs};
-use clippy_utils::{is_lint_allowed, is_unit_expr, is_wild, peel_blocks, peel_hir_pat_refs, peel_n_hir_expr_refs};
-use core::cmp::max;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::{
+    is_lint_allowed, is_unit_expr, peel_blocks, peel_hir_pat_refs, peel_middle_ty_refs, peel_n_hir_expr_refs,
+};
+use core::ops::ControlFlow;
+use rustc_arena::DroplessArena;
 use rustc_errors::Applicability;
-use rustc_hir::{Arm, BindingMode, Block, Expr, ExprKind, Pat, PatKind};
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::intravisit::{walk_pat, Visitor};
+use rustc_hir::{Arm, Expr, ExprKind, HirId, Pat, PatKind, QPath};
 use rustc_lint::LateContext;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, AdtDef, ParamEnv, TyCtxt, TypeckResults, VariantDef};
 use rustc_span::{sym, Span};
 
 use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE};
@@ -27,52 +32,58 @@ fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool {
 }
 
 #[rustfmt::skip]
-pub(crate) fn check(cx: &LateContext<'_>, ex: &Expr<'_>, arms: &[Arm<'_>], expr: &Expr<'_>) {
-    if arms.len() == 2 && arms[0].guard.is_none() && arms[1].guard.is_none() {
-        if expr.span.from_expansion() {
-            // Don't lint match expressions present in
-            // macro_rules! block
-            return;
-        }
-        if let PatKind::Or(..) = arms[0].pat.kind {
-            // don't lint for or patterns for now, this makes
-            // the lint noisy in unnecessary situations
-            return;
-        }
-        let els = arms[1].body;
-        let els = if is_unit_expr(peel_blocks(els)) && !empty_arm_has_comment(cx, els.span) {
+pub(crate) fn check<'tcx>(cx: &LateContext<'tcx>, ex: &'tcx Expr<'_>, arms: &'tcx [Arm<'_>], expr: &'tcx Expr<'_>) {
+    if let [arm1, arm2] = arms
+        && arm1.guard.is_none()
+        && arm2.guard.is_none()
+        && !expr.span.from_expansion()
+        // don't lint for or patterns for now, this makes
+        // the lint noisy in unnecessary situations
+        && !matches!(arm1.pat.kind, PatKind::Or(..))
+    {
+        let els = if is_unit_expr(peel_blocks(arm2.body)) && !empty_arm_has_comment(cx, arm2.body.span) {
             None
-        } else if let ExprKind::Block(Block { stmts, expr: block_expr, .. }, _) = els.kind {
-            if stmts.len() == 1 && block_expr.is_none() || stmts.is_empty() && block_expr.is_some() {
+        } else if let ExprKind::Block(block, _) = arm2.body.kind {
+            if matches!((block.stmts, block.expr), ([], Some(_)) | ([_], None)) {
                 // single statement/expr "else" block, don't lint
                 return;
             }
             // block with 2+ statements or 1 expr and 1+ statement
-            Some(els)
+            Some(arm2.body)
         } else {
             // not a block or an empty block w/ comments, don't lint
             return;
         };
 
-        let ty = cx.typeck_results().expr_ty(ex);
-        if (*ty.kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id)) &&
-            (check_single_pattern(arms) || check_opt_like(cx, arms, ty)) {
-            report_single_pattern(cx, ex, arms, expr, els);
+        let typeck = cx.typeck_results();
+        if *typeck.expr_ty(ex).peel_refs().kind() != ty::Bool || is_lint_allowed(cx, MATCH_BOOL, ex.hir_id) {
+            let mut v = PatVisitor {
+                typeck,
+                has_enum: false,
+            };
+            if v.visit_pat(arm2.pat).is_break() {
+                return;
+            }
+            if v.has_enum {
+                let cx = PatCtxt {
+                    tcx: cx.tcx,
+                    param_env: cx.param_env,
+                    typeck,
+                    arena: DroplessArena::default(),
+                };
+                let mut state = PatState::Other;
+                if !(state.add_pat(&cx, arm2.pat) || state.add_pat(&cx, arm1.pat)) {
+                    // Don't lint if the pattern contains an enum which doesn't have a wild match.
+                    return;
+                }
+            }
+
+            report_single_pattern(cx, ex, arm1, expr, els);
         }
     }
 }
 
-fn check_single_pattern(arms: &[Arm<'_>]) -> bool {
-    is_wild(arms[1].pat)
-}
-
-fn report_single_pattern(
-    cx: &LateContext<'_>,
-    ex: &Expr<'_>,
-    arms: &[Arm<'_>],
-    expr: &Expr<'_>,
-    els: Option<&Expr<'_>>,
-) {
+fn report_single_pattern(cx: &LateContext<'_>, ex: &Expr<'_>, arm: &Arm<'_>, expr: &Expr<'_>, els: Option<&Expr<'_>>) {
     let lint = if els.is_some() { SINGLE_MATCH_ELSE } else { SINGLE_MATCH };
     let ctxt = expr.span.ctxt();
     let mut app = Applicability::MachineApplicable;
@@ -80,9 +91,9 @@ fn report_single_pattern(
         format!(" else {}", expr_block(cx, els, ctxt, "..", Some(expr.span), &mut app))
     });
 
-    let (pat, pat_ref_count) = peel_hir_pat_refs(arms[0].pat);
+    let (pat, pat_ref_count) = peel_hir_pat_refs(arm.pat);
     let (msg, sugg) = if let PatKind::Path(_) | PatKind::Lit(_) = pat.kind
-        && let (ty, ty_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(ex))
+        && let (ty, ty_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(ex))
         && let Some(spe_trait_id) = cx.tcx.lang_items().structural_peq_trait()
         && let Some(pe_trait_id) = cx.tcx.lang_items().eq_trait()
         && (ty.is_integral()
@@ -114,17 +125,17 @@ fn report_single_pattern(
             snippet(cx, ex.span, ".."),
             // PartialEq for different reference counts may not exist.
             "&".repeat(ref_count_diff),
-            snippet(cx, arms[0].pat.span, ".."),
-            expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app),
+            snippet(cx, arm.pat.span, ".."),
+            expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app),
         );
         (msg, sugg)
     } else {
         let msg = "you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`";
         let sugg = format!(
             "if let {} = {} {}{els_str}",
-            snippet(cx, arms[0].pat.span, ".."),
+            snippet(cx, arm.pat.span, ".."),
             snippet(cx, ex.span, ".."),
-            expr_block(cx, arms[0].body, ctxt, "..", Some(expr.span), &mut app),
+            expr_block(cx, arm.body, ctxt, "..", Some(expr.span), &mut app),
         );
         (msg, sugg)
     };
@@ -132,106 +143,227 @@ fn report_single_pattern(
     span_lint_and_sugg(cx, lint, expr.span, msg, "try", sugg, app);
 }
 
-fn check_opt_like<'a>(cx: &LateContext<'a>, arms: &[Arm<'_>], ty: Ty<'a>) -> bool {
-    // We don't want to lint if the second arm contains an enum which could
-    // have more variants in the future.
-    form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat)
+struct PatVisitor<'tcx> {
+    typeck: &'tcx TypeckResults<'tcx>,
+    has_enum: bool,
 }
-
-/// Returns `true` if all of the types in the pattern are enums which we know
-/// won't be expanded in the future
-fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> bool {
-    let mut paths_and_types = Vec::new();
-    collect_pat_paths(&mut paths_and_types, cx, pat, ty);
-    paths_and_types.iter().all(|ty| in_candidate_enum(cx, *ty))
+impl<'tcx> Visitor<'tcx> for PatVisitor<'tcx> {
+    type Result = ControlFlow<()>;
+    fn visit_pat(&mut self, pat: &'tcx Pat<'_>) -> Self::Result {
+        if matches!(pat.kind, PatKind::Binding(..)) {
+            ControlFlow::Break(())
+        } else {
+            self.has_enum |= self.typeck.pat_ty(pat).ty_adt_def().map_or(false, AdtDef::is_enum);
+            walk_pat(self, pat)
+        }
+    }
 }
 
-/// Returns `true` if the given type is an enum we know won't be expanded in the future
-fn in_candidate_enum(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
-    // list of candidate `Enum`s we know will never get any more members
-    let candidates = [sym::Cow, sym::Option, sym::Result];
+/// The context needed to manipulate a `PatState`.
+struct PatCtxt<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    param_env: ParamEnv<'tcx>,
+    typeck: &'tcx TypeckResults<'tcx>,
+    arena: DroplessArena,
+}
 
-    for candidate_ty in candidates {
-        if is_type_diagnostic_item(cx, ty, candidate_ty) {
-            return true;
+/// State for tracking whether a match can become non-exhaustive by adding a variant to a contained
+/// enum.
+///
+/// This treats certain std enums as if they will never be extended.
+enum PatState<'a> {
+    /// Either a wild match or an uninteresting type. Uninteresting types include:
+    /// * builtin types (e.g. `i32` or `!`)
+    /// * A struct/tuple/array containing only uninteresting types.
+    /// * A std enum containing only uninteresting types.
+    Wild,
+    /// A std enum we know won't be extended. Tracks the states of each variant separately.
+    ///
+    /// This is not used for `Option` since it uses the current pattern to track it's state.
+    StdEnum(&'a mut [PatState<'a>]),
+    /// Either the initial state for a pattern or a non-std enum. There is currently no need to
+    /// distinguish these cases.
+    ///
+    /// For non-std enums there's no need to track the state of sub-patterns as the state of just
+    /// this pattern on it's own is enough for linting. Consider two cases:
+    /// * This enum has no wild match. This case alone is enough to determine we can lint.
+    /// * This enum has a wild match and therefore all sub-patterns also have a wild match.
+    ///
+    /// In both cases the sub patterns are not needed to determine whether to lint.
+    Other,
+}
+impl<'a> PatState<'a> {
+    /// Adds a set of patterns as a product type to the current state. Returns whether or not the
+    /// current state is a wild match after the merge.
+    fn add_product_pat<'tcx>(
+        &mut self,
+        cx: &'a PatCtxt<'tcx>,
+        pats: impl IntoIterator<Item = &'tcx Pat<'tcx>>,
+    ) -> bool {
+        // Ideally this would actually keep track of the state separately for each pattern. Doing so would
+        // require implementing something similar to exhaustiveness checking which is a significant increase
+        // in complexity.
+        //
+        // For now treat this as a wild match only if all the sub-patterns are wild
+        let is_wild = pats.into_iter().all(|p| {
+            let mut state = Self::Other;
+            state.add_pat(cx, p)
+        });
+        if is_wild {
+            *self = Self::Wild;
         }
+        is_wild
     }
-    false
-}
 
-/// Collects types from the given pattern
-fn collect_pat_paths<'a>(acc: &mut Vec<Ty<'a>>, cx: &LateContext<'a>, pat: &Pat<'_>, ty: Ty<'a>) {
-    match pat.kind {
-        PatKind::Tuple(inner, _) => inner.iter().for_each(|p| {
-            let p_ty = cx.typeck_results().pat_ty(p);
-            collect_pat_paths(acc, cx, p, p_ty);
-        }),
-        PatKind::TupleStruct(..) | PatKind::Binding(BindingMode::NONE, .., None) | PatKind::Path(_) => {
-            acc.push(ty);
-        },
-        _ => {},
+    /// Attempts to get the state for the enum variant, initializing the current state if necessary.
+    fn get_std_enum_variant<'tcx>(
+        &mut self,
+        cx: &'a PatCtxt<'tcx>,
+        adt: AdtDef<'tcx>,
+        path: &'tcx QPath<'_>,
+        hir_id: HirId,
+    ) -> Option<(&mut Self, &'tcx VariantDef)> {
+        let states = match self {
+            Self::Wild => return None,
+            Self::Other => {
+                *self = Self::StdEnum(cx.arena.alloc_from_iter((0..adt.variants().len()).map(|_| Self::Other)));
+                let Self::StdEnum(x) = self else {
+                    unreachable!();
+                };
+                x
+            },
+            Self::StdEnum(x) => x,
+        };
+        let i = match cx.typeck.qpath_res(path, hir_id) {
+            Res::Def(DefKind::Ctor(..), id) => adt.variant_index_with_ctor_id(id),
+            Res::Def(DefKind::Variant, id) => adt.variant_index_with_id(id),
+            _ => return None,
+        };
+        Some((&mut states[i.as_usize()], adt.variant(i)))
     }
-}
 
-/// Returns true if the given arm of pattern matching contains wildcard patterns.
-fn contains_only_wilds(pat: &Pat<'_>) -> bool {
-    match pat.kind {
-        PatKind::Wild => true,
-        PatKind::Tuple(inner, _) | PatKind::TupleStruct(_, inner, ..) => inner.iter().all(contains_only_wilds),
-        _ => false,
+    fn check_all_wild_enum(&mut self) -> bool {
+        if let Self::StdEnum(states) = self
+            && states.iter().all(|s| matches!(s, Self::Wild))
+        {
+            *self = Self::Wild;
+            true
+        } else {
+            false
+        }
     }
-}
 
-/// Returns true if the given patterns forms only exhaustive matches that don't contain enum
-/// patterns without a wildcard.
-fn form_exhaustive_matches<'a>(cx: &LateContext<'a>, ty: Ty<'a>, left: &Pat<'_>, right: &Pat<'_>) -> bool {
-    match (&left.kind, &right.kind) {
-        (PatKind::Wild, _) | (_, PatKind::Wild) => true,
-        (PatKind::Tuple(left_in, left_pos), PatKind::Tuple(right_in, right_pos)) => {
-            // We don't actually know the position and the presence of the `..` (dotdot) operator
-            // in the arms, so we need to evaluate the correct offsets here in order to iterate in
-            // both arms at the same time.
-            let left_pos = left_pos.as_opt_usize();
-            let right_pos = right_pos.as_opt_usize();
-            let len = max(
-                left_in.len() + usize::from(left_pos.is_some()),
-                right_in.len() + usize::from(right_pos.is_some()),
-            );
-            let mut left_pos = left_pos.unwrap_or(usize::MAX);
-            let mut right_pos = right_pos.unwrap_or(usize::MAX);
-            let mut left_dot_space = 0;
-            let mut right_dot_space = 0;
-            for i in 0..len {
-                let mut found_dotdot = false;
-                if i == left_pos {
-                    left_dot_space += 1;
-                    if left_dot_space < len - left_in.len() {
-                        left_pos += 1;
-                    }
-                    found_dotdot = true;
-                }
-                if i == right_pos {
-                    right_dot_space += 1;
-                    if right_dot_space < len - right_in.len() {
-                        right_pos += 1;
-                    }
-                    found_dotdot = true;
-                }
-                if found_dotdot {
-                    continue;
+    #[expect(clippy::similar_names)]
+    fn add_struct_pats<'tcx>(
+        &mut self,
+        cx: &'a PatCtxt<'tcx>,
+        pat: &'tcx Pat<'tcx>,
+        path: &'tcx QPath<'tcx>,
+        single_pat: Option<&'tcx Pat<'tcx>>,
+        pats: impl IntoIterator<Item = &'tcx Pat<'tcx>>,
+    ) -> bool {
+        let ty::Adt(adt, _) = *cx.typeck.pat_ty(pat).kind() else {
+            // Should never happen
+            *self = Self::Wild;
+            return true;
+        };
+        if adt.is_struct() {
+            return if let Some(pat) = single_pat
+                && adt.non_enum_variant().fields.len() == 1
+            {
+                self.add_pat(cx, pat)
+            } else {
+                self.add_product_pat(cx, pats)
+            };
+        }
+        match cx.tcx.get_diagnostic_name(adt.did()) {
+            Some(sym::Option) => {
+                if let Some(pat) = single_pat {
+                    self.add_pat(cx, pat)
+                } else {
+                    *self = Self::Wild;
+                    true
                 }
-                if !contains_only_wilds(&left_in[i - left_dot_space])
-                    && !contains_only_wilds(&right_in[i - right_dot_space])
+            },
+            Some(sym::Result | sym::Cow) => {
+                let Some((state, variant)) = self.get_std_enum_variant(cx, adt, path, pat.hir_id) else {
+                    return matches!(self, Self::Wild);
+                };
+                let is_wild = if let Some(pat) = single_pat
+                    && variant.fields.len() == 1
                 {
-                    return false;
-                }
-            }
-            true
-        },
-        (PatKind::TupleStruct(..), PatKind::Path(_)) => pat_in_candidate_enum(cx, ty, right),
-        (PatKind::TupleStruct(..), PatKind::TupleStruct(_, inner, _)) => {
-            pat_in_candidate_enum(cx, ty, right) && inner.iter().all(contains_only_wilds)
-        },
-        _ => false,
+                    state.add_pat(cx, pat)
+                } else {
+                    state.add_product_pat(cx, pats)
+                };
+                is_wild && self.check_all_wild_enum()
+            },
+            _ => matches!(self, Self::Wild),
+        }
+    }
+
+    /// Adds the pattern into the current state. Returns whether or not the current state is a wild
+    /// match after the merge.
+    #[expect(clippy::similar_names)]
+    fn add_pat<'tcx>(&mut self, cx: &'a PatCtxt<'tcx>, pat: &'tcx Pat<'_>) -> bool {
+        match pat.kind {
+            PatKind::Path(_)
+                if match *cx.typeck.pat_ty(pat).peel_refs().kind() {
+                    ty::Adt(adt, _) => adt.is_enum() || (adt.is_struct() && !adt.non_enum_variant().fields.is_empty()),
+                    ty::Tuple(tys) => !tys.is_empty(),
+                    ty::Array(_, len) => len.try_eval_target_usize(cx.tcx, cx.param_env) != Some(1),
+                    ty::Slice(..) => true,
+                    _ => false,
+                } =>
+            {
+                matches!(self, Self::Wild)
+            },
+
+            // Patterns for things which can only contain a single sub-pattern.
+            PatKind::Binding(_, _, _, Some(pat)) | PatKind::Ref(pat, _) | PatKind::Box(pat) | PatKind::Deref(pat) => {
+                self.add_pat(cx, pat)
+            },
+            PatKind::Tuple([sub_pat], pos)
+                if pos.as_opt_usize().is_none() || cx.typeck.pat_ty(pat).tuple_fields().len() == 1 =>
+            {
+                self.add_pat(cx, sub_pat)
+            },
+            PatKind::Slice([sub_pat], _, []) | PatKind::Slice([], _, [sub_pat])
+                if let ty::Array(_, len) = *cx.typeck.pat_ty(pat).kind()
+                    && len.try_eval_target_usize(cx.tcx, cx.param_env) == Some(1) =>
+            {
+                self.add_pat(cx, sub_pat)
+            },
+
+            PatKind::Or(pats) => pats.iter().any(|p| self.add_pat(cx, p)),
+            PatKind::Tuple(pats, _) => self.add_product_pat(cx, pats),
+            PatKind::Slice(head, _, tail) => self.add_product_pat(cx, head.iter().chain(tail)),
+
+            PatKind::TupleStruct(ref path, pats, _) => self.add_struct_pats(
+                cx,
+                pat,
+                path,
+                if let [pat] = pats { Some(pat) } else { None },
+                pats.iter(),
+            ),
+            PatKind::Struct(ref path, pats, _) => self.add_struct_pats(
+                cx,
+                pat,
+                path,
+                if let [pat] = pats { Some(pat.pat) } else { None },
+                pats.iter().map(|p| p.pat),
+            ),
+
+            PatKind::Wild
+            | PatKind::Binding(_, _, _, None)
+            | PatKind::Lit(_)
+            | PatKind::Range(..)
+            | PatKind::Path(_)
+            | PatKind::Never
+            | PatKind::Err(_) => {
+                *self = PatState::Wild;
+                true
+            },
+        }
     }
 }
diff --git a/clippy_lints/src/matches/try_err.rs b/clippy_lints/src/matches/try_err.rs
index dd489fc250b7b..b7ffa8b8a7814 100644
--- a/clippy_lints/src/matches/try_err.rs
+++ b/clippy_lints/src/matches/try_err.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{get_parent_expr, is_res_lang_ctor, path_res};
@@ -48,29 +48,28 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
             return;
         };
 
-        let expr_err_ty = cx.typeck_results().expr_ty(err_arg);
-        let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt());
-        let mut applicability = Applicability::MachineApplicable;
-        let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability);
-        let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) {
-            "" // already returns
-        } else {
-            "return "
-        };
-        let suggestion = if err_ty == expr_err_ty {
-            format!("{ret_prefix}{prefix}{origin_snippet}{suffix}")
-        } else {
-            format!("{ret_prefix}{prefix}{origin_snippet}.into(){suffix}")
-        };
-
-        span_lint_and_sugg(
+        span_lint_and_then(
             cx,
             TRY_ERR,
             expr.span,
             "returning an `Err(_)` with the `?` operator",
-            "try",
-            suggestion,
-            applicability,
+            |diag| {
+                let expr_err_ty = cx.typeck_results().expr_ty(err_arg);
+                let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt());
+                let mut applicability = Applicability::MachineApplicable;
+                let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability);
+                let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) {
+                    "" // already returns
+                } else {
+                    "return "
+                };
+                let suggestion = if err_ty == expr_err_ty {
+                    format!("{ret_prefix}{prefix}{origin_snippet}{suffix}")
+                } else {
+                    format!("{ret_prefix}{prefix}{origin_snippet}.into(){suffix}")
+                };
+                diag.span_suggestion(expr.span, "try", suggestion, applicability);
+            },
         );
     }
 }
diff --git a/clippy_lints/src/methods/bind_instead_of_map.rs b/clippy_lints/src/methods/bind_instead_of_map.rs
index 20f3722e173ad..2a8a5fcc3b6f9 100644
--- a/clippy_lints/src/methods/bind_instead_of_map.rs
+++ b/clippy_lints/src/methods/bind_instead_of_map.rs
@@ -1,5 +1,5 @@
 use super::{contains_return, BIND_INSTEAD_OF_MAP};
-use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::peel_blocks;
 use clippy_utils::source::{snippet, snippet_with_context};
 use clippy_utils::visitors::find_all_ret_expressions;
@@ -136,15 +136,16 @@ impl BindInsteadOfMap {
             return false;
         };
         span_lint_and_then(cx, BIND_INSTEAD_OF_MAP, expr.span, msg, |diag| {
-            multispan_sugg_with_applicability(
-                diag,
-                "try",
+            diag.multipart_suggestion(
+                format!("use `{}` instead", self.good_method_name),
+                std::iter::once((span, self.good_method_name.into()))
+                    .chain(
+                        suggs
+                            .into_iter()
+                            .map(|(span1, span2)| (span1, snippet(cx, span2, "_").into())),
+                    )
+                    .collect(),
                 Applicability::MachineApplicable,
-                std::iter::once((span, self.good_method_name.into())).chain(
-                    suggs
-                        .into_iter()
-                        .map(|(span1, span2)| (span1, snippet(cx, span2, "_").into())),
-                ),
             );
         });
         true
diff --git a/clippy_lints/src/methods/clone_on_ref_ptr.rs b/clippy_lints/src/methods/clone_on_ref_ptr.rs
index 926bd06bacbd2..e0826b53004b6 100644
--- a/clippy_lints/src/methods/clone_on_ref_ptr.rs
+++ b/clippy_lints/src/methods/clone_on_ref_ptr.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_with_context;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -29,19 +29,22 @@ pub(super) fn check(
             sym::RcWeak | sym::ArcWeak => "Weak",
             _ => return,
         };
-
-        // Sometimes unnecessary ::<_> after Rc/Arc/Weak
-        let mut app = Applicability::Unspecified;
-        let snippet = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "..", &mut app).0;
-
-        span_lint_and_sugg(
+        span_lint_and_then(
             cx,
             CLONE_ON_REF_PTR,
             expr.span,
             "using `.clone()` on a ref-counted pointer",
-            "try",
-            format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)),
-            app,
+            |diag| {
+                // Sometimes unnecessary ::<_> after Rc/Arc/Weak
+                let mut app = Applicability::Unspecified;
+                let snippet = snippet_with_context(cx, receiver.span, expr.span.ctxt(), "..", &mut app).0;
+                diag.span_suggestion(
+                    expr.span,
+                    "try",
+                    format!("{caller_type}::<{}>::clone(&{snippet})", subst.type_at(0)),
+                    app,
+                );
+            },
         );
     }
 }
diff --git a/clippy_lints/src/methods/filetype_is_file.rs b/clippy_lints/src/methods/filetype_is_file.rs
index eab536b88a5b3..2ab0401947c4c 100644
--- a/clippy_lints/src/methods/filetype_is_file.rs
+++ b/clippy_lints/src/methods/filetype_is_file.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::get_parent_expr;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_hir as hir;
@@ -33,6 +33,9 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
         span = expr.span;
     }
     let lint_msg = format!("`{lint_unary}FileType::is_file()` only {verb} regular files");
-    let help_msg = format!("use `{help_unary}FileType::is_dir()` instead");
-    span_lint_and_help(cx, FILETYPE_IS_FILE, span, lint_msg, None, help_msg);
+
+    #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+    span_lint_and_then(cx, FILETYPE_IS_FILE, span, lint_msg, |diag| {
+        diag.help(format!("use `{help_unary}FileType::is_dir()` instead"));
+    });
 }
diff --git a/clippy_lints/src/methods/get_unwrap.rs b/clippy_lints/src/methods/get_unwrap.rs
index 455274a442854..c6285c87a2636 100644
--- a/clippy_lints/src/methods/get_unwrap.rs
+++ b/clippy_lints/src/methods/get_unwrap.rs
@@ -1,5 +1,5 @@
 use super::utils::derefs_to_slice;
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::get_parent_expr;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::is_type_diagnostic_item;
@@ -19,9 +19,7 @@ pub(super) fn check<'tcx>(
 ) {
     // Note: we don't want to lint `get_mut().unwrap` for `HashMap` or `BTreeMap`,
     // because they do not implement `IndexMut`
-    let mut applicability = Applicability::MachineApplicable;
     let expr_ty = cx.typeck_results().expr_ty(recv);
-    let get_args_str = snippet_with_applicability(cx, get_arg.span, "..", &mut applicability);
     let caller_type = if derefs_to_slice(cx, recv, expr_ty).is_some() {
         "slice"
     } else if is_type_diagnostic_item(cx, expr_ty, sym::Vec) {
@@ -58,24 +56,34 @@ pub(super) fn check<'tcx>(
     };
 
     let mut_str = if is_mut { "_mut" } else { "" };
-    let borrow_str = if !needs_ref {
-        ""
-    } else if is_mut {
-        "&mut "
-    } else {
-        "&"
-    };
 
-    span_lint_and_sugg(
+    span_lint_and_then(
         cx,
         GET_UNWRAP,
         span,
-        format!("called `.get{mut_str}().unwrap()` on a {caller_type}. Using `[]` is more clear and more concise"),
-        "try",
-        format!(
-            "{borrow_str}{}[{get_args_str}]",
-            snippet_with_applicability(cx, recv.span, "..", &mut applicability)
-        ),
-        applicability,
+        format!("called `.get{mut_str}().unwrap()` on a {caller_type}"),
+        |diag| {
+            let mut applicability = Applicability::MachineApplicable;
+            let get_args_str = snippet_with_applicability(cx, get_arg.span, "..", &mut applicability);
+
+            let borrow_str = if !needs_ref {
+                ""
+            } else if is_mut {
+                "&mut "
+            } else {
+                "&"
+            };
+
+            diag.span_suggestion_with_style(
+                span,
+                "using `[]` is clearer and more concise",
+                format!(
+                    "{borrow_str}{}[{get_args_str}]",
+                    snippet_with_applicability(cx, recv.span, "..", &mut applicability)
+                ),
+                applicability,
+                rustc_errors::SuggestionStyle::ShowAlways,
+            );
+        },
     );
 }
diff --git a/clippy_lints/src/methods/implicit_clone.rs b/clippy_lints/src/methods/implicit_clone.rs
index c510cd915d0c9..519091406ccf3 100644
--- a/clippy_lints/src/methods/implicit_clone.rs
+++ b/clippy_lints/src/methods/implicit_clone.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_context;
-use clippy_utils::ty::{implements_trait, peel_mid_ty_refs};
-use clippy_utils::{is_diag_item_method, is_diag_trait_item};
+use clippy_utils::ty::implements_trait;
+use clippy_utils::{is_diag_item_method, is_diag_trait_item, peel_middle_ty_refs};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -14,7 +14,7 @@ pub fn check(cx: &LateContext<'_>, method_name: &str, expr: &hir::Expr<'_>, recv
         && is_clone_like(cx, method_name, method_def_id)
         && let return_type = cx.typeck_results().expr_ty(expr)
         && let input_type = cx.typeck_results().expr_ty(recv)
-        && let (input_type, ref_count) = peel_mid_ty_refs(input_type)
+        && let (input_type, ref_count) = peel_middle_ty_refs(input_type)
         && !(ref_count > 0 && is_diag_trait_item(cx, method_def_id, sym::ToOwned))
         && let Some(ty_name) = input_type.ty_adt_def().map(|adt_def| cx.tcx.item_name(adt_def.did()))
         && return_type == input_type
diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs
index 210e4ae0a7bc0..22d896433f07a 100644
--- a/clippy_lints/src/methods/is_digit_ascii_radix.rs
+++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs
@@ -2,7 +2,7 @@
 
 use super::IS_DIGIT_ASCII_RADIX;
 use clippy_config::msrvs::{self, Msrv};
-use clippy_utils::consts::{constant_full_int, FullInt};
+use clippy_utils::consts::{ConstEvalCtxt, FullInt};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use rustc_errors::Applicability;
@@ -24,7 +24,7 @@ pub(super) fn check<'tcx>(
         return;
     }
 
-    if let Some(radix_val) = constant_full_int(cx, cx.typeck_results(), radix) {
+    if let Some(radix_val) = ConstEvalCtxt::new(cx).eval_full_int(radix) {
         let (num, replacement) = match radix_val {
             FullInt::S(10) | FullInt::U(10) => (10, "is_ascii_digit"),
             FullInt::S(16) | FullInt::U(16) => (16, "is_ascii_hexdigit"),
diff --git a/clippy_lints/src/methods/is_empty.rs b/clippy_lints/src/methods/is_empty.rs
index d921b7ea14f5d..cc82f6cfd6384 100644
--- a/clippy_lints/src/methods/is_empty.rs
+++ b/clippy_lints/src/methods/is_empty.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::constant_is_empty;
+use clippy_utils::consts::ConstEvalCtxt;
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::{find_binding_init, path_to_local};
 use rustc_hir::{Expr, HirId};
@@ -18,7 +18,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_
     if !receiver.span.eq_ctxt(init_expr.span) {
         return;
     }
-    if let Some(init_is_empty) = constant_is_empty(cx, init_expr) {
+    if let Some(init_is_empty) = ConstEvalCtxt::new(cx).eval_is_empty(init_expr) {
         span_lint(
             cx,
             CONST_IS_EMPTY,
diff --git a/clippy_lints/src/methods/iter_kv_map.rs b/clippy_lints/src/methods/iter_kv_map.rs
index 05e77386128f9..33de3b87abc82 100644
--- a/clippy_lints/src/methods/iter_kv_map.rs
+++ b/clippy_lints/src/methods/iter_kv_map.rs
@@ -1,15 +1,12 @@
-#![allow(unused_imports)]
-
 use super::ITER_KV_MAP;
 use clippy_config::msrvs::{self, Msrv};
-use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::{snippet, snippet_with_applicability};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::pat_is_wild;
+use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::is_type_diagnostic_item;
-use clippy_utils::{pat_is_wild, sugg};
-use rustc_hir::{BindingMode, Body, BorrowKind, ByRef, Expr, ExprKind, Mutability, Pat, PatKind};
-use rustc_lint::{LateContext, LintContext};
-use rustc_middle::ty;
-use rustc_span::{sym, Span};
+use rustc_hir::{Body, Expr, ExprKind, PatKind};
+use rustc_lint::LateContext;
+use rustc_span::sym;
 
 /// lint use of:
 ///
diff --git a/clippy_lints/src/methods/iter_nth_zero.rs b/clippy_lints/src/methods/iter_nth_zero.rs
index 262a57ab591a6..9ff6eaa348710 100644
--- a/clippy_lints/src/methods/iter_nth_zero.rs
+++ b/clippy_lints/src/methods/iter_nth_zero.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::{is_lang_item_or_ctor, is_trait_method};
@@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr
     if let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir().get_parent_item(expr.hir_id))
         && let def_id = item.owner_id.to_def_id()
         && is_trait_method(cx, expr, sym::Iterator)
-        && let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), arg)
+        && let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg)
         && !is_lang_item_or_ctor(cx, def_id, LangItem::IteratorNext)
     {
         let mut app = Applicability::MachineApplicable;
diff --git a/clippy_lints/src/methods/iter_skip_zero.rs b/clippy_lints/src/methods/iter_skip_zero.rs
index 6b696b42a6931..39e440e784f6d 100644
--- a/clippy_lints/src/methods/iter_skip_zero.rs
+++ b/clippy_lints/src/methods/iter_skip_zero.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::{is_from_proc_macro, is_trait_method};
 use rustc_errors::Applicability;
@@ -11,7 +11,7 @@ use super::ITER_SKIP_ZERO;
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg_expr: &Expr<'_>) {
     if !expr.span.from_expansion()
         && is_trait_method(cx, expr, sym::Iterator)
-        && let Some(arg) = constant(cx, cx.typeck_results(), arg_expr).and_then(|constant| {
+        && let Some(arg) = ConstEvalCtxt::new(cx).eval(arg_expr).and_then(|constant| {
             if let Constant::Int(arg) = constant {
                 Some(arg)
             } else {
diff --git a/clippy_lints/src/methods/iterator_step_by_zero.rs b/clippy_lints/src/methods/iterator_step_by_zero.rs
index b631cd00cda43..9b358235a40df 100644
--- a/clippy_lints/src/methods/iterator_step_by_zero.rs
+++ b/clippy_lints/src/methods/iterator_step_by_zero.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::is_trait_method;
 use rustc_hir as hir;
@@ -9,7 +9,7 @@ use super::ITERATOR_STEP_BY_ZERO;
 
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) {
     if is_trait_method(cx, expr, sym::Iterator) {
-        if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), arg) {
+        if let Some(Constant::Int(0)) = ConstEvalCtxt::new(cx).eval(arg) {
             span_lint(
                 cx,
                 ITERATOR_STEP_BY_ZERO,
diff --git a/clippy_lints/src/methods/map_clone.rs b/clippy_lints/src/methods/map_clone.rs
index a5ba5e5d8918c..08ce7e204dd27 100644
--- a/clippy_lints/src/methods/map_clone.rs
+++ b/clippy_lints/src/methods/map_clone.rs
@@ -2,10 +2,10 @@ use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{is_copy, is_type_diagnostic_item, should_call_clone_as_function};
-use clippy_utils::{is_diag_trait_item, match_def_path, paths, peel_blocks};
+use clippy_utils::{is_diag_trait_item, peel_blocks};
 use rustc_errors::Applicability;
-use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
+use rustc_hir::{self as hir, LangItem};
 use rustc_lint::LateContext;
 use rustc_middle::mir::Mutability;
 use rustc_middle::ty;
@@ -114,7 +114,7 @@ fn handle_path(
     recv: &hir::Expr<'_>,
 ) {
     if let Some(path_def_id) = cx.qpath_res(qpath, arg.hir_id).opt_def_id()
-        && match_def_path(cx, path_def_id, &paths::CLONE_TRAIT_METHOD)
+        && cx.tcx.lang_items().get(LangItem::CloneFn) == Some(path_def_id)
     {
         // The `copied` and `cloned` methods are only available on `&T` and `&mut T` in `Option`
         // and `Result`.
diff --git a/clippy_lints/src/methods/map_err_ignore.rs b/clippy_lints/src/methods/map_err_ignore.rs
index fbb83c8ce5635..162f0ac564d7d 100644
--- a/clippy_lints/src/methods/map_err_ignore.rs
+++ b/clippy_lints/src/methods/map_err_ignore.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_hir::{CaptureBy, Closure, Expr, ExprKind, PatKind};
 use rustc_lint::LateContext;
@@ -22,13 +22,17 @@ pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, arg: &Expr<'_>) {
     {
         // span the area of the closure capture and warn that the
         // original error will be thrown away
-        span_lint_and_help(
+        #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+        span_lint_and_then(
             cx,
             MAP_ERR_IGNORE,
             fn_decl_span,
             "`map_err(|_|...` wildcard pattern discards the original error",
-            None,
-            "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
+            |diag| {
+                diag.help(
+                    "consider storing the original error as a source in the new error, or silence this warning using an ignored identifier (`.map_err(|_foo| ...`)",
+                );
+            },
         );
     }
 }
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index aa4cad9342a17..1d7b10fe8f045 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -133,7 +133,7 @@ mod zst_offset;
 
 use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 use clippy_utils::macros::FormatArgsStorage;
 use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
@@ -749,7 +749,6 @@ declare_clippy_lint! {
     ///
      /// ### Example
     /// ```no_run
-    /// # #![allow(unused)]
     /// (0_i32..10)
     ///     .filter(|n| n.checked_add(1).is_some())
     ///     .map(|n| n.checked_add(1).unwrap());
@@ -757,7 +756,6 @@ declare_clippy_lint! {
     ///
     /// Use instead:
     /// ```no_run
-    /// # #[allow(unused)]
     /// (0_i32..10).filter_map(|n| n.checked_add(1));
     /// ```
     #[clippy::version = "1.51.0"]
@@ -850,7 +848,6 @@ declare_clippy_lint! {
     ///
     /// ### Example
     /// ```no_run
-    /// # #![allow(unused)]
     /// let vec = vec![1];
     /// vec.iter().find(|x| **x == 0).is_some();
     ///
@@ -862,7 +859,6 @@ declare_clippy_lint! {
     /// let vec = vec![1];
     /// vec.iter().any(|x| *x == 0);
     ///
-    /// # #[allow(unused)]
     /// !"hello world".contains("world");
     /// ```
     #[clippy::version = "pre 1.29.0"]
@@ -1505,7 +1501,6 @@ declare_clippy_lint! {
     ///
     /// ### Example
     /// ```no_run
-    /// # #[allow(unused)]
     /// (0..3).fold(false, |acc, x| acc || x > 2);
     /// ```
     ///
@@ -1897,7 +1892,9 @@ declare_clippy_lint! {
     /// trait.
     ///
     /// ### Why is this bad?
-    /// It is recommended style to use collect. See
+    /// If it's needed to create a collection from the contents of an iterator, the `Iterator::collect(_)`
+    /// method is preferred. However, when it's needed to specify the container type,
+    /// `Vec::from_iter(_)` can be more readable than using a turbofish (e.g. `_.collect::<Vec<_>>()`). See
     /// [FromIterator documentation](https://doc.rust-lang.org/std/iter/trait.FromIterator.html)
     ///
     /// ### Example
@@ -1916,6 +1913,14 @@ declare_clippy_lint! {
     ///
     /// assert_eq!(v, vec![5, 5, 5, 5, 5]);
     /// ```
+    /// but prefer to use
+    /// ```no_run
+    /// let numbers: Vec<i32> = FromIterator::from_iter(1..=5);
+    /// ```
+    /// instead of
+    /// ```no_run
+    /// let numbers = (1..=5).collect::<Vec<_>>();
+    /// ```
     #[clippy::version = "1.49.0"]
     pub FROM_ITER_INSTEAD_OF_COLLECT,
     pedantic,
@@ -2008,13 +2013,11 @@ declare_clippy_lint! {
     ///
     /// ### Example
     /// ```no_run
-    /// # #[allow(unused)]
     /// "Hello".bytes().nth(3);
     /// ```
     ///
     /// Use instead:
     /// ```no_run
-    /// # #[allow(unused)]
     /// "Hello".as_bytes().get(3);
     /// ```
     #[clippy::version = "1.52.0"]
@@ -2059,7 +2062,6 @@ declare_clippy_lint! {
     ///
     /// ### Example
     /// ```no_run
-    /// # #![allow(unused)]
     /// let some_vec = vec![0, 1, 2, 3];
     ///
     /// some_vec.iter().count();
@@ -3656,7 +3658,6 @@ declare_clippy_lint! {
     ///
     /// ### Example
     /// ```no_run
-    /// # #![allow(unused)]
     /// let owned_string = "This is a string".to_owned();
     /// owned_string.as_str().as_bytes()
     /// # ;
@@ -3664,7 +3665,6 @@ declare_clippy_lint! {
     ///
     /// Use instead:
     /// ```no_run
-    /// # #![allow(unused)]
     /// let owned_string = "This is a string".to_owned();
     /// owned_string.as_bytes()
     /// # ;
@@ -4915,13 +4915,13 @@ impl Methods {
                     str_split::check(cx, expr, recv, arg);
                 },
                 ("splitn" | "rsplitn", [count_arg, pat_arg]) => {
-                    if let Some(Constant::Int(count)) = constant(cx, cx.typeck_results(), count_arg) {
+                    if let Some(Constant::Int(count)) = ConstEvalCtxt::new(cx).eval(count_arg) {
                         suspicious_splitn::check(cx, name, expr, recv, count);
                         str_splitn::check(cx, name, expr, recv, pat_arg, count, &self.msrv);
                     }
                 },
                 ("splitn_mut" | "rsplitn_mut", [count_arg, _]) => {
-                    if let Some(Constant::Int(count)) = constant(cx, cx.typeck_results(), count_arg) {
+                    if let Some(Constant::Int(count)) = ConstEvalCtxt::new(cx).eval(count_arg) {
                         suspicious_splitn::check(cx, name, expr, recv, count);
                     }
                 },
diff --git a/clippy_lints/src/methods/open_options.rs b/clippy_lints/src/methods/open_options.rs
index d425b505a760c..cbeb48b6cc376 100644
--- a/clippy_lints/src/methods/open_options.rs
+++ b/clippy_lints/src/methods/open_options.rs
@@ -151,7 +151,7 @@ fn check_open_options(cx: &LateContext<'_>, settings: &[(OpenOption, Argument, S
                 cx,
                 NONSENSICAL_OPEN_OPTIONS,
                 prev_span,
-                format!("the method `{}` is called more than once", &option),
+                format!("the method `{option}` is called more than once"),
             );
         }
     }
diff --git a/clippy_lints/src/methods/repeat_once.rs b/clippy_lints/src/methods/repeat_once.rs
index bb4cdd2a6fa10..7837517ed5d8a 100644
--- a/clippy_lints/src/methods/repeat_once.rs
+++ b/clippy_lints/src/methods/repeat_once.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_lang_item;
@@ -14,7 +14,7 @@ pub(super) fn check<'tcx>(
     recv: &'tcx Expr<'_>,
     repeat_arg: &'tcx Expr<'_>,
 ) {
-    if constant(cx, cx.typeck_results(), repeat_arg) == Some(Constant::Int(1)) {
+    if ConstEvalCtxt::new(cx).eval(repeat_arg) == Some(Constant::Int(1)) {
         let ty = cx.typeck_results().expr_ty(recv).peel_refs();
         if ty.is_str() {
             span_lint_and_sugg(
diff --git a/clippy_lints/src/methods/str_splitn.rs b/clippy_lints/src/methods/str_splitn.rs
index 4f42fb73547a7..12cabd43cb1b2 100644
--- a/clippy_lints/src/methods/str_splitn.rs
+++ b/clippy_lints/src/methods/str_splitn.rs
@@ -1,5 +1,5 @@
 use clippy_config::msrvs::{self, Msrv};
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::usage::local_used_after_expr;
@@ -301,7 +301,7 @@ fn parse_iter_usage<'tcx>(
                     };
                 },
                 ("nth" | "skip", [idx_expr]) if cx.tcx.trait_of_item(did) == Some(iter_id) => {
-                    if let Some(Constant::Int(idx)) = constant(cx, cx.typeck_results(), idx_expr) {
+                    if let Some(Constant::Int(idx)) = ConstEvalCtxt::new(cx).eval(idx_expr) {
                         let span = if name.ident.as_str() == "nth" {
                             e.span
                         } else if let Some((_, Node::Expr(next_expr))) = iter.next()
diff --git a/clippy_lints/src/methods/unnecessary_min_or_max.rs b/clippy_lints/src/methods/unnecessary_min_or_max.rs
index 78851d4122f12..86c0a6322b666 100644
--- a/clippy_lints/src/methods/unnecessary_min_or_max.rs
+++ b/clippy_lints/src/methods/unnecessary_min_or_max.rs
@@ -3,7 +3,7 @@ use std::cmp::Ordering;
 use super::UNNECESSARY_MIN_OR_MAX;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 
-use clippy_utils::consts::{constant, constant_with_source, Constant, ConstantSource, FullInt};
+use clippy_utils::consts::{ConstEvalCtxt, Constant, ConstantSource, FullInt};
 use clippy_utils::source::snippet;
 
 use rustc_errors::Applicability;
@@ -20,10 +20,9 @@ pub(super) fn check<'tcx>(
     arg: &'tcx Expr<'_>,
 ) {
     let typeck_results = cx.typeck_results();
-    if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) =
-        constant_with_source(cx, typeck_results, recv)
-        && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) =
-            constant_with_source(cx, typeck_results, arg)
+    let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck_results);
+    if let Some((left, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(recv)
+        && let Some((right, ConstantSource::Local | ConstantSource::CoreConstant)) = ecx.eval_with_source(arg)
     {
         let Some(ord) = Constant::partial_cmp(cx.tcx, typeck_results.expr_ty(recv), &left, &right) else {
             return;
@@ -78,9 +77,9 @@ enum Extrema {
 fn detect_extrema<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<Extrema> {
     let ty = cx.typeck_results().expr_ty(expr);
 
-    let cv = constant(cx, cx.typeck_results(), expr)?;
+    let cv = ConstEvalCtxt::new(cx).eval(expr)?;
 
-    match (cv.int_value(cx, ty)?, ty.kind()) {
+    match (cv.int_value(cx.tcx, ty)?, ty.kind()) {
         (FullInt::S(i), &ty::Int(ity)) if i == i128::MIN >> (128 - ity.bit_width()?) => Some(Extrema::Minimum),
         (FullInt::S(i), &ty::Int(ity)) if i == i128::MAX >> (128 - ity.bit_width()?) => Some(Extrema::Maximum),
         (FullInt::U(i), &ty::Uint(uty)) if i == u128::MAX >> (128 - uty.bit_width()?) => Some(Extrema::Maximum),
diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs
index 5d899415d7728..fed2b128dcf30 100644
--- a/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -3,12 +3,11 @@ use super::unnecessary_iter_cloned::{self, is_into_iter};
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, snippet_opt};
-use clippy_utils::ty::{
-    get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item, peel_mid_ty_refs,
-};
+use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item};
 use clippy_utils::visitors::find_all_ret_expressions;
 use clippy_utils::{
-    fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, match_def_path, paths, return_ty,
+    fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, match_def_path, paths, peel_middle_ty_refs,
+    return_ty,
 };
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
@@ -120,8 +119,8 @@ fn check_addr_of_expr(
                 },
             ] = adjustments[..]
         && let receiver_ty = cx.typeck_results().expr_ty(receiver)
-        && let (target_ty, n_target_refs) = peel_mid_ty_refs(*target_ty)
-        && let (receiver_ty, n_receiver_refs) = peel_mid_ty_refs(receiver_ty)
+        && let (target_ty, n_target_refs) = peel_middle_ty_refs(*target_ty)
+        && let (receiver_ty, n_receiver_refs) = peel_middle_ty_refs(receiver_ty)
         // Only flag cases satisfying at least one of the following three conditions:
         // * the referent and receiver types are distinct
         // * the referent/receiver type is a copyable array
@@ -382,7 +381,7 @@ fn check_other_call_arg<'tcx>(
         && let fn_sig = cx.tcx.fn_sig(callee_def_id).instantiate_identity().skip_binder()
         && let Some(i) = recv.into_iter().chain(call_args).position(|arg| arg.hir_id == maybe_arg.hir_id)
         && let Some(input) = fn_sig.inputs().get(i)
-        && let (input, n_refs) = peel_mid_ty_refs(*input)
+        && let (input, n_refs) = peel_middle_ty_refs(*input)
         && let (trait_predicates, _) = get_input_traits_and_projections(cx, callee_def_id, input)
         && let Some(sized_def_id) = cx.tcx.lang_items().sized_trait()
         && let [trait_predicate] = trait_predicates
diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs
index 8b8a965b9f0c9..3004d9c4233a7 100644
--- a/clippy_lints/src/methods/unused_enumerate_index.rs
+++ b/clippy_lints/src/methods/unused_enumerate_index.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_hir_and_then};
+use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::source::{snippet, snippet_opt};
 use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild};
 use rustc_errors::Applicability;
@@ -97,10 +97,8 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>,
             enumerate_span,
             "you seem to use `.enumerate()` and immediately discard the index",
             |diag| {
-                multispan_sugg_with_applicability(
-                    diag,
+                diag.multipart_suggestion(
                     "remove the `.enumerate()` call",
-                    Applicability::MachineApplicable,
                     vec![
                         (closure_param.span, new_closure_param),
                         (
@@ -108,6 +106,7 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>,
                             String::new(),
                         ),
                     ],
+                    Applicability::MachineApplicable,
                 );
             },
         );
diff --git a/clippy_lints/src/methods/useless_asref.rs b/clippy_lints/src/methods/useless_asref.rs
index ae2b6e6347eb0..ebad4ae6ee95d 100644
--- a/clippy_lints/src/methods/useless_asref.rs
+++ b/clippy_lints/src/methods/useless_asref.rs
@@ -2,10 +2,10 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{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, paths, peel_blocks, strip_pat_refs,
+    get_parent_expr, is_diag_trait_item, match_def_path, path_to_local_id, peel_blocks, strip_pat_refs,
 };
 use rustc_errors::Applicability;
-use rustc_hir as hir;
+use rustc_hir::{self as hir, LangItem};
 use rustc_lint::LateContext;
 use rustc_middle::ty::adjustment::Adjust;
 use rustc_middle::ty::{Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor};
@@ -104,7 +104,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, call_name: &str,
 fn check_qpath(cx: &LateContext<'_>, qpath: hir::QPath<'_>, hir_id: hir::HirId) -> bool {
     // We check it's calling the `clone` method of the `Clone` trait.
     if let Some(path_def_id) = cx.qpath_res(&qpath, hir_id).opt_def_id() {
-        match_def_path(cx, path_def_id, &paths::CLONE_TRAIT_METHOD)
+        cx.tcx.lang_items().get(LangItem::CloneFn) == Some(path_def_id)
     } else {
         false
     }
diff --git a/clippy_lints/src/methods/verbose_file_reads.rs b/clippy_lints/src/methods/verbose_file_reads.rs
index 181b413a18288..8ed61637eca21 100644
--- a/clippy_lints/src/methods/verbose_file_reads.rs
+++ b/clippy_lints/src/methods/verbose_file_reads.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_trait_method;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_hir::{Expr, ExprKind, QPath};
@@ -23,6 +23,9 @@ pub(super) fn check<'tcx>(
         && matches!(recv.kind, ExprKind::Path(QPath::Resolved(None, _)))
         && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty_adjusted(recv).peel_refs(), sym::File)
     {
-        span_lint_and_help(cx, VERBOSE_FILE_READS, expr.span, msg, None, help);
+        #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+        span_lint_and_then(cx, VERBOSE_FILE_READS, expr.span, msg, |diag| {
+            diag.help(help);
+        });
     }
 }
diff --git a/clippy_lints/src/methods/wrong_self_convention.rs b/clippy_lints/src/methods/wrong_self_convention.rs
index 28068c6347325..7384e534ed7d7 100644
--- a/clippy_lints/src/methods/wrong_self_convention.rs
+++ b/clippy_lints/src/methods/wrong_self_convention.rs
@@ -127,7 +127,7 @@ pub(super) fn check<'tcx>(
                         .collect::<Vec<_>>()
                         .join(" and ");
 
-                    format!("methods with the following characteristics: ({})", &s)
+                    format!("methods with the following characteristics: ({s})")
                 } else {
                     format!("methods called {}", &conventions[0])
                 }
diff --git a/clippy_lints/src/minmax.rs b/clippy_lints/src/minmax.rs
index c3fbca1d560e6..e95864c6db8b5 100644
--- a/clippy_lints/src/minmax.rs
+++ b/clippy_lints/src/minmax.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant_simple, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::is_trait_method;
 use rustc_hir::{Expr, ExprKind};
@@ -106,15 +106,11 @@ fn fetch_const<'a, 'tcx>(
     if args.next().is_some() {
         return None;
     }
-    constant_simple(cx, cx.typeck_results(), first_arg).map_or_else(
-        || constant_simple(cx, cx.typeck_results(), second_arg).map(|c| (m, c, first_arg)),
-        |c| {
-            if constant_simple(cx, cx.typeck_results(), second_arg).is_none() {
-                // otherwise ignore
-                Some((m, c, second_arg))
-            } else {
-                None
-            }
-        },
-    )
+    let ecx = ConstEvalCtxt::new(cx);
+    match (ecx.eval_simple(first_arg), ecx.eval_simple(second_arg)) {
+        (Some(c), None) => Some((m, c, second_arg)),
+        (None, Some(c)) => Some((m, c, first_arg)),
+        // otherwise ignore
+        _ => None,
+    }
 }
diff --git a/clippy_lints/src/misc_early/literal_suffix.rs b/clippy_lints/src/misc_early/literal_suffix.rs
index e0a5e401a50aa..22467999cd8d5 100644
--- a/clippy_lints/src/misc_early/literal_suffix.rs
+++ b/clippy_lints/src/misc_early/literal_suffix.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_errors::Applicability;
 use rustc_lint::EarlyContext;
 use rustc_span::Span;
@@ -12,24 +12,36 @@ pub(super) fn check(cx: &EarlyContext<'_>, lit_span: Span, lit_snip: &str, suffi
     // Do not lint when literal is unsuffixed.
     if !suffix.is_empty() {
         if lit_snip.as_bytes()[maybe_last_sep_idx] == b'_' {
-            span_lint_and_sugg(
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(
                 cx,
                 SEPARATED_LITERAL_SUFFIX,
                 lit_span,
                 format!("{sugg_type} type suffix should not be separated by an underscore"),
-                "remove the underscore",
-                format!("{}{suffix}", &lit_snip[..maybe_last_sep_idx]),
-                Applicability::MachineApplicable,
+                |diag| {
+                    diag.span_suggestion(
+                        lit_span,
+                        "remove the underscore",
+                        format!("{}{suffix}", &lit_snip[..maybe_last_sep_idx]),
+                        Applicability::MachineApplicable,
+                    );
+                },
             );
         } else {
-            span_lint_and_sugg(
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(
                 cx,
                 UNSEPARATED_LITERAL_SUFFIX,
                 lit_span,
                 format!("{sugg_type} type suffix should be separated by an underscore"),
-                "add an underscore",
-                format!("{}_{suffix}", &lit_snip[..=maybe_last_sep_idx]),
-                Applicability::MachineApplicable,
+                |diag| {
+                    diag.span_suggestion(
+                        lit_span,
+                        "add an underscore",
+                        format!("{}_{suffix}", &lit_snip[..=maybe_last_sep_idx]),
+                        Applicability::MachineApplicable,
+                    );
+                },
             );
         }
     }
diff --git a/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/clippy_lints/src/misc_early/unneeded_field_pattern.rs
index cb305cf5582d1..6db03adf44ac9 100644
--- a/clippy_lints/src/misc_early/unneeded_field_pattern.rs
+++ b/clippy_lints/src/misc_early/unneeded_field_pattern.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::source::snippet_opt;
 use rustc_ast::ast::{Pat, PatKind};
 use rustc_lint::EarlyContext;
@@ -21,13 +21,15 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) {
             }
         }
         if !pfields.is_empty() && wilds == pfields.len() {
-            span_lint_and_help(
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(
                 cx,
                 UNNEEDED_FIELD_PATTERN,
                 pat.span,
                 "all the struct fields are matched to a wildcard pattern, consider using `..`",
-                None,
-                format!("try with `{type_name} {{ .. }}` instead"),
+                |diag| {
+                    diag.help(format!("try with `{type_name} {{ .. }}` instead"));
+                },
             );
             return;
         }
@@ -56,14 +58,15 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) {
                             }
                         }
 
-                        span_lint_and_help(
+                        #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+                        span_lint_and_then(
                             cx,
                             UNNEEDED_FIELD_PATTERN,
                             field.span,
-                            "you matched a field with a wildcard pattern, consider using `..` \
-                             instead",
-                            None,
-                            format!("try with `{type_name} {{ {}, .. }}`", normal[..].join(", ")),
+                            "you matched a field with a wildcard pattern, consider using `..` instead",
+                            |diag| {
+                                diag.help(format!("try with `{type_name} {{ {}, .. }}`", normal[..].join(", ")));
+                            },
                         );
                     }
                 }
diff --git a/clippy_lints/src/missing_assert_message.rs b/clippy_lints/src/missing_assert_message.rs
index 935ed48dacc50..a9ea11f4c2b06 100644
--- a/clippy_lints/src/missing_assert_message.rs
+++ b/clippy_lints/src/missing_assert_message.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_in_test;
 use clippy_utils::macros::{find_assert_args, find_assert_eq_args, root_macro_call_first_node, PanicExpn};
 use rustc_hir::Expr;
@@ -79,13 +79,15 @@ impl<'tcx> LateLintPass<'tcx> for MissingAssertMessage {
         };
 
         if let PanicExpn::Empty = panic_expn {
-            span_lint_and_help(
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(
                 cx,
                 MISSING_ASSERT_MESSAGE,
                 macro_call.span,
                 "assert without any message",
-                None,
-                "consider describing why the failing assert is problematic",
+                |diag| {
+                    diag.help("consider describing why the failing assert is problematic");
+                },
             );
         }
     }
diff --git a/clippy_lints/src/missing_const_for_fn.rs b/clippy_lints/src/missing_const_for_fn.rs
index 68c158330ab53..052d738b2e79e 100644
--- a/clippy_lints/src/missing_const_for_fn.rs
+++ b/clippy_lints/src/missing_const_for_fn.rs
@@ -186,7 +186,7 @@ fn already_const(header: hir::FnHeader) -> bool {
 fn could_be_const_with_abi(cx: &LateContext<'_>, msrv: &Msrv, abi: Abi) -> bool {
     match abi {
         Abi::Rust => true,
-        // `const extern "C"` was stablized after 1.62.0
+        // `const extern "C"` was stabilized after 1.62.0
         Abi::C { unwind: false } => msrv.meets(msrvs::CONST_EXTERN_FN),
         // Rest ABIs are still unstable and need the `const_extern_fn` feature enabled.
         _ => cx.tcx.features().const_extern_fn,
diff --git a/clippy_lints/src/missing_trait_methods.rs b/clippy_lints/src/missing_trait_methods.rs
index 85029a5e6a0d4..7ee746365d102 100644
--- a/clippy_lints/src/missing_trait_methods.rs
+++ b/clippy_lints/src/missing_trait_methods.rs
@@ -1,10 +1,9 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_lint_allowed;
 use clippy_utils::macros::span_is_local;
-use rustc_hir::def_id::DefIdMap;
+use rustc_hir::def_id::DefIdSet;
 use rustc_hir::{Impl, Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::AssocItem;
 use rustc_session::declare_lint_pass;
 
 declare_clippy_lint! {
@@ -68,33 +67,26 @@ impl<'tcx> LateLintPass<'tcx> for MissingTraitMethods {
             }) = item.kind
             && let Some(trait_id) = trait_ref.trait_def_id()
         {
-            let mut provided: DefIdMap<&AssocItem> = cx
-                .tcx
-                .provided_trait_methods(trait_id)
-                .map(|assoc| (assoc.def_id, assoc))
+            let trait_item_ids: DefIdSet = items
+                .iter()
+                .filter_map(|impl_item| impl_item.trait_item_def_id)
                 .collect();
 
-            for impl_item in *items {
-                if let Some(def_id) = impl_item.trait_item_def_id {
-                    provided.remove(&def_id);
-                }
+            for assoc in cx
+                .tcx
+                .provided_trait_methods(trait_id)
+                .filter(|assoc| !trait_item_ids.contains(&assoc.def_id))
+            {
+                span_lint_and_then(
+                    cx,
+                    MISSING_TRAIT_METHODS,
+                    cx.tcx.def_span(item.owner_id),
+                    format!("missing trait method provided by default: `{}`", assoc.name),
+                    |diag| {
+                        diag.span_help(cx.tcx.def_span(assoc.def_id), "implement the method");
+                    },
+                );
             }
-
-            cx.tcx.with_stable_hashing_context(|hcx| {
-                for assoc in provided.values_sorted(&hcx, true) {
-                    let source_map = cx.tcx.sess.source_map();
-                    let definition_span = source_map.guess_head_span(cx.tcx.def_span(assoc.def_id));
-
-                    span_lint_and_help(
-                        cx,
-                        MISSING_TRAIT_METHODS,
-                        source_map.guess_head_span(item.span),
-                        format!("missing trait method provided by default: `{}`", assoc.name),
-                        Some(definition_span),
-                        "implement the method",
-                    );
-                }
-            });
         }
     }
 }
diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs
index 9c5a8a0cfcdfa..6964d8c8dbb33 100644
--- a/clippy_lints/src/mixed_read_write_in_expression.rs
+++ b/clippy_lints/src/mixed_read_write_in_expression.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::{get_parent_expr, path_to_local, path_to_local_id};
 use rustc_hir::intravisit::{walk_expr, Visitor};
 use rustc_hir::{BinOpKind, Block, Expr, ExprKind, HirId, LetStmt, Node, Stmt, StmtKind};
@@ -324,13 +324,17 @@ impl<'a, 'tcx> Visitor<'tcx> for ReadVisitor<'a, 'tcx> {
         if path_to_local_id(expr, self.var) {
             // Check that this is a read, not a write.
             if !is_in_assignment_position(self.cx, expr) {
-                span_lint_and_note(
+                span_lint_and_then(
                     self.cx,
                     MIXED_READ_WRITE_IN_EXPRESSION,
                     expr.span,
                     format!("unsequenced read of `{}`", self.cx.tcx.hir().name(self.var)),
-                    Some(self.write_expr.span),
-                    "whether read occurs before this write depends on evaluation order",
+                    |diag| {
+                        diag.span_note(
+                            self.write_expr.span,
+                            "whether read occurs before this write depends on evaluation order",
+                        );
+                    },
                 );
             }
         }
diff --git a/clippy_lints/src/module_style.rs b/clippy_lints/src/module_style.rs
index 305499f9da43c..e9c5f64a2550d 100644
--- a/clippy_lints/src/module_style.rs
+++ b/clippy_lints/src/module_style.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_ast::ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_lint::{EarlyContext, EarlyLintPass, Level, LintContext};
@@ -121,17 +121,18 @@ impl EarlyLintPass for ModStyle {
         for folder in &folder_segments {
             if !mod_folders.contains(folder) {
                 if let Some((file, path)) = file_map.get(folder) {
-                    let mut correct = path.to_path_buf();
-                    correct.pop();
-                    correct.push(folder);
-                    correct.push("mod.rs");
-                    span_lint_and_help(
+                    span_lint_and_then(
                         cx,
                         SELF_NAMED_MODULE_FILES,
                         Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None),
                         format!("`mod.rs` files are required, found `{}`", path.display()),
-                        None,
-                        format!("move `{}` to `{}`", path.display(), correct.display(),),
+                        |diag| {
+                            let mut correct = path.to_path_buf();
+                            correct.pop();
+                            correct.push(folder);
+                            correct.push("mod.rs");
+                            diag.help(format!("move `{}` to `{}`", path.display(), correct.display(),));
+                        },
                     );
                 }
             }
@@ -161,17 +162,18 @@ fn process_paths_for_mod_files<'a>(
 /// for code-sharing between tests.
 fn check_self_named_mod_exists(cx: &EarlyContext<'_>, path: &Path, file: &SourceFile) {
     if path.ends_with("mod.rs") && !path.starts_with("tests") {
-        let mut mod_file = path.to_path_buf();
-        mod_file.pop();
-        mod_file.set_extension("rs");
-
-        span_lint_and_help(
+        span_lint_and_then(
             cx,
             MOD_MODULE_FILES,
             Span::new(file.start_pos, file.start_pos, SyntaxContext::root(), None),
             format!("`mod.rs` files are not allowed, found `{}`", path.display()),
-            None,
-            format!("move `{}` to `{}`", path.display(), mod_file.display()),
+            |diag| {
+                let mut mod_file = path.to_path_buf();
+                mod_file.pop();
+                mod_file.set_extension("rs");
+
+                diag.help(format!("move `{}` to `{}`", path.display(), mod_file.display()));
+            },
         );
     }
 }
diff --git a/clippy_lints/src/needless_borrows_for_generic_args.rs b/clippy_lints/src/needless_borrows_for_generic_args.rs
index 67f9b52c352e6..32e7fde03b2c8 100644
--- a/clippy_lints/src/needless_borrows_for_generic_args.rs
+++ b/clippy_lints/src/needless_borrows_for_generic_args.rs
@@ -1,7 +1,7 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::mir::PossibleBorrowerMap;
+use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap};
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::{implements_trait, is_copy};
 use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode};
@@ -12,6 +12,7 @@ use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath};
 use rustc_index::bit_set::BitSet;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::mir::{Rvalue, StatementKind};
 use rustc_middle::ty::{
     self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, ParamTy, ProjectionPredicate, Ty,
 };
@@ -107,6 +108,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
             }
             && let count = needless_borrow_count(
                 cx,
+                &mut self.possible_borrowers,
                 fn_id,
                 cx.typeck_results().node_args(hir_id),
                 i,
@@ -155,9 +157,14 @@ fn path_has_args(p: &QPath<'_>) -> bool {
 /// The following constraints will be checked:
 /// * The borrowed expression meets all the generic type's constraints.
 /// * The generic type appears only once in the functions signature.
-/// * The borrowed value is Copy itself OR not a variable (created by a function call)
+/// * The borrowed value is:
+///   - `Copy` itself, or
+///   - the only use of a mutable reference, or
+///   - not a variable (created by a function call)
+#[expect(clippy::too_many_arguments)]
 fn needless_borrow_count<'tcx>(
     cx: &LateContext<'tcx>,
+    possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
     fn_id: DefId,
     callee_args: ty::GenericArgsRef<'tcx>,
     arg_index: usize,
@@ -232,9 +239,9 @@ fn needless_borrow_count<'tcx>(
 
         let referent_ty = cx.typeck_results().expr_ty(referent);
 
-        if (!is_copy(cx, referent_ty) && !referent_ty.is_ref())
-            && let ExprKind::AddrOf(_, _, inner) = reference.kind
-            && !matches!(inner.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
+        if !(is_copy(cx, referent_ty)
+            || referent_ty.is_ref() && referent_used_exactly_once(cx, possible_borrowers, reference)
+            || matches!(referent.kind, ExprKind::Call(..) | ExprKind::MethodCall(..)))
         {
             return false;
         }
@@ -337,6 +344,37 @@ fn is_mixed_projection_predicate<'tcx>(
     }
 }
 
+fn referent_used_exactly_once<'tcx>(
+    cx: &LateContext<'tcx>,
+    possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
+    reference: &Expr<'tcx>,
+) -> bool {
+    if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id)
+        && let Some(local) = expr_local(cx.tcx, reference)
+        && let [location] = *local_assignments(mir, local).as_slice()
+        && let block_data = &mir.basic_blocks[location.block]
+        && let Some(statement) = block_data.statements.get(location.statement_index)
+        && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind
+        && !place.is_indirect_first_projection()
+    {
+        let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id);
+        if possible_borrowers
+            .last()
+            .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id)
+        {
+            possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir)));
+        }
+        let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1;
+        // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
+        // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
+        // itself. See the comment in that method for an explanation as to why.
+        possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location)
+            && used_exactly_once(mir, place.local).unwrap_or(false)
+    } else {
+        false
+    }
+}
+
 // Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting
 // projected type that is a type parameter. Returns `false` if replacing the types would have an
 // effect on the function signature beyond substituting `new_ty` for `param_ty`.
diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs
index f2e00cef7e9ff..a0bbf6b14b217 100644
--- a/clippy_lints/src/needless_pass_by_value.rs
+++ b/clippy_lints/src/needless_pass_by_value.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_self;
 use clippy_utils::ptr::get_spans;
 use clippy_utils::source::{snippet, snippet_opt};
@@ -278,9 +278,12 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
                         }
                     }
 
-                    let spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];
-
-                    multispan_sugg(diag, "consider taking a reference instead", spans);
+                    diag.span_suggestion(
+                        input.span,
+                        "consider taking a reference instead",
+                        format!("&{}", snippet(cx, input.span, "_")),
+                        Applicability::MaybeIncorrect,
+                    );
                 };
 
                 span_lint_and_then(
diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs
index 9d326c06eff65..8232e69db3917 100644
--- a/clippy_lints/src/no_effect.rs
+++ b/clippy_lints/src/no_effect.rs
@@ -273,7 +273,7 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
             }
             let snippet =
                 if let (Some(arr), Some(func)) = (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span)) {
-                    format!("assert!({}.len() > {});", &arr, &func)
+                    format!("assert!({arr}.len() > {func});")
                 } else {
                     return;
                 };
diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs
index 15bb328b4465a..6915cd4061551 100644
--- a/clippy_lints/src/non_copy_const.rs
+++ b/clippy_lints/src/non_copy_const.rs
@@ -6,7 +6,7 @@ use std::ptr;
 
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::in_constant;
+use clippy_utils::is_in_const_context;
 use clippy_utils::macros::macro_backtrace;
 use clippy_utils::ty::{implements_trait, InteriorMut};
 use rustc_hir::def::{DefKind, Res};
@@ -406,7 +406,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if let ExprKind::Path(qpath) = &expr.kind {
             // Only lint if we use the const item inside a function.
-            if in_constant(cx, expr.hir_id) {
+            if is_in_const_context(cx) {
                 return;
             }
 
diff --git a/clippy_lints/src/only_used_in_recursion.rs b/clippy_lints/src/only_used_in_recursion.rs
index 8b8aabe7accc6..aadd729f32a45 100644
--- a/clippy_lints/src/only_used_in_recursion.rs
+++ b/clippy_lints/src/only_used_in_recursion.rs
@@ -243,7 +243,6 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
                 owner_id,
                 ..
             }) => {
-                #[allow(trivial_casts)]
                 if let Node::Item(item) = cx.tcx.parent_hir_node(owner_id.into())
                     && let Some(trait_ref) = cx
                         .tcx
diff --git a/clippy_lints/src/operators/absurd_extreme_comparisons.rs b/clippy_lints/src/operators/absurd_extreme_comparisons.rs
index 9769da6d3e9b1..a0de5ea711ca0 100644
--- a/clippy_lints/src/operators/absurd_extreme_comparisons.rs
+++ b/clippy_lints/src/operators/absurd_extreme_comparisons.rs
@@ -3,7 +3,7 @@ use rustc_lint::LateContext;
 use rustc_middle::ty;
 
 use clippy_utils::comparisons::{normalize_comparison, Rel};
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::is_isize_or_usize;
@@ -121,7 +121,7 @@ fn detect_absurd_comparison<'tcx>(
 fn detect_extreme_expr<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> Option<ExtremeExpr<'tcx>> {
     let ty = cx.typeck_results().expr_ty(expr);
 
-    let cv = constant(cx, cx.typeck_results(), expr)?;
+    let cv = ConstEvalCtxt::new(cx).eval(expr)?;
 
     let which = match (ty.kind(), cv) {
         (&ty::Bool, Constant::Bool(false)) | (&ty::Uint(_), Constant::Int(0)) => ExtremeType::Minimum,
diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs
index a7e381be743d5..bc71a4790b9d4 100644
--- a/clippy_lints/src/operators/arithmetic_side_effects.rs
+++ b/clippy_lints/src/operators/arithmetic_side_effects.rs
@@ -1,6 +1,6 @@
 use super::ARITHMETIC_SIDE_EFFECTS;
 use clippy_config::Conf;
-use clippy_utils::consts::{constant, constant_simple, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{expr_or_init, is_from_proc_macro, is_lint_allowed, peel_hir_expr_refs, peel_hir_expr_unary};
@@ -162,7 +162,7 @@ impl ArithmeticSideEffects {
         {
             return Some(n.get());
         }
-        if let Some(Constant::Int(n)) = constant(cx, cx.typeck_results(), expr) {
+        if let Some(Constant::Int(n)) = ConstEvalCtxt::new(cx).eval(expr) {
             return Some(n);
         }
         None
@@ -200,7 +200,7 @@ impl ArithmeticSideEffects {
         lhs: &'tcx hir::Expr<'_>,
         rhs: &'tcx hir::Expr<'_>,
     ) {
-        if constant_simple(cx, cx.typeck_results(), expr).is_some() {
+        if ConstEvalCtxt::new(cx).eval_simple(expr).is_some() {
             return;
         }
         if !matches!(
@@ -280,7 +280,7 @@ impl ArithmeticSideEffects {
         let Some(arg) = args.first() else {
             return;
         };
-        if constant_simple(cx, cx.typeck_results(), receiver).is_some() {
+        if ConstEvalCtxt::new(cx).eval_simple(receiver).is_some() {
             return;
         }
         let instance_ty = cx.typeck_results().expr_ty(receiver);
@@ -308,7 +308,7 @@ impl ArithmeticSideEffects {
         let hir::UnOp::Neg = un_op else {
             return;
         };
-        if constant(cx, cx.typeck_results(), un_expr).is_some() {
+        if ConstEvalCtxt::new(cx).eval(un_expr).is_some() {
             return;
         }
         let ty = cx.typeck_results().expr_ty(expr).peel_refs();
diff --git a/clippy_lints/src/operators/bit_mask.rs b/clippy_lints/src/operators/bit_mask.rs
index 545e680ce0de5..4414056a467ca 100644
--- a/clippy_lints/src/operators/bit_mask.rs
+++ b/clippy_lints/src/operators/bit_mask.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
@@ -166,7 +166,7 @@ fn check_ineffective_gt(cx: &LateContext<'_>, span: Span, m: u128, c: u128, op:
 }
 
 fn fetch_int_literal(cx: &LateContext<'_>, lit: &Expr<'_>) -> Option<u128> {
-    match constant(cx, cx.typeck_results(), lit)? {
+    match ConstEvalCtxt::new(cx).eval(lit)? {
         Constant::Int(n) => Some(n),
         _ => None,
     }
diff --git a/clippy_lints/src/operators/const_comparisons.rs b/clippy_lints/src/operators/const_comparisons.rs
index 7bf9b8ef866a2..c131752439628 100644
--- a/clippy_lints/src/operators/const_comparisons.rs
+++ b/clippy_lints/src/operators/const_comparisons.rs
@@ -2,7 +2,7 @@
 
 use std::cmp::Ordering;
 
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::layout::HasTyCtxt;
@@ -20,13 +20,14 @@ use super::{IMPOSSIBLE_COMPARISONS, REDUNDANT_COMPARISONS};
 // Flip yoda conditionals, turnings expressions like `42 < x` into `x > 42`
 fn comparison_to_const<'tcx>(
     cx: &LateContext<'tcx>,
-    typeck: &TypeckResults<'tcx>,
+    typeck: &'tcx TypeckResults<'tcx>,
     expr: &'tcx Expr<'tcx>,
 ) -> Option<(CmpOp, &'tcx Expr<'tcx>, &'tcx Expr<'tcx>, Constant<'tcx>, Ty<'tcx>)> {
     if let ExprKind::Binary(operator, left, right) = expr.kind
         && let Ok(cmp_op) = CmpOp::try_from(operator.node)
     {
-        match (constant(cx, typeck, left), constant(cx, typeck, right)) {
+        let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck);
+        match (ecx.eval(left), ecx.eval(right)) {
             (Some(_), Some(_)) => None,
             (_, Some(con)) => Some((cmp_op, left, right, con, typeck.expr_ty(right))),
             (Some(con), _) => Some((cmp_op.reverse(), right, left, con, typeck.expr_ty(left))),
diff --git a/clippy_lints/src/operators/duration_subsec.rs b/clippy_lints/src/operators/duration_subsec.rs
index ca3112ce5c468..e3029f8438e5f 100644
--- a/clippy_lints/src/operators/duration_subsec.rs
+++ b/clippy_lints/src/operators/duration_subsec.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::is_type_diagnostic_item;
@@ -19,7 +19,7 @@ pub(crate) fn check<'tcx>(
     if op == BinOpKind::Div
         && let ExprKind::MethodCall(method_path, self_arg, [], _) = left.kind
         && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration)
-        && let Some(Constant::Int(divisor)) = constant(cx, cx.typeck_results(), right)
+        && let Some(Constant::Int(divisor)) = ConstEvalCtxt::new(cx).eval(right)
     {
         let suggested_fn = match (method_path.ident.as_str(), divisor) {
             ("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis",
diff --git a/clippy_lints/src/operators/erasing_op.rs b/clippy_lints/src/operators/erasing_op.rs
index 066e08f3bd4ca..24bfe2b050bb5 100644
--- a/clippy_lints/src/operators/erasing_op.rs
+++ b/clippy_lints/src/operators/erasing_op.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant_simple, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::ty::same_type_and_consts;
 
@@ -34,12 +34,12 @@ fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>)
 
 fn check_op<'tcx>(
     cx: &LateContext<'tcx>,
-    tck: &TypeckResults<'tcx>,
+    tck: &'tcx TypeckResults<'tcx>,
     op: &Expr<'tcx>,
     other: &Expr<'tcx>,
     parent: &Expr<'tcx>,
 ) {
-    if constant_simple(cx, tck, op) == Some(Constant::Int(0)) {
+    if ConstEvalCtxt::with_env(cx.tcx, cx.param_env, tck).eval_simple(op) == Some(Constant::Int(0)) {
         if different_types(tck, other, parent) {
             return;
         }
diff --git a/clippy_lints/src/operators/float_cmp.rs b/clippy_lints/src/operators/float_cmp.rs
index 0e5b440c50f27..df6e67455965e 100644
--- a/clippy_lints/src/operators/float_cmp.rs
+++ b/clippy_lints/src/operators/float_cmp.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant_with_source, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::get_item_name;
 use clippy_utils::sugg::Sugg;
@@ -17,12 +17,14 @@ pub(crate) fn check<'tcx>(
     right: &'tcx Expr<'_>,
 ) {
     if (op == BinOpKind::Eq || op == BinOpKind::Ne) && is_float(cx, left) {
-        let left_is_local = match constant_with_source(cx, cx.typeck_results(), left) {
+        let typeck = cx.typeck_results();
+        let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck);
+        let left_is_local = match ecx.eval_with_source(left) {
             Some((c, s)) if !is_allowed(&c) => s.is_local(),
             Some(_) => return,
             None => true,
         };
-        let right_is_local = match constant_with_source(cx, cx.typeck_results(), right) {
+        let right_is_local = match ecx.eval_with_source(right) {
             Some((c, s)) if !is_allowed(&c) => s.is_local(),
             Some(_) => return,
             None => true,
diff --git a/clippy_lints/src/operators/identity_op.rs b/clippy_lints/src/operators/identity_op.rs
index 0879dcd9bcdf6..830be50c8ba4f 100644
--- a/clippy_lints/src/operators/identity_op.rs
+++ b/clippy_lints/src/operators/identity_op.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant_full_int, constant_simple, Constant, FullInt};
+use clippy_utils::consts::{ConstEvalCtxt, Constant, FullInt};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::{clip, peel_hir_expr_refs, unsext};
@@ -184,14 +184,13 @@ fn is_allowed(cx: &LateContext<'_>, cmp: BinOpKind, left: &Expr<'_>, right: &Exp
         && cx.typeck_results().expr_ty(right).peel_refs().is_integral()
         // `1 << 0` is a common pattern in bit manipulation code
         && !(cmp == BinOpKind::Shl
-            && constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0))
-            && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1)))
+            && ConstEvalCtxt::new(cx).eval_simple(right) == Some(Constant::Int(0))
+            && ConstEvalCtxt::new(cx).eval_simple(left) == Some(Constant::Int(1)))
 }
 
 fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span: Span, arg: Span) {
-    let lhs_const = constant_full_int(cx, cx.typeck_results(), left);
-    let rhs_const = constant_full_int(cx, cx.typeck_results(), right);
-    if match (lhs_const, rhs_const) {
+    let ecx = ConstEvalCtxt::new(cx);
+    if match (ecx.eval_full_int(left), ecx.eval_full_int(right)) {
         (Some(FullInt::S(lv)), Some(FullInt::S(rv))) => lv.abs() < rv.abs(),
         (Some(FullInt::U(lv)), Some(FullInt::U(rv))) => lv < rv,
         _ => return,
@@ -201,7 +200,7 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span
 }
 
 fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens, is_erased: bool) -> bool {
-    if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) {
+    if let Some(Constant::Int(v)) = ConstEvalCtxt::new(cx).eval_simple(e).map(Constant::peel_refs) {
         let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
             ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
             ty::Uint(uty) => clip(cx.tcx, !0, uty),
diff --git a/clippy_lints/src/operators/integer_division.rs b/clippy_lints/src/operators/integer_division.rs
index 631d10f4a72e9..76eba7327cff6 100644
--- a/clippy_lints/src/operators/integer_division.rs
+++ b/clippy_lints/src/operators/integer_division.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
 
@@ -15,13 +15,9 @@ pub(crate) fn check<'tcx>(
         && cx.typeck_results().expr_ty(left).is_integral()
         && cx.typeck_results().expr_ty(right).is_integral()
     {
-        span_lint_and_help(
-            cx,
-            INTEGER_DIVISION,
-            expr.span,
-            "integer division",
-            None,
-            "division of integers may cause loss of precision. consider using floats",
-        );
+        #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+        span_lint_and_then(cx, INTEGER_DIVISION, expr.span, "integer division", |diag| {
+            diag.help("division of integers may cause loss of precision. consider using floats");
+        });
     }
 }
diff --git a/clippy_lints/src/operators/modulo_arithmetic.rs b/clippy_lints/src/operators/modulo_arithmetic.rs
index d65fffac5a828..c83bdda347a66 100644
--- a/clippy_lints/src/operators/modulo_arithmetic.rs
+++ b/clippy_lints/src/operators/modulo_arithmetic.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::sext;
 use rustc_hir::{BinOpKind, Expr, ExprKind, Node};
@@ -42,15 +42,11 @@ fn used_in_comparison_with_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     };
 
     if op.node == BinOpKind::Eq || op.node == BinOpKind::Ne {
-        if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), rhs) {
-            return true;
-        }
-        if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), lhs) {
-            return true;
-        }
+        let ecx = ConstEvalCtxt::new(cx);
+        matches!(ecx.eval(lhs), Some(Constant::Int(0))) || matches!(ecx.eval(rhs), Some(Constant::Int(0)))
+    } else {
+        false
     }
-
-    false
 }
 
 struct OperandInfo {
@@ -60,7 +56,7 @@ struct OperandInfo {
 }
 
 fn analyze_operand(operand: &Expr<'_>, cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<OperandInfo> {
-    match constant(cx, cx.typeck_results(), operand) {
+    match ConstEvalCtxt::new(cx).eval(operand) {
         Some(Constant::Int(v)) => match *cx.typeck_results().expr_ty(expr).kind() {
             ty::Int(ity) => {
                 let value = sext(cx.tcx, v, ity);
diff --git a/clippy_lints/src/operators/numeric_arithmetic.rs b/clippy_lints/src/operators/numeric_arithmetic.rs
index ea933168cfd5d..565294bb40a82 100644
--- a/clippy_lints/src/operators/numeric_arithmetic.rs
+++ b/clippy_lints/src/operators/numeric_arithmetic.rs
@@ -1,5 +1,5 @@
 use super::FLOAT_ARITHMETIC;
-use clippy_utils::consts::constant_simple;
+use clippy_utils::consts::ConstEvalCtxt;
 use clippy_utils::diagnostics::span_lint;
 use rustc_hir as hir;
 use rustc_lint::LateContext;
@@ -55,7 +55,7 @@ impl Context {
             return;
         }
         let ty = cx.typeck_results().expr_ty(arg);
-        if constant_simple(cx, cx.typeck_results(), expr).is_none() && ty.is_floating_point() {
+        if ConstEvalCtxt::new(cx).eval_simple(expr).is_none() && ty.is_floating_point() {
             span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
             self.expr_id = Some(expr.hir_id);
         }
diff --git a/clippy_lints/src/operators/op_ref.rs b/clippy_lints/src/operators/op_ref.rs
index c2b27c9b2298a..82b9d10fbeb1d 100644
--- a/clippy_lints/src/operators/op_ref.rs
+++ b/clippy_lints/src/operators/op_ref.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::get_enclosing_block;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::{implements_trait, is_copy};
@@ -64,10 +64,10 @@ pub(crate) fn check<'tcx>(
                         |diag| {
                             let lsnip = snippet(cx, l.span, "...").to_string();
                             let rsnip = snippet(cx, r.span, "...").to_string();
-                            multispan_sugg(
-                                diag,
+                            diag.multipart_suggestion(
                                 "use the values directly",
                                 vec![(left.span, lsnip), (right.span, rsnip)],
+                                Applicability::MachineApplicable,
                             );
                         },
                     );
diff --git a/clippy_lints/src/option_if_let_else.rs b/clippy_lints/src/option_if_let_else.rs
index d4906328ccb93..ae02d22512e31 100644
--- a/clippy_lints/src/option_if_let_else.rs
+++ b/clippy_lints/src/option_if_let_else.rs
@@ -1,8 +1,8 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{
-    can_move_expr_to_closure, eager_or_lazy, higher, in_constant, is_else_clause, is_res_lang_ctor, peel_blocks,
-    peel_hir_expr_while, CaptureKind,
+    can_move_expr_to_closure, eager_or_lazy, higher, is_else_clause, is_in_const_context, is_res_lang_ctor,
+    peel_blocks, peel_hir_expr_while, CaptureKind,
 };
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
@@ -294,7 +294,7 @@ fn is_none_or_err_arm(cx: &LateContext<'_>, arm: &Arm<'_>) -> bool {
 impl<'tcx> LateLintPass<'tcx> for OptionIfLetElse {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
         // Don't lint macros and constants
-        if expr.span.from_expansion() || in_constant(cx, expr.hir_id) {
+        if expr.span.from_expansion() || is_in_const_context(cx) {
             return;
         }
 
diff --git a/clippy_lints/src/partial_pub_fields.rs b/clippy_lints/src/partial_pub_fields.rs
index 2d20cbea698f2..267e2067e101a 100644
--- a/clippy_lints/src/partial_pub_fields.rs
+++ b/clippy_lints/src/partial_pub_fields.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_ast::ast::{Item, ItemKind};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::declare_lint_pass;
@@ -57,24 +57,16 @@ impl EarlyLintPass for PartialPubFields {
 
         for field in fields {
             if all_priv && field.vis.kind.is_pub() {
-                span_lint_and_help(
-                    cx,
-                    PARTIAL_PUB_FIELDS,
-                    field.vis.span,
-                    msg,
-                    None,
-                    "consider using private field here",
-                );
+                #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+                span_lint_and_then(cx, PARTIAL_PUB_FIELDS, field.vis.span, msg, |diag| {
+                    diag.help("consider using private field here");
+                });
                 return;
             } else if all_pub && !field.vis.kind.is_pub() {
-                span_lint_and_help(
-                    cx,
-                    PARTIAL_PUB_FIELDS,
-                    field.vis.span,
-                    msg,
-                    None,
-                    "consider using public field here",
-                );
+                #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+                span_lint_and_then(cx, PARTIAL_PUB_FIELDS, field.vis.span, msg, |diag| {
+                    diag.help("consider using public field here");
+                });
                 return;
             }
         }
diff --git a/clippy_lints/src/pathbuf_init_then_push.rs b/clippy_lints/src/pathbuf_init_then_push.rs
index 0008f154ae3a8..18bfb588a1187 100644
--- a/clippy_lints/src/pathbuf_init_then_push.rs
+++ b/clippy_lints/src/pathbuf_init_then_push.rs
@@ -21,8 +21,8 @@ declare_clippy_lint! {
     /// `PathBuf::from` instead.
     ///
     /// ### Known problems
-    /// `.join()` introduces an implicit `clone()`. `PathBuf::from` can alternativly be
-    /// used when the `PathBuf` is newly constructed. This will avoild the implicit clone.
+    /// `.join()` introduces an implicit `clone()`. `PathBuf::from` can alternatively be
+    /// used when the `PathBuf` is newly constructed. This will avoid the implicit clone.
     ///
     /// ### Example
     /// ```rust
diff --git a/clippy_lints/src/pattern_type_mismatch.rs b/clippy_lints/src/pattern_type_mismatch.rs
index 9661a57b8b95c..c1296b04387a0 100644
--- a/clippy_lints/src/pattern_type_mismatch.rs
+++ b/clippy_lints/src/pattern_type_mismatch.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_hir::{
     intravisit, Body, Expr, ExprKind, FnDecl, LetExpr, LocalSource, Mutability, Pat, PatKind, Stmt, StmtKind,
 };
@@ -133,23 +133,25 @@ enum DerefPossible {
 fn apply_lint(cx: &LateContext<'_>, pat: &Pat<'_>, deref_possible: DerefPossible) -> bool {
     let maybe_mismatch = find_first_mismatch(cx, pat);
     if let Some((span, mutability, level)) = maybe_mismatch {
-        span_lint_and_help(
+        #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+        span_lint_and_then(
             cx,
             PATTERN_TYPE_MISMATCH,
             span,
             "type of pattern does not match the expression type",
-            None,
-            format!(
-                "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings",
-                match (deref_possible, level) {
-                    (DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ",
-                    _ => "",
-                },
-                match mutability {
-                    Mutability::Mut => "&mut _",
-                    Mutability::Not => "&_",
-                },
-            ),
+            |diag| {
+                diag.help(format!(
+                    "{}explicitly match against a `{}` pattern and adjust the enclosed variable bindings",
+                    match (deref_possible, level) {
+                        (DerefPossible::Possible, Level::Top) => "use `*` to dereference the match expression or ",
+                        _ => "",
+                    },
+                    match mutability {
+                        Mutability::Mut => "&mut _",
+                        Mutability::Not => "&_",
+                    },
+                ));
+            },
         );
         true
     } else {
diff --git a/clippy_lints/src/pub_use.rs b/clippy_lints/src/pub_use.rs
index ab8f8a1689dc3..5b973a79eae36 100644
--- a/clippy_lints/src/pub_use.rs
+++ b/clippy_lints/src/pub_use.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_ast::ast::{Item, ItemKind, VisibilityKind};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::declare_lint_pass;
@@ -42,14 +42,10 @@ impl EarlyLintPass for PubUse {
         if let ItemKind::Use(_) = item.kind
             && let VisibilityKind::Public = item.vis.kind
         {
-            span_lint_and_help(
-                cx,
-                PUB_USE,
-                item.span,
-                "using `pub use`",
-                None,
-                "move the exported item to a public module instead",
-            );
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(cx, PUB_USE, item.span, "using `pub use`", |diag| {
+                diag.help("move the exported item to a public module instead");
+            });
         }
     }
 }
diff --git a/clippy_lints/src/question_mark.rs b/clippy_lints/src/question_mark.rs
index ef6b4d3aeab27..e1e3ded2c795d 100644
--- a/clippy_lints/src/question_mark.rs
+++ b/clippy_lints/src/question_mark.rs
@@ -7,7 +7,7 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 use clippy_utils::{
-    eq_expr_value, higher, in_constant, is_else_clause, is_lint_allowed, is_path_lang_item, is_res_lang_ctor,
+    eq_expr_value, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor,
     pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt,
     span_contains_comment,
 };
@@ -346,15 +346,13 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark {
             return;
         }
 
-        if !self.inside_try_block() && !in_constant(cx, stmt.hir_id) {
+        if !self.inside_try_block() && !is_in_const_context(cx) {
             check_let_some_else_return_none(cx, stmt);
         }
         self.check_manual_let_else(cx, stmt);
     }
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !self.inside_try_block()
-            && !in_constant(cx, expr.hir_id)
-            && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id)
+        if !self.inside_try_block() && !is_in_const_context(cx) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id)
         {
             check_is_none_or_err_and_early_return(cx, expr);
             check_if_let_some_or_err_and_early_return(cx, expr);
diff --git a/clippy_lints/src/question_mark_used.rs b/clippy_lints/src/question_mark_used.rs
index f5e6cb804da17..0a974bf9d2f71 100644
--- a/clippy_lints/src/question_mark_used.rs
+++ b/clippy_lints/src/question_mark_used.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 
 use clippy_utils::macros::span_is_local;
 use rustc_hir::{Expr, ExprKind, MatchSource};
@@ -39,13 +39,15 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMarkUsed {
                 return;
             }
 
-            span_lint_and_help(
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(
                 cx,
                 QUESTION_MARK_USED,
                 expr.span,
                 "question mark operator was used",
-                None,
-                "consider using a custom macro or match expression",
+                |diag| {
+                    diag.help("consider using a custom macro or match expression");
+                },
             );
         }
     }
diff --git a/clippy_lints/src/ranges.rs b/clippy_lints/src/ranges.rs
index 829fb58bc652b..81189fe517c00 100644
--- a/clippy_lints/src/ranges.rs
+++ b/clippy_lints/src/ranges.rs
@@ -1,10 +1,10 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, snippet_with_applicability, SpanRangeExt};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{get_parent_expr, higher, in_constant, is_integer_const, path_to_local};
+use clippy_utils::{get_parent_expr, higher, is_in_const_context, is_integer_const, path_to_local};
 use rustc_ast::ast::RangeLimits;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, HirId};
@@ -202,7 +202,7 @@ fn check_possible_range_contains(
     expr: &Expr<'_>,
     span: Span,
 ) {
-    if in_constant(cx, expr.hir_id) {
+    if is_in_const_context(cx) {
         return;
     }
 
@@ -319,7 +319,7 @@ fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) ->
             _ => return None,
         };
         if let Some(id) = path_to_local(l) {
-            if let Some(c) = constant(cx, cx.typeck_results(), r) {
+            if let Some(c) = ConstEvalCtxt::new(cx).eval(r) {
                 return Some(RangeBounds {
                     val: c,
                     expr: r,
@@ -331,7 +331,7 @@ fn check_range_bounds<'a, 'tcx>(cx: &'a LateContext<'tcx>, ex: &'a Expr<'_>) ->
                 });
             }
         } else if let Some(id) = path_to_local(r) {
-            if let Some(c) = constant(cx, cx.typeck_results(), l) {
+            if let Some(c) = ConstEvalCtxt::new(cx).eval(l) {
                 return Some(RangeBounds {
                     val: c,
                     expr: l,
@@ -451,8 +451,9 @@ fn check_reversed_empty_range(cx: &LateContext<'_>, expr: &Expr<'_>) {
     }) = higher::Range::hir(expr)
         && let ty = cx.typeck_results().expr_ty(start)
         && let ty::Int(_) | ty::Uint(_) = ty.kind()
-        && let Some(start_idx) = constant(cx, cx.typeck_results(), start)
-        && let Some(end_idx) = constant(cx, cx.typeck_results(), end)
+        && let ecx = ConstEvalCtxt::new(cx)
+        && let Some(start_idx) = ecx.eval(start)
+        && let Some(end_idx) = ecx.eval(end)
         && let Some(ordering) = Constant::partial_cmp(cx.tcx, ty, &start_idx, &end_idx)
         && is_empty_range(limits, ordering)
     {
diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs
index 3416a93e3c4ac..a1231c082e68a 100644
--- a/clippy_lints/src/redundant_clone.rs
+++ b/clippy_lints/src/redundant_clone.rs
@@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
             let (fn_def_id, arg, arg_ty, clone_ret) =
                 unwrap_or_continue!(is_call_with_ref_arg(cx, mir, &terminator.kind));
 
-            let from_borrow = match_def_path(cx, fn_def_id, &paths::CLONE_TRAIT_METHOD)
+            let from_borrow = cx.tcx.lang_items().get(LangItem::CloneFn) == Some(fn_def_id)
                 || cx.tcx.is_diagnostic_item(sym::to_owned_method, fn_def_id)
                 || (cx.tcx.is_diagnostic_item(sym::to_string_method, fn_def_id)
                     && is_type_lang_item(cx, arg_ty, LangItem::String));
diff --git a/clippy_lints/src/redundant_slicing.rs b/clippy_lints/src/redundant_slicing.rs
index 82f22ad693d78..1c10e84d3caf6 100644
--- a/clippy_lints/src/redundant_slicing.rs
+++ b/clippy_lints/src/redundant_slicing.rs
@@ -1,7 +1,7 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::get_parent_expr;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_with_context;
-use clippy_utils::ty::{is_type_lang_item, peel_mid_ty_refs};
+use clippy_utils::ty::is_type_lang_item;
+use clippy_utils::{get_parent_expr, peel_middle_ty_refs};
 use rustc_ast::util::parser::PREC_PREFIX;
 use rustc_errors::Applicability;
 use rustc_hir::{BorrowKind, Expr, ExprKind, LangItem, Mutability};
@@ -82,13 +82,12 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
             && let ExprKind::Index(indexed, range, _) = addressee.kind
             && is_type_lang_item(cx, cx.typeck_results().expr_ty_adjusted(range), LangItem::RangeFull)
         {
-            let (expr_ty, expr_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(expr));
-            let (indexed_ty, indexed_ref_count) = peel_mid_ty_refs(cx.typeck_results().expr_ty(indexed));
+            let (expr_ty, expr_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(expr));
+            let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed));
             let parent_expr = get_parent_expr(cx, expr);
             let needs_parens_for_prefix = parent_expr.map_or(false, |parent| parent.precedence().order() > PREC_PREFIX);
-            let mut app = Applicability::MachineApplicable;
 
-            let ((lint, msg), help, sugg) = if expr_ty == indexed_ty {
+            if expr_ty == indexed_ty {
                 if expr_ref_count > indexed_ref_count {
                     // Indexing takes self by reference and can't return a reference to that
                     // reference as it's a local variable. The only way this could happen is if
@@ -99,7 +98,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
                 }
                 let deref_count = indexed_ref_count - expr_ref_count;
 
-                let (lint, reborrow_str, help_str) = if mutability == Mutability::Mut {
+                let ((lint, msg), reborrow_str, help_msg) = if mutability == Mutability::Mut {
                     // The slice was used to reborrow the mutable reference.
                     (DEREF_BY_SLICING_LINT, "&mut *", "reborrow the original value instead")
                 } else if matches!(
@@ -113,8 +112,11 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
                         a.kind,
                         Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. }))
                     )
-                }) {
-                    // The slice was used to make a temporary reference.
+                }) || (matches!(
+                    cx.typeck_results().expr_ty(indexed).ref_mutability(),
+                    Some(Mutability::Mut)
+                ) && mutability == Mutability::Not)
+                {
                     (DEREF_BY_SLICING_LINT, "&*", "reborrow the original value instead")
                 } else if deref_count != 0 {
                     (DEREF_BY_SLICING_LINT, "", "dereference the original value instead")
@@ -122,38 +124,36 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
                     (REDUNDANT_SLICING_LINT, "", "use the original value instead")
                 };
 
-                let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
-                let sugg = if (deref_count != 0 || !reborrow_str.is_empty()) && needs_parens_for_prefix {
-                    format!("({reborrow_str}{}{snip})", "*".repeat(deref_count))
-                } else {
-                    format!("{reborrow_str}{}{snip}", "*".repeat(deref_count))
-                };
-
-                (lint, help_str, sugg)
+                span_lint_and_then(cx, lint, expr.span, msg, |diag| {
+                    let mut app = Applicability::MachineApplicable;
+                    let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
+                    let sugg = if (deref_count != 0 || !reborrow_str.is_empty()) && needs_parens_for_prefix {
+                        format!("({reborrow_str}{}{snip})", "*".repeat(deref_count))
+                    } else {
+                        format!("{reborrow_str}{}{snip}", "*".repeat(deref_count))
+                    };
+                    diag.span_suggestion(expr.span, help_msg, sugg, app);
+                });
             } else if let Some(target_id) = cx.tcx.lang_items().deref_target() {
                 if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions(
                     cx.param_env,
                     Ty::new_projection_from_args(cx.tcx, target_id, cx.tcx.mk_args(&[GenericArg::from(indexed_ty)])),
                 ) {
                     if deref_ty == expr_ty {
-                        let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
-                        let sugg = if needs_parens_for_prefix {
-                            format!("(&{}{}*{snip})", mutability.prefix_str(), "*".repeat(indexed_ref_count))
-                        } else {
-                            format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count))
-                        };
-                        (DEREF_BY_SLICING_LINT, "dereference the original value instead", sugg)
-                    } else {
-                        return;
+                        let (lint, msg) = DEREF_BY_SLICING_LINT;
+                        span_lint_and_then(cx, lint, expr.span, msg, |diag| {
+                            let mut app = Applicability::MachineApplicable;
+                            let snip = snippet_with_context(cx, indexed.span, ctxt, "..", &mut app).0;
+                            let sugg = if needs_parens_for_prefix {
+                                format!("(&{}{}*{snip})", mutability.prefix_str(), "*".repeat(indexed_ref_count))
+                            } else {
+                                format!("&{}{}*{snip}", mutability.prefix_str(), "*".repeat(indexed_ref_count))
+                            };
+                            diag.span_suggestion(expr.span, "dereference the original value instead", sugg, app);
+                        });
                     }
-                } else {
-                    return;
                 }
-            } else {
-                return;
-            };
-
-            span_lint_and_sugg(cx, lint, expr.span, msg, help, sugg, app);
+            }
         }
     }
 }
diff --git a/clippy_lints/src/ref_patterns.rs b/clippy_lints/src/ref_patterns.rs
index 467038523b496..002c6c41d52ba 100644
--- a/clippy_lints/src/ref_patterns.rs
+++ b/clippy_lints/src/ref_patterns.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_ast::ast::{BindingMode, Pat, PatKind};
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::declare_lint_pass;
@@ -33,14 +33,10 @@ impl EarlyLintPass for RefPatterns {
         if let PatKind::Ident(BindingMode::REF, _, _) = pat.kind
             && !pat.span.from_expansion()
         {
-            span_lint_and_help(
-                cx,
-                REF_PATTERNS,
-                pat.span,
-                "usage of ref pattern",
-                None,
-                "consider using `&` for clarity instead",
-            );
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(cx, REF_PATTERNS, pat.span, "usage of ref pattern", |diag| {
+                diag.help("consider using `&` for clarity instead");
+            });
         }
     }
 }
diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs
index 8cacb646f5148..95014b230431c 100644
--- a/clippy_lints/src/regex.rs
+++ b/clippy_lints/src/regex.rs
@@ -1,6 +1,6 @@
 use std::fmt::Display;
 
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::{def_path_def_ids, path_def_id, paths};
@@ -148,7 +148,7 @@ fn lint_syntax_error(cx: &LateContext<'_>, error: &regex_syntax::Error, unescape
 }
 
 fn const_str<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<String> {
-    constant(cx, cx.typeck_results(), e).and_then(|c| match c {
+    ConstEvalCtxt::new(cx).eval(e).and_then(|c| match c {
         Constant::Str(s) => Some(s),
         _ => None,
     })
diff --git a/clippy_lints/src/renamed_lints.rs b/clippy_lints/src/renamed_lints.rs
deleted file mode 100644
index 8e999f3e89af3..0000000000000
--- a/clippy_lints/src/renamed_lints.rs
+++ /dev/null
@@ -1,65 +0,0 @@
-// This file is managed by `cargo dev rename_lint`. Prefer using that when possible.
-
-#[rustfmt::skip]
-pub static RENAMED_LINTS: &[(&str, &str)] = &[
-    ("clippy::almost_complete_letter_range", "clippy::almost_complete_range"),
-    ("clippy::blacklisted_name", "clippy::disallowed_names"),
-    ("clippy::block_in_if_condition_expr", "clippy::blocks_in_conditions"),
-    ("clippy::block_in_if_condition_stmt", "clippy::blocks_in_conditions"),
-    ("clippy::blocks_in_if_conditions", "clippy::blocks_in_conditions"),
-    ("clippy::box_vec", "clippy::box_collection"),
-    ("clippy::const_static_lifetime", "clippy::redundant_static_lifetimes"),
-    ("clippy::cyclomatic_complexity", "clippy::cognitive_complexity"),
-    ("clippy::derive_hash_xor_eq", "clippy::derived_hash_with_manual_eq"),
-    ("clippy::disallowed_method", "clippy::disallowed_methods"),
-    ("clippy::disallowed_type", "clippy::disallowed_types"),
-    ("clippy::eval_order_dependence", "clippy::mixed_read_write_in_expression"),
-    ("clippy::identity_conversion", "clippy::useless_conversion"),
-    ("clippy::if_let_some_result", "clippy::match_result_ok"),
-    ("clippy::incorrect_clone_impl_on_copy_type", "clippy::non_canonical_clone_impl"),
-    ("clippy::incorrect_partial_ord_impl_on_ord_type", "clippy::non_canonical_partial_ord_impl"),
-    ("clippy::integer_arithmetic", "clippy::arithmetic_side_effects"),
-    ("clippy::logic_bug", "clippy::overly_complex_bool_expr"),
-    ("clippy::new_without_default_derive", "clippy::new_without_default"),
-    ("clippy::option_and_then_some", "clippy::bind_instead_of_map"),
-    ("clippy::option_expect_used", "clippy::expect_used"),
-    ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"),
-    ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"),
-    ("clippy::option_unwrap_used", "clippy::unwrap_used"),
-    ("clippy::overflow_check_conditional", "clippy::panicking_overflow_checks"),
-    ("clippy::ref_in_deref", "clippy::needless_borrow"),
-    ("clippy::result_expect_used", "clippy::expect_used"),
-    ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"),
-    ("clippy::result_unwrap_used", "clippy::unwrap_used"),
-    ("clippy::single_char_push_str", "clippy::single_char_add_str"),
-    ("clippy::stutter", "clippy::module_name_repetitions"),
-    ("clippy::thread_local_initializer_can_be_made_const", "clippy::missing_const_for_thread_local"),
-    ("clippy::to_string_in_display", "clippy::recursive_format_impl"),
-    ("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"),
-    ("clippy::zero_width_space", "clippy::invisible_characters"),
-    ("clippy::cast_ref_to_mut", "invalid_reference_casting"),
-    ("clippy::clone_double_ref", "suspicious_double_ref_op"),
-    ("clippy::cmp_nan", "invalid_nan_comparisons"),
-    ("clippy::drop_bounds", "drop_bounds"),
-    ("clippy::drop_copy", "dropping_copy_types"),
-    ("clippy::drop_ref", "dropping_references"),
-    ("clippy::fn_null_check", "useless_ptr_null_checks"),
-    ("clippy::for_loop_over_option", "for_loops_over_fallibles"),
-    ("clippy::for_loop_over_result", "for_loops_over_fallibles"),
-    ("clippy::for_loops_over_fallibles", "for_loops_over_fallibles"),
-    ("clippy::forget_copy", "forgetting_copy_types"),
-    ("clippy::forget_ref", "forgetting_references"),
-    ("clippy::into_iter_on_array", "array_into_iter"),
-    ("clippy::invalid_atomic_ordering", "invalid_atomic_ordering"),
-    ("clippy::invalid_ref", "invalid_value"),
-    ("clippy::invalid_utf8_in_unchecked", "invalid_from_utf8_unchecked"),
-    ("clippy::let_underscore_drop", "let_underscore_drop"),
-    ("clippy::mem_discriminant_non_enum", "enum_intrinsics_non_enums"),
-    ("clippy::panic_params", "non_fmt_panics"),
-    ("clippy::positional_named_format_parameters", "named_arguments_used_positionally"),
-    ("clippy::temporary_cstring_as_ptr", "temporary_cstring_as_ptr"),
-    ("clippy::undropped_manually_drops", "undropped_manually_drops"),
-    ("clippy::unknown_clippy_lints", "unknown_lints"),
-    ("clippy::unused_label", "unused_labels"),
-    ("clippy::vtable_address_comparisons", "ambiguous_wide_pointer_comparisons"),
-];
diff --git a/clippy_lints/src/repeat_vec_with_capacity.rs b/clippy_lints/src/repeat_vec_with_capacity.rs
index 792d8fc88f0bb..678681ea4252d 100644
--- a/clippy_lints/src/repeat_vec_with_capacity.rs
+++ b/clippy_lints/src/repeat_vec_with_capacity.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::VecArgs;
 use clippy_utils::macros::matching_root_macro_call;
@@ -69,7 +69,7 @@ fn check_vec_macro(cx: &LateContext<'_>, expr: &Expr<'_>) {
         && let Some(VecArgs::Repeat(repeat_expr, len_expr)) = VecArgs::hir(cx, expr)
         && fn_def_id(cx, repeat_expr).is_some_and(|did| match_def_path(cx, did, &paths::VEC_WITH_CAPACITY))
         && !len_expr.span.from_expansion()
-        && let Some(Constant::Int(2..)) = constant(cx, cx.typeck_results(), expr_or_init(cx, len_expr))
+        && let Some(Constant::Int(2..)) = ConstEvalCtxt::new(cx).eval(expr_or_init(cx, len_expr))
     {
         emit_lint(
             cx,
diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs
index 8ced47b48a43d..13016cdadb072 100644
--- a/clippy_lints/src/returns.rs
+++ b/clippy_lints/src/returns.rs
@@ -395,7 +395,7 @@ fn check_final_expr<'tcx>(
             // Returns may be used to turn an expression into a statement in rustc's AST.
             // This allows the addition of attributes, like `#[allow]` (See: clippy#9361)
             // `#[expect(clippy::needless_return)]` needs to be handled separatly to
-            // actually fullfil the expectation (clippy::#12998)
+            // actually fulfill the expectation (clippy::#12998)
             match cx.tcx.hir().attrs(expr.hir_id) {
                 [] => {},
                 [attr] => {
diff --git a/clippy_lints/src/semicolon_block.rs b/clippy_lints/src/semicolon_block.rs
index 7615c21276d69..09f1c11235291 100644
--- a/clippy_lints/src/semicolon_block.rs
+++ b/clippy_lints/src/semicolon_block.rs
@@ -1,5 +1,5 @@
 use clippy_config::Conf;
-use clippy_utils::diagnostics::{multispan_sugg_with_applicability, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_errors::Applicability;
 use rustc_hir::{Block, Expr, ExprKind, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -92,11 +92,10 @@ impl SemicolonBlock {
             semi_span,
             "consider moving the `;` inside the block for consistent formatting",
             |diag| {
-                multispan_sugg_with_applicability(
-                    diag,
+                diag.multipart_suggestion(
                     "put the `;` here",
+                    vec![(remove_span, String::new()), (insert_span, ";".to_owned())],
                     Applicability::MachineApplicable,
-                    [(remove_span, String::new()), (insert_span, ";".to_owned())],
                 );
             },
         );
@@ -124,11 +123,10 @@ impl SemicolonBlock {
             block.span,
             "consider moving the `;` outside the block for consistent formatting",
             |diag| {
-                multispan_sugg_with_applicability(
-                    diag,
+                diag.multipart_suggestion(
                     "put the `;` here",
+                    vec![(remove_span, String::new()), (insert_span, ";".to_owned())],
                     Applicability::MachineApplicable,
-                    [(remove_span, String::new()), (insert_span, ";".to_owned())],
                 );
             },
         );
diff --git a/clippy_lints/src/set_contains_or_insert.rs b/clippy_lints/src/set_contains_or_insert.rs
index 5e65b9fa5171d..e6fe76493974a 100644
--- a/clippy_lints/src/set_contains_or_insert.rs
+++ b/clippy_lints/src/set_contains_or_insert.rs
@@ -12,8 +12,8 @@ use rustc_span::{sym, Span};
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for usage of `contains` to see if a value is not
-    /// present on `HashSet` followed by a `insert`.
+    /// Checks for usage of `contains` to see if a value is not present
+    /// in a set like `HashSet` or `BTreeSet`, followed by an `insert`.
     ///
     /// ### Why is this bad?
     /// Using just `insert` and checking the returned `bool` is more efficient.
@@ -45,12 +45,12 @@ declare_clippy_lint! {
     #[clippy::version = "1.80.0"]
     pub SET_CONTAINS_OR_INSERT,
     nursery,
-    "call to `HashSet::contains` followed by `HashSet::insert`"
+    "call to `<set>::contains` followed by `<set>::insert`"
 }
 
-declare_lint_pass!(HashsetInsertAfterContains => [SET_CONTAINS_OR_INSERT]);
+declare_lint_pass!(SetContainsOrInsert => [SET_CONTAINS_OR_INSERT]);
 
-impl<'tcx> LateLintPass<'tcx> for HashsetInsertAfterContains {
+impl<'tcx> LateLintPass<'tcx> for SetContainsOrInsert {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if !expr.span.from_expansion()
             && let Some(higher::If {
@@ -58,14 +58,14 @@ impl<'tcx> LateLintPass<'tcx> for HashsetInsertAfterContains {
                 then: then_expr,
                 ..
             }) = higher::If::hir(expr)
-            && let Some(contains_expr) = try_parse_op_call(cx, cond_expr, sym!(contains))//try_parse_contains(cx, cond_expr)
+            && let Some((contains_expr, sym)) = try_parse_op_call(cx, cond_expr, sym!(contains))//try_parse_contains(cx, cond_expr)
             && let Some(insert_expr) = find_insert_calls(cx, &contains_expr, then_expr)
         {
             span_lint(
                 cx,
                 SET_CONTAINS_OR_INSERT,
                 vec![contains_expr.span, insert_expr.span],
-                "usage of `HashSet::insert` after `HashSet::contains`",
+                format!("usage of `{sym}::insert` after `{sym}::contains`"),
             );
         }
     }
@@ -77,7 +77,11 @@ struct OpExpr<'tcx> {
     span: Span,
 }
 
-fn try_parse_op_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, symbol: Symbol) -> Option<OpExpr<'tcx>> {
+fn try_parse_op_call<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    symbol: Symbol,
+) -> Option<(OpExpr<'tcx>, Symbol)> {
     let expr = peel_hir_expr_while(expr, |e| {
         if let ExprKind::Unary(UnOp::Not, e) = e.kind {
             Some(e)
@@ -97,11 +101,12 @@ fn try_parse_op_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, symbol:
         });
         let receiver = receiver.peel_borrows();
         let receiver_ty = cx.typeck_results().expr_ty(receiver).peel_refs();
-        if value.span.eq_ctxt(expr.span)
-            && is_type_diagnostic_item(cx, receiver_ty, sym::HashSet)
-            && path.ident.name == symbol
-        {
-            return Some(OpExpr { receiver, value, span });
+        if value.span.eq_ctxt(expr.span) && path.ident.name == symbol {
+            for sym in &[sym::HashSet, sym::BTreeSet] {
+                if is_type_diagnostic_item(cx, receiver_ty, *sym) {
+                    return Some((OpExpr { receiver, value, span }, *sym));
+                }
+            }
         }
     }
     None
@@ -113,7 +118,7 @@ fn find_insert_calls<'tcx>(
     expr: &'tcx Expr<'_>,
 ) -> Option<OpExpr<'tcx>> {
     for_each_expr(cx, expr, |e| {
-        if let Some(insert_expr) = try_parse_op_call(cx, e, sym!(insert))
+        if let Some((insert_expr, _)) = try_parse_op_call(cx, e, sym!(insert))
             && SpanlessEq::new(cx).eq_expr(contains_expr.receiver, insert_expr.receiver)
             && SpanlessEq::new(cx).eq_expr(contains_expr.value, insert_expr.value)
         {
diff --git a/clippy_lints/src/shadow.rs b/clippy_lints/src/shadow.rs
index 80f5fd0b49441..7ae0310b6d996 100644
--- a/clippy_lints/src/shadow.rs
+++ b/clippy_lints/src/shadow.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
 use clippy_utils::visitors::is_local_used;
 use rustc_data_structures::fx::FxHashMap;
@@ -194,14 +194,9 @@ fn lint_shadow(cx: &LateContext<'_>, pat: &Pat<'_>, shadowed: HirId, span: Span)
             (SHADOW_UNRELATED, msg)
         },
     };
-    span_lint_and_note(
-        cx,
-        lint,
-        span,
-        msg,
-        Some(cx.tcx.hir().span(shadowed)),
-        "previous binding is here",
-    );
+    span_lint_and_then(cx, lint, span, msg, |diag| {
+        diag.span_note(cx.tcx.hir().span(shadowed), "previous binding is here");
+    });
 }
 
 /// Returns true if the expression is a simple transformation of a local binding such as `&x`
diff --git a/clippy_lints/src/single_char_lifetime_names.rs b/clippy_lints/src/single_char_lifetime_names.rs
index 72feb977c3104..d92b890950a3f 100644
--- a/clippy_lints/src/single_char_lifetime_names.rs
+++ b/clippy_lints/src/single_char_lifetime_names.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_ast::ast::{GenericParam, GenericParamKind};
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -48,13 +48,15 @@ impl EarlyLintPass for SingleCharLifetimeNames {
 
         if let GenericParamKind::Lifetime = param.kind {
             if !param.is_placeholder && param.ident.as_str().len() <= 2 {
-                span_lint_and_help(
+                #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+                span_lint_and_then(
                     ctx,
                     SINGLE_CHAR_LIFETIME_NAMES,
                     param.ident.span,
                     "single-character lifetime names are likely uninformative",
-                    None,
-                    "use a more informative name",
+                    |diag| {
+                        diag.help("use a more informative name");
+                    },
                 );
             }
         }
diff --git a/clippy_lints/src/size_of_ref.rs b/clippy_lints/src/size_of_ref.rs
index 8d7f12af86e8a..b3d32a6d7d84c 100644
--- a/clippy_lints/src/size_of_ref.rs
+++ b/clippy_lints/src/size_of_ref.rs
@@ -1,6 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::path_def_id;
-use clippy_utils::ty::peel_mid_ty_refs;
+use clippy_utils::{path_def_id, peel_middle_ty_refs};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
@@ -60,7 +59,7 @@ impl LateLintPass<'_> for SizeOfRef {
             && let Some(def_id) = path_def_id(cx, path)
             && cx.tcx.is_diagnostic_item(sym::mem_size_of_val, def_id)
             && let arg_ty = cx.typeck_results().expr_ty(arg)
-            && peel_mid_ty_refs(arg_ty).1 > 1
+            && peel_middle_ty_refs(arg_ty).1 > 1
         {
             span_lint_and_help(
                 cx,
diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs
index 12b70075a3d5e..974e21df817a8 100644
--- a/clippy_lints/src/std_instead_of_core.rs
+++ b/clippy_lints/src/std_instead_of_core.rs
@@ -1,11 +1,15 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_config::msrvs::Msrv;
+use clippy_config::Conf;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_from_proc_macro;
+use rustc_attr::{StabilityLevel, StableSince};
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{HirId, Path, PathSegment};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
+use rustc_semver::RustcVersion;
 use rustc_session::impl_lint_pass;
 use rustc_span::symbol::kw;
 use rustc_span::{sym, Span};
@@ -66,6 +70,10 @@ declare_clippy_lint! {
     /// imported from core to ensure disabling `alloc` does not cause the crate to fail to compile. This lint
     /// is also useful for crates migrating to become `no_std` compatible.
     ///
+    /// ### Known problems
+    /// The lint is only partially aware of the required MSRV for items that were originally in `std` but moved
+    /// to `core`.
+    ///
     /// ### Example
     /// ```no_run
     /// # extern crate alloc;
@@ -81,20 +89,30 @@ declare_clippy_lint! {
     "type is imported from alloc when available in core"
 }
 
-#[derive(Default)]
 pub struct StdReexports {
     // Paths which can be either a module or a macro (e.g. `std::env`) will cause this check to happen
     // twice. First for the mod, second for the macro. This is used to avoid the lint reporting for the macro
     // when the path could be also be used to access the module.
     prev_span: Span,
+    msrv: Msrv,
 }
+
+impl StdReexports {
+    pub fn new(conf: &'static Conf) -> Self {
+        Self {
+            prev_span: Span::default(),
+            msrv: conf.msrv.clone(),
+        }
+    }
+}
+
 impl_lint_pass!(StdReexports => [STD_INSTEAD_OF_CORE, STD_INSTEAD_OF_ALLOC, ALLOC_INSTEAD_OF_CORE]);
 
 impl<'tcx> LateLintPass<'tcx> for StdReexports {
     fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, _: HirId) {
         if let Res::Def(_, def_id) = path.res
             && let Some(first_segment) = get_first_segment(path)
-            && is_stable(cx, def_id)
+            && is_stable(cx, def_id, &self.msrv)
             && !in_external_macro(cx.sess(), path.span)
             && !is_from_proc_macro(cx, &first_segment.ident)
         {
@@ -118,19 +136,27 @@ impl<'tcx> LateLintPass<'tcx> for StdReexports {
                 _ => return,
             };
             if first_segment.ident.span != self.prev_span {
-                span_lint_and_sugg(
+                #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+                span_lint_and_then(
                     cx,
                     lint,
                     first_segment.ident.span,
                     format!("used import from `{used_mod}` instead of `{replace_with}`"),
-                    format!("consider importing the item from `{replace_with}`"),
-                    replace_with.to_string(),
-                    Applicability::MachineApplicable,
+                    |diag| {
+                        diag.span_suggestion(
+                            first_segment.ident.span,
+                            format!("consider importing the item from `{replace_with}`"),
+                            replace_with.to_string(),
+                            Applicability::MachineApplicable,
+                        );
+                    },
                 );
                 self.prev_span = first_segment.ident.span;
             }
         }
     }
+
+    extract_msrv_attr!(LateContext);
 }
 
 /// Returns the first named segment of a [`Path`].
@@ -146,16 +172,29 @@ fn get_first_segment<'tcx>(path: &Path<'tcx>) -> Option<&'tcx PathSegment<'tcx>>
     }
 }
 
-/// Checks if all ancestors of `def_id` are stable, to avoid linting
-/// [unstable moves](https://github.com/rust-lang/rust/pull/95956)
-fn is_stable(cx: &LateContext<'_>, mut def_id: DefId) -> bool {
+/// Checks if all ancestors of `def_id` meet `msrv` to avoid linting [unstable moves](https://github.com/rust-lang/rust/pull/95956)
+/// or now stable moves that were once unstable.
+///
+/// Does not catch individually moved items
+fn is_stable(cx: &LateContext<'_>, mut def_id: DefId, msrv: &Msrv) -> bool {
     loop {
-        if cx
-            .tcx
-            .lookup_stability(def_id)
-            .map_or(false, |stability| stability.is_unstable())
+        if let Some(stability) = cx.tcx.lookup_stability(def_id)
+            && let StabilityLevel::Stable {
+                since,
+                allowed_through_unstable_modules: false,
+            } = stability.level
         {
-            return false;
+            let stable = match since {
+                StableSince::Version(v) => {
+                    msrv.meets(RustcVersion::new(v.major.into(), v.minor.into(), v.patch.into()))
+                },
+                StableSince::Current => msrv.current().is_none(),
+                StableSince::Err => false,
+            };
+
+            if !stable {
+                return false;
+            }
         }
 
         match cx.tcx.opt_parent(def_id) {
diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs
index 7da661485abfc..cfc387886dc01 100644
--- a/clippy_lints/src/strings.rs
+++ b/clippy_lints/src/strings.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::ty::is_type_lang_item;
 use clippy_utils::{
@@ -399,17 +399,16 @@ impl<'tcx> LateLintPass<'tcx> for StrToString {
             && let ty::Ref(_, ty, ..) = ty.kind()
             && ty.is_str()
         {
-            let mut applicability = Applicability::MachineApplicable;
-            let snippet = snippet_with_applicability(cx, self_arg.span, "..", &mut applicability);
-
-            span_lint_and_sugg(
+            span_lint_and_then(
                 cx,
                 STR_TO_STRING,
                 expr.span,
                 "`to_string()` called on a `&str`",
-                "try",
-                format!("{snippet}.to_owned()"),
-                applicability,
+                |diag| {
+                    let mut applicability = Applicability::MachineApplicable;
+                    let snippet = snippet_with_applicability(cx, self_arg.span, "..", &mut applicability);
+                    diag.span_suggestion(expr.span, "try", format!("{snippet}.to_owned()"), applicability);
+                },
             );
         }
     }
@@ -455,13 +454,15 @@ impl<'tcx> LateLintPass<'tcx> for StringToString {
             && let ty = cx.typeck_results().expr_ty(self_arg)
             && is_type_lang_item(cx, ty, LangItem::String)
         {
-            span_lint_and_help(
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(
                 cx,
                 STRING_TO_STRING,
                 expr.span,
                 "`to_string()` called on a `String`",
-                None,
-                "consider using `.clone()`",
+                |diag| {
+                    diag.help("consider using `.clone()`");
+                },
             );
         }
     }
diff --git a/clippy_lints/src/suspicious_xor_used_as_pow.rs b/clippy_lints/src/suspicious_xor_used_as_pow.rs
index d150a5f858aa3..d1d822a553260 100644
--- a/clippy_lints/src/suspicious_xor_used_as_pow.rs
+++ b/clippy_lints/src/suspicious_xor_used_as_pow.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::numeric_literal::NumericLiteral;
 use clippy_utils::source::snippet;
 use rustc_ast::LitKind;
@@ -43,14 +43,19 @@ impl LateLintPass<'_> for ConfusingXorAndPow {
             && NumericLiteral::from_lit_kind(&snippet(cx, lit_right.span, ".."), &lit_right.node)
                 .is_some_and(|x| x.is_decimal())
         {
-            span_lint_and_sugg(
+            span_lint_and_then(
                 cx,
                 SUSPICIOUS_XOR_USED_AS_POW,
                 expr.span,
                 "`^` is not the exponentiation operator",
-                "did you mean to write",
-                format!("{}.pow({})", lit_left.node, lit_right.node),
-                Applicability::MaybeIncorrect,
+                |diag| {
+                    diag.span_suggestion_verbose(
+                        expr.span,
+                        "did you mean to write",
+                        format!("{}.pow({})", lit_left.node, lit_right.node),
+                        Applicability::MaybeIncorrect,
+                    );
+                },
             );
         }
     }
diff --git a/clippy_lints/src/swap.rs b/clippy_lints/src/swap.rs
index 93bad86580975..197011cde3a1a 100644
--- a/clippy_lints/src/swap.rs
+++ b/clippy_lints/src/swap.rs
@@ -3,7 +3,7 @@ use clippy_utils::source::{snippet_indent, snippet_with_context};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_type_diagnostic_item;
 
-use clippy_utils::{can_mut_borrow_both, eq_expr_value, in_constant, std_or_core};
+use clippy_utils::{can_mut_borrow_both, eq_expr_value, is_in_const_context, std_or_core};
 use itertools::Itertools;
 
 use rustc_hir::intravisit::{walk_expr, Visitor};
@@ -170,7 +170,7 @@ fn generate_swap_warning<'tcx>(
 
 /// Implementation of the `MANUAL_SWAP` lint.
 fn check_manual_swap<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) {
-    if in_constant(cx, block.hir_id) {
+    if is_in_const_context(cx) {
         return;
     }
 
diff --git a/clippy_lints/src/tests_outside_test_module.rs b/clippy_lints/src/tests_outside_test_module.rs
index 58e42892c41de..25d0a16e2ab45 100644
--- a/clippy_lints/src/tests_outside_test_module.rs
+++ b/clippy_lints/src/tests_outside_test_module.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_note;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::{is_in_cfg_test, is_in_test_function};
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Body, FnDecl};
@@ -61,13 +61,15 @@ impl LateLintPass<'_> for TestsOutsideTestModule {
             && is_in_test_function(cx.tcx, body.id().hir_id)
             && !is_in_cfg_test(cx.tcx, body.id().hir_id)
         {
-            span_lint_and_note(
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(
                 cx,
                 TESTS_OUTSIDE_TEST_MODULE,
                 sp,
                 "this function marked with #[test] is outside a #[cfg(test)] module",
-                None,
-                "move it to a testing module marked with #[cfg(test)]",
+                |diag| {
+                    diag.note("move it to a testing module marked with #[cfg(test)]");
+                },
             );
         }
     }
diff --git a/clippy_lints/src/transmute/mod.rs b/clippy_lints/src/transmute/mod.rs
index 9c6813a54b943..373bf61d8ff94 100644
--- a/clippy_lints/src/transmute/mod.rs
+++ b/clippy_lints/src/transmute/mod.rs
@@ -21,7 +21,7 @@ mod wrong_transmute;
 
 use clippy_config::msrvs::Msrv;
 use clippy_config::Conf;
-use clippy_utils::in_constant;
+use clippy_utils::is_in_const_context;
 use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
@@ -595,7 +595,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
             // - from/to bits (https://github.com/rust-lang/rust/issues/73736)
             // - dereferencing raw pointers (https://github.com/rust-lang/rust/issues/51911)
             // - char conversions (https://github.com/rust-lang/rust/issues/89259)
-            let const_context = in_constant(cx, e.hir_id);
+            let const_context = is_in_const_context(cx);
 
             let (from_ty, from_ty_adjusted) = match cx.typeck_results().expr_adjustments(arg) {
                 [] => (cx.typeck_results().expr_ty(arg), false),
diff --git a/clippy_lints/src/transmute/transmute_null_to_fn.rs b/clippy_lints/src/transmute/transmute_null_to_fn.rs
index b26365e34ab94..7acf3be51fb7e 100644
--- a/clippy_lints/src/transmute/transmute_null_to_fn.rs
+++ b/clippy_lints/src/transmute/transmute_null_to_fn.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::{is_integer_literal, is_path_diagnostic_item};
 use rustc_hir::{Expr, ExprKind};
@@ -33,10 +33,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
         // Catching:
         // transmute over constants that resolve to `null`.
         ExprKind::Path(ref _qpath)
-            if matches!(
-                constant(cx, cx.typeck_results(), casts_peeled),
-                Some(Constant::RawPtr(0))
-            ) =>
+            if matches!(ConstEvalCtxt::new(cx).eval(casts_peeled), Some(Constant::RawPtr(0))) =>
         {
             lint_expr(cx, expr);
             true
diff --git a/clippy_lints/src/transmute/transmuting_null.rs b/clippy_lints/src/transmute/transmuting_null.rs
index 471bd44b5d5ee..544014bd32b30 100644
--- a/clippy_lints/src/transmute/transmuting_null.rs
+++ b/clippy_lints/src/transmute/transmuting_null.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::{is_integer_literal, is_path_diagnostic_item};
 use rustc_hir::{Expr, ExprKind};
@@ -17,7 +17,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, arg: &'t
 
     // Catching transmute over constants that resolve to `null`.
     if let ExprKind::Path(ref _qpath) = arg.kind
-        && let Some(Constant::RawPtr(0)) = constant(cx, cx.typeck_results(), arg)
+        && let Some(Constant::RawPtr(0)) = ConstEvalCtxt::new(cx).eval(arg)
     {
         span_lint(cx, TRANSMUTING_NULL, expr.span, LINT_MSG);
         return true;
diff --git a/clippy_lints/src/types/borrowed_box.rs b/clippy_lints/src/types/borrowed_box.rs
index 83cc9f2d8df23..2fcfc71a8c768 100644
--- a/clippy_lints/src/types/borrowed_box.rs
+++ b/clippy_lints/src/types/borrowed_box.rs
@@ -48,15 +48,15 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
                 let inner_snippet = snippet(cx, inner.span, "..");
                 let suggestion = match &inner.kind {
                     TyKind::TraitObject(bounds, lt_bound, _) if bounds.len() > 1 || !lt_bound.is_elided() => {
-                        format!("&{ltopt}({})", &inner_snippet)
+                        format!("&{ltopt}({inner_snippet})")
                     },
                     TyKind::Path(qpath)
                         if get_bounds_if_impl_trait(cx, qpath, inner.hir_id)
                             .map_or(false, |bounds| bounds.len() > 1) =>
                     {
-                        format!("&{ltopt}({})", &inner_snippet)
+                        format!("&{ltopt}({inner_snippet})")
                     },
-                    _ => format!("&{ltopt}{}", &inner_snippet),
+                    _ => format!("&{ltopt}{inner_snippet}"),
                 };
                 span_lint_and_sugg(
                     cx,
@@ -82,16 +82,14 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
 // Returns true if given type is `Any` trait.
 fn is_any_trait(cx: &LateContext<'_>, t: &hir::Ty<'_>) -> bool {
     if let TyKind::TraitObject(traits, ..) = t.kind {
-        return traits
-            .iter()
-            .any(|(bound, _)| {
-                if let Some(trait_did) = bound.trait_ref.trait_def_id()
-                    && cx.tcx.is_diagnostic_item(sym::Any, trait_did)
-                {
-                    return true;
-                }
-                false
-            });
+        return traits.iter().any(|(bound, _)| {
+            if let Some(trait_did) = bound.trait_ref.trait_def_id()
+                && cx.tcx.is_diagnostic_item(sym::Any, trait_did)
+            {
+                return true;
+            }
+            false
+        });
     }
 
     false
diff --git a/clippy_lints/src/types/rc_buffer.rs b/clippy_lints/src/types/rc_buffer.rs
index f6c2d8d5a5ed9..d691f1878b11b 100644
--- a/clippy_lints/src/types/rc_buffer.rs
+++ b/clippy_lints/src/types/rc_buffer.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::{path_def_id, qpath_generic_tys};
 use rustc_errors::Applicability;
@@ -13,14 +13,15 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
     let app = Applicability::Unspecified;
     if cx.tcx.is_diagnostic_item(sym::Rc, def_id) {
         if let Some(alternate) = match_buffer_type(cx, qpath) {
-            span_lint_and_sugg(
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(
                 cx,
                 RC_BUFFER,
                 hir_ty.span,
                 "usage of `Rc<T>` when T is a buffer type",
-                "try",
-                format!("Rc<{alternate}>"),
-                app,
+                |diag| {
+                    diag.span_suggestion(hir_ty.span, "try", format!("Rc<{alternate}>"), app);
+                },
             );
         } else {
             let Some(ty) = qpath_generic_tys(qpath).next() else {
@@ -35,31 +36,37 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
                 Some(ty) => ty.span,
                 None => return false,
             };
-            let mut applicability = app;
-            span_lint_and_sugg(
+            span_lint_and_then(
                 cx,
                 RC_BUFFER,
                 hir_ty.span,
                 "usage of `Rc<T>` when T is a buffer type",
-                "try",
-                format!(
-                    "Rc<[{}]>",
-                    snippet_with_applicability(cx, inner_span, "..", &mut applicability)
-                ),
-                app,
+                |diag| {
+                    let mut applicability = app;
+                    diag.span_suggestion(
+                        hir_ty.span,
+                        "try",
+                        format!(
+                            "Rc<[{}]>",
+                            snippet_with_applicability(cx, inner_span, "..", &mut applicability)
+                        ),
+                        app,
+                    );
+                },
             );
             return true;
         }
     } else if cx.tcx.is_diagnostic_item(sym::Arc, def_id) {
         if let Some(alternate) = match_buffer_type(cx, qpath) {
-            span_lint_and_sugg(
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(
                 cx,
                 RC_BUFFER,
                 hir_ty.span,
                 "usage of `Arc<T>` when T is a buffer type",
-                "try",
-                format!("Arc<{alternate}>"),
-                app,
+                |diag| {
+                    diag.span_suggestion(hir_ty.span, "try", format!("Arc<{alternate}>"), app);
+                },
             );
         } else if let Some(ty) = qpath_generic_tys(qpath).next() {
             let Some(id) = path_def_id(cx, ty) else { return false };
@@ -71,18 +78,23 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
                 Some(ty) => ty.span,
                 None => return false,
             };
-            let mut applicability = app;
-            span_lint_and_sugg(
+            span_lint_and_then(
                 cx,
                 RC_BUFFER,
                 hir_ty.span,
                 "usage of `Arc<T>` when T is a buffer type",
-                "try",
-                format!(
-                    "Arc<[{}]>",
-                    snippet_with_applicability(cx, inner_span, "..", &mut applicability)
-                ),
-                app,
+                |diag| {
+                    let mut applicability = app;
+                    diag.span_suggestion(
+                        hir_ty.span,
+                        "try",
+                        format!(
+                            "Arc<[{}]>",
+                            snippet_with_applicability(cx, inner_span, "..", &mut applicability)
+                        ),
+                        app,
+                    );
+                },
             );
             return true;
         }
diff --git a/clippy_lints/src/types/rc_mutex.rs b/clippy_lints/src/types/rc_mutex.rs
index afc319217042f..7b13debc01ef8 100644
--- a/clippy_lints/src/types/rc_mutex.rs
+++ b/clippy_lints/src/types/rc_mutex.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::{path_def_id, qpath_generic_tys};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{self as hir, QPath};
@@ -13,14 +13,10 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, qpath: &QPath<'_
         && let Some(id) = path_def_id(cx, arg)
         && cx.tcx.is_diagnostic_item(sym::Mutex, id)
     {
-        span_lint_and_help(
-            cx,
-            RC_MUTEX,
-            hir_ty.span,
-            "usage of `Rc<Mutex<_>>`",
-            None,
-            "consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead",
-        );
+        #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+        span_lint_and_then(cx, RC_MUTEX, hir_ty.span, "usage of `Rc<Mutex<_>>`", |diag| {
+            diag.help("consider using `Rc<RefCell<_>>` or `Arc<Mutex<_>>` instead");
+        });
         return true;
     }
 
diff --git a/clippy_lints/src/undocumented_unsafe_blocks.rs b/clippy_lints/src/undocumented_unsafe_blocks.rs
index 3ab30bf5dba7d..f51c5f74d9930 100644
--- a/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -1,7 +1,7 @@
 use std::ops::ControlFlow;
 
 use clippy_config::Conf;
-use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_lint_allowed;
 use clippy_utils::source::walk_span_to_context;
 use clippy_utils::visitors::{for_each_expr, Descend};
@@ -129,13 +129,15 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
                 block.span
             };
 
-            span_lint_and_help(
+            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+            span_lint_and_then(
                 cx,
                 UNDOCUMENTED_UNSAFE_BLOCKS,
                 span,
                 "unsafe block missing a safety comment",
-                None,
-                "consider adding a safety comment on the preceding line",
+                |diag| {
+                    diag.help("consider adding a safety comment on the preceding line");
+                },
             );
         }
 
@@ -145,13 +147,14 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
             && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, tail.span, tail.hir_id)
             && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, tail, pos)
         {
-            span_lint_and_help(
+            span_lint_and_then(
                 cx,
                 UNNECESSARY_SAFETY_COMMENT,
                 tail.span,
                 "expression has unnecessary safety comment",
-                Some(help_span),
-                "consider removing the safety comment",
+                |diag| {
+                    diag.span_help(help_span, "consider removing the safety comment");
+                },
             );
         }
     }
@@ -168,13 +171,14 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
             && let HasSafetyComment::Yes(pos) = stmt_has_safety_comment(cx, stmt.span, stmt.hir_id)
             && let Some(help_span) = expr_has_unnecessary_safety_comment(cx, expr, pos)
         {
-            span_lint_and_help(
+            span_lint_and_then(
                 cx,
                 UNNECESSARY_SAFETY_COMMENT,
                 stmt.span,
                 "statement has unnecessary safety comment",
-                Some(help_span),
-                "consider removing the safety comment",
+                |diag| {
+                    diag.span_help(help_span, "consider removing the safety comment");
+                },
             );
         }
     }
@@ -210,13 +214,15 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
                         item.span
                     };
 
-                    span_lint_and_help(
+                    #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+                    span_lint_and_then(
                         cx,
                         UNDOCUMENTED_UNSAFE_BLOCKS,
                         span,
                         "unsafe impl missing a safety comment",
-                        None,
-                        "consider adding a safety comment on the preceding line",
+                        |diag| {
+                            diag.help("consider adding a safety comment on the preceding line");
+                        },
                     );
                 }
             },
@@ -225,13 +231,14 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
                 if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) {
                     let (span, help_span) = mk_spans(pos);
 
-                    span_lint_and_help(
+                    span_lint_and_then(
                         cx,
                         UNNECESSARY_SAFETY_COMMENT,
                         span,
                         "impl has unnecessary safety comment",
-                        Some(help_span),
-                        "consider removing the safety comment",
+                        |diag| {
+                            diag.span_help(help_span, "consider removing the safety comment");
+                        },
                     );
                 }
             },
@@ -246,13 +253,14 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
                     ) {
                         let (span, help_span) = mk_spans(pos);
 
-                        span_lint_and_help(
+                        span_lint_and_then(
                             cx,
                             UNNECESSARY_SAFETY_COMMENT,
                             span,
                             format!("{} has unnecessary safety comment", item.kind.descr()),
-                            Some(help_span),
-                            "consider removing the safety comment",
+                            |diag| {
+                                diag.span_help(help_span, "consider removing the safety comment");
+                            },
                         );
                     }
                 }
@@ -263,13 +271,14 @@ impl<'tcx> LateLintPass<'tcx> for UndocumentedUnsafeBlocks {
                 if !is_lint_allowed(cx, UNNECESSARY_SAFETY_COMMENT, item.hir_id()) {
                     let (span, help_span) = mk_spans(pos);
 
-                    span_lint_and_help(
+                    span_lint_and_then(
                         cx,
                         UNNECESSARY_SAFETY_COMMENT,
                         span,
                         format!("{} has unnecessary safety comment", item.kind.descr()),
-                        Some(help_span),
-                        "consider removing the safety comment",
+                        |diag| {
+                            diag.span_help(help_span, "consider removing the safety comment");
+                        },
                     );
                 }
             },
diff --git a/clippy_lints/src/unicode.rs b/clippy_lints/src/unicode.rs
index d42697b31d1f2..e1fc644e4ceeb 100644
--- a/clippy_lints/src/unicode.rs
+++ b/clippy_lints/src/unicode.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_lint_allowed;
 use clippy_utils::macros::span_is_local;
 use clippy_utils::source::snippet;
@@ -105,45 +105,51 @@ fn check_str(cx: &LateContext<'_>, span: Span, id: HirId) {
 
     let string = snippet(cx, span, "");
     if string.chars().any(|c| ['\u{200B}', '\u{ad}', '\u{2060}'].contains(&c)) {
-        span_lint_and_sugg(
-            cx,
-            INVISIBLE_CHARACTERS,
-            span,
-            "invisible character detected",
-            "consider replacing the string with",
-            string
-                .replace('\u{200B}', "\\u{200B}")
-                .replace('\u{ad}', "\\u{AD}")
-                .replace('\u{2060}', "\\u{2060}"),
-            Applicability::MachineApplicable,
-        );
+        #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+        span_lint_and_then(cx, INVISIBLE_CHARACTERS, span, "invisible character detected", |diag| {
+            diag.span_suggestion(
+                span,
+                "consider replacing the string with",
+                string
+                    .replace('\u{200B}', "\\u{200B}")
+                    .replace('\u{ad}', "\\u{AD}")
+                    .replace('\u{2060}', "\\u{2060}"),
+                Applicability::MachineApplicable,
+            );
+        });
     }
 
     if string.chars().any(|c| c as u32 > 0x7F) {
-        span_lint_and_sugg(
+        #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+        span_lint_and_then(
             cx,
             NON_ASCII_LITERAL,
             span,
             "literal non-ASCII character detected",
-            "consider replacing the string with",
-            if is_lint_allowed(cx, UNICODE_NOT_NFC, id) {
-                escape(string.chars())
-            } else {
-                escape(string.nfc())
+            |diag| {
+                diag.span_suggestion(
+                    span,
+                    "consider replacing the string with",
+                    if is_lint_allowed(cx, UNICODE_NOT_NFC, id) {
+                        escape(string.chars())
+                    } else {
+                        escape(string.nfc())
+                    },
+                    Applicability::MachineApplicable,
+                );
             },
-            Applicability::MachineApplicable,
         );
     }
 
     if is_lint_allowed(cx, NON_ASCII_LITERAL, id) && string.chars().zip(string.nfc()).any(|(a, b)| a != b) {
-        span_lint_and_sugg(
-            cx,
-            UNICODE_NOT_NFC,
-            span,
-            "non-NFC Unicode sequence detected",
-            "consider replacing the string with",
-            string.nfc().collect::<String>(),
-            Applicability::MachineApplicable,
-        );
+        #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+        span_lint_and_then(cx, UNICODE_NOT_NFC, span, "non-NFC Unicode sequence detected", |diag| {
+            diag.span_suggestion(
+                span,
+                "consider replacing the string with",
+                string.nfc().collect::<String>(),
+                Applicability::MachineApplicable,
+            );
+        });
     }
 }
diff --git a/clippy_lints/src/unused_result_ok.rs b/clippy_lints/src/unused_result_ok.rs
new file mode 100644
index 0000000000000..297288db0a56b
--- /dev/null
+++ b/clippy_lints/src/unused_result_ok.rs
@@ -0,0 +1,59 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_context;
+use clippy_utils::ty::is_type_diagnostic_item;
+use rustc_errors::Applicability;
+use rustc_hir::{ExprKind, Stmt, StmtKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::declare_lint_pass;
+use rustc_span::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for calls to `Result::ok()` without using the returned `Option`.
+    ///
+    /// ### Why is this bad?
+    /// Using `Result::ok()` may look like the result is checked like `unwrap` or `expect` would do
+    /// but it only silences the warning caused by `#[must_use]` on the `Result`.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// # fn some_function() -> Result<(), ()> { Ok(()) }
+    /// some_function().ok();
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// # fn some_function() -> Result<(), ()> { Ok(()) }
+    /// let _ = some_function();
+    /// ```
+    #[clippy::version = "1.70.0"]
+    pub UNUSED_RESULT_OK,
+    restriction,
+    "Use of `.ok()` to silence `Result`'s `#[must_use]` is misleading. Use `let _ =` instead."
+}
+declare_lint_pass!(UnusedResultOk => [UNUSED_RESULT_OK]);
+
+impl LateLintPass<'_> for UnusedResultOk {
+    fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) {
+        if let StmtKind::Semi(expr) = stmt.kind
+            && let ExprKind::MethodCall(ok_path, recv, [], ..) = expr.kind //check is expr.ok() has type Result<T,E>.ok(, _)
+            && ok_path.ident.as_str() == "ok"
+            && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result)
+            && !in_external_macro(cx.sess(), stmt.span)
+        {
+            let ctxt = expr.span.ctxt();
+            let mut applicability = Applicability::MaybeIncorrect;
+            let snippet = snippet_with_context(cx, recv.span, ctxt, "", &mut applicability).0;
+            let sugg = format!("let _ = {snippet}");
+            span_lint_and_sugg(
+                cx,
+                UNUSED_RESULT_OK,
+                expr.span,
+                "ignoring a result with `.ok()` is misleading",
+                "consider using `let _ =` and removing the call to `.ok()` instead",
+                sugg,
+                applicability,
+            );
+        }
+    }
+}
diff --git a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs
index b017a6bf665c5..7c2e23995c1d3 100644
--- a/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs
+++ b/clippy_lints/src/utils/internal_lints/interning_defined_symbol.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant_simple, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
 use clippy_utils::ty::match_type;
@@ -93,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for InterningDefinedSymbol {
         if let ExprKind::Call(func, [arg]) = &expr.kind
             && let ty::FnDef(def_id, _) = cx.typeck_results().expr_ty(func).kind()
             && match_def_path(cx, *def_id, &paths::SYMBOL_INTERN)
-            && let Some(Constant::Str(arg)) = constant_simple(cx, cx.typeck_results(), arg)
+            && let Some(Constant::Str(arg)) = ConstEvalCtxt::new(cx).eval_simple(arg)
             && let value = Symbol::intern(&arg).as_u32()
             && let Some(&def_id) = self.symbol_map.get(&value)
         {
@@ -199,7 +199,7 @@ impl InterningDefinedSymbol {
             });
         }
         // is a string constant
-        if let Some(Constant::Str(s)) = constant_simple(cx, cx.typeck_results(), expr) {
+        if let Some(Constant::Str(s)) = ConstEvalCtxt::new(cx).eval_simple(expr) {
             let value = Symbol::intern(&s).as_u32();
             // ...which matches a symbol constant
             if let Some(&def_id) = self.symbol_map.get(&value) {
diff --git a/clippy_lints/src/utils/internal_lints/invalid_paths.rs b/clippy_lints/src/utils/internal_lints/invalid_paths.rs
index 980437259c3e9..0ffcb433481e4 100644
--- a/clippy_lints/src/utils/internal_lints/invalid_paths.rs
+++ b/clippy_lints/src/utils/internal_lints/invalid_paths.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant_simple, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::def_path_res;
 use clippy_utils::diagnostics::span_lint;
 use rustc_hir as hir;
@@ -32,9 +32,9 @@ impl<'tcx> LateLintPass<'tcx> for InvalidPaths {
         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 body = cx.tcx.hir().body(body_id)
-            && let typeck_results = cx.tcx.typeck_body(body_id)
-            && let Some(Constant::Vec(path)) = constant_simple(cx, typeck_results, body.value)
+            && let Some(Constant::Vec(path)) =
+                ConstEvalCtxt::with_env(cx.tcx, cx.tcx.param_env(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| {
diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
index 84f84781e7126..df342e48d637f 100644
--- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
+++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
@@ -1,4 +1,3 @@
-use crate::utils::internal_lints::metadata_collector::is_deprecated_lint;
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 use clippy_utils::macros::root_macro_call_first_node;
 use clippy_utils::{is_lint_allowed, match_def_path, paths};
@@ -87,82 +86,32 @@ declare_clippy_lint! {
     "found clippy lint without `clippy::version` attribute"
 }
 
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for cases of an auto-generated deprecated lint without an updated reason,
-    /// i.e. `"default deprecation note"`.
-    ///
-    /// ### Why is this bad?
-    /// Indicates that the documentation is incomplete.
-    ///
-    /// ### Example
-    /// ```rust,ignore
-    /// declare_deprecated_lint! {
-    ///     /// ### What it does
-    ///     /// Nothing. This lint has been deprecated.
-    ///     ///
-    ///     /// ### Deprecation reason
-    ///     /// TODO
-    ///     #[clippy::version = "1.63.0"]
-    ///     pub COOL_LINT,
-    ///     "default deprecation note"
-    /// }
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust,ignore
-    /// declare_deprecated_lint! {
-    ///     /// ### What it does
-    ///     /// Nothing. This lint has been deprecated.
-    ///     ///
-    ///     /// ### Deprecation reason
-    ///     /// This lint has been replaced by `cooler_lint`
-    ///     #[clippy::version = "1.63.0"]
-    ///     pub COOL_LINT,
-    ///     "this lint has been replaced by `cooler_lint`"
-    /// }
-    /// ```
-    pub DEFAULT_DEPRECATION_REASON,
-    internal,
-    "found 'default deprecation note' in a deprecated lint declaration"
-}
-
 #[derive(Clone, Debug, Default)]
 pub struct LintWithoutLintPass {
     declared_lints: FxHashMap<Symbol, Span>,
     registered_lints: FxHashSet<Symbol>,
 }
 
-impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]);
+impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE]);
 
 impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-        if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id())
-            || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id())
-        {
+        if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) {
             return;
         }
 
         if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
-            let is_lint_ref_ty = is_lint_ref_type(cx, ty);
-            if is_deprecated_lint(cx, ty) || is_lint_ref_ty {
+            if is_lint_ref_type(cx, ty) {
                 check_invalid_clippy_version_attribute(cx, item);
 
                 let expr = &cx.tcx.hir().body(body_id).value;
-                let fields;
-                if is_lint_ref_ty {
-                    if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind
-                        && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind
-                    {
-                        fields = struct_fields;
-                    } else {
-                        return;
-                    }
-                } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind {
-                    fields = struct_fields;
+                let fields = if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind
+                    && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind
+                {
+                    struct_fields
                 } else {
                     return;
-                }
+                };
 
                 let field = fields
                     .iter()
@@ -175,25 +124,15 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
                 }) = field.expr.kind
                 {
                     let sym_str = sym.as_str();
-                    if is_lint_ref_ty {
-                        if sym_str == "default lint description" {
-                            span_lint(
-                                cx,
-                                DEFAULT_LINT,
-                                item.span,
-                                format!("the lint `{}` has the default lint description", item.ident.name),
-                            );
-                        }
-
-                        self.declared_lints.insert(item.ident.name, item.span);
-                    } else if sym_str == "default deprecation note" {
+                    if sym_str == "default lint description" {
                         span_lint(
                             cx,
-                            DEFAULT_DEPRECATION_REASON,
+                            DEFAULT_LINT,
                             item.span,
-                            format!("the lint `{}` has the default deprecation reason", item.ident.name),
+                            format!("the lint `{}` has the default lint description", item.ident.name),
                         );
                     }
+                    self.declared_lints.insert(item.ident.name, item.span);
                 }
             }
         } else if let Some(macro_call) = root_macro_call_first_node(cx, item) {
diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index 1c149f2045629..57f45aa3e4851 100644
--- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -7,13 +7,12 @@
 //! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such
 //! a simple mistake)
 
-use crate::renamed_lints::RENAMED_LINTS;
 use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type};
 use clippy_config::{get_configuration_metadata, ClippyConfiguration};
 
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::ty::{match_type, walk_ptrs_ty_depth};
-use clippy_utils::{last_path_segment, match_def_path, match_function_call, match_path, paths};
+use clippy_utils::{last_path_segment, match_function_call, match_path, paths};
 use itertools::Itertools;
 use rustc_ast as ast;
 use rustc_data_structures::fx::FxHashMap;
@@ -85,11 +84,6 @@ const SUGGESTION_DIAG_METHODS: [(&str, bool); 9] = [
     ("tool_only_multipart_suggestion", true),
     ("span_suggestions", true),
 ];
-const SUGGESTION_FUNCTIONS: [&[&str]; 2] = [
-    &["clippy_utils", "diagnostics", "multispan_sugg"],
-    &["clippy_utils", "diagnostics", "multispan_sugg_with_applicability"],
-];
-const DEPRECATED_LINT_TYPE: [&str; 3] = ["clippy_lints", "deprecated_lints", "ClippyDeprecatedLint"];
 
 /// The index of the applicability name of `paths::APPLICABILITY_VALUES`
 const APPLICABILITY_NAME_INDEX: usize = 2;
@@ -165,9 +159,9 @@ impl MetadataCollector {
     fn get_lint_configs(&self, lint_name: &str) -> Option<String> {
         self.config
             .iter()
-            .filter(|config| config.lints.iter().any(|lint| lint == lint_name))
+            .filter(|config| config.lints.iter().any(|&lint| lint == lint_name))
             .map(ToString::to_string)
-            .reduce(|acc, x| acc + &x)
+            .reduce(|acc, x| acc + "\n\n" + &x)
             .map(|configurations| {
                 format!(
                     r#"
@@ -216,6 +210,13 @@ impl Drop for MetadataCollector {
 
         let mut applicability_info = std::mem::take(&mut self.applicability_info);
 
+        // Add deprecated lints
+        self.lints.extend(
+            crate::deprecated_lints::DEPRECATED
+                .iter()
+                .zip(crate::deprecated_lints::DEPRECATED_VERSION)
+                .filter_map(|((lint, reason), version)| LintMetadata::new_deprecated(lint, reason, version)),
+        );
         // Mapping the final data
         let mut lints = std::mem::take(&mut self.lints).into_sorted_vec();
         for x in &mut lints {
@@ -261,7 +262,7 @@ Please use that command to update the file and do not edit it by hand.
 #[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
 struct LintMetadata {
     id: String,
-    id_span: SerializableSpan,
+    id_span: Option<SerializableSpan>,
     group: String,
     level: String,
     docs: String,
@@ -285,7 +286,7 @@ impl LintMetadata {
     ) -> Self {
         Self {
             id,
-            id_span,
+            id_span: Some(id_span),
             group,
             level: level.to_string(),
             version,
@@ -294,6 +295,29 @@ impl LintMetadata {
             former_ids: BTreeSet::new(),
         }
     }
+
+    fn new_deprecated(name: &str, reason: &str, version: &str) -> Option<Self> {
+        // The reason starts with a lowercase letter and end without a period.
+        // This needs to be fixed for the website.
+        let mut reason = reason.to_owned();
+        if let Some(reason) = reason.get_mut(0..1) {
+            reason.make_ascii_uppercase();
+        }
+        name.strip_prefix("clippy::").map(|name| Self {
+            id: name.into(),
+            id_span: None,
+            group: DEPRECATED_LINT_GROUP_STR.into(),
+            level: DEPRECATED_LINT_LEVEL.into(),
+            version: version.into(),
+            docs: format!(
+                "### What it does\n\n\
+                Nothing. This lint has been deprecated\n\n\
+                ### Deprecation reason\n\n{reason}.\n",
+            ),
+            applicability: None,
+            former_ids: BTreeSet::new(),
+        })
+    }
 }
 
 fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Path) {
@@ -564,24 +588,6 @@ impl<'hir> LateLintPass<'hir> for MetadataCollector {
                     raw_docs,
                 ));
             }
-
-            if is_deprecated_lint(cx, ty)
-                // disallow check
-                && let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase()
-                // Metadata the little we can get from a deprecated lint
-                && let Some(raw_docs) = extract_attr_docs_or_lint(cx, item)
-            {
-                let version = get_lint_version(cx, item);
-
-                self.lints.push(LintMetadata::new(
-                    lint_name,
-                    SerializableSpan::from_item(cx, item),
-                    DEPRECATED_LINT_GROUP_STR.to_string(),
-                    DEPRECATED_LINT_LEVEL,
-                    version,
-                    raw_docs,
-                ));
-            }
         }
     }
 
@@ -684,6 +690,11 @@ fn cleanup_docs(docs_collection: &Vec<String>) -> String {
                     .find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic"))
                     // if no language is present, fill in "rust"
                     .unwrap_or("rust");
+                let len_diff = line.len() - line.trim_start().len();
+                if len_diff != 0 {
+                    // We put back the indentation.
+                    docs.push_str(&line[..len_diff]);
+                }
                 docs.push_str("```");
                 docs.push_str(lang);
 
@@ -766,16 +777,6 @@ fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> {
         .find_map(|(group_name, group_level)| (*group_name == lint_group).then_some(*group_level))
 }
 
-pub(super) fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
-    if let hir::TyKind::Path(ref path) = ty.kind {
-        if let hir::def::Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, ty.hir_id) {
-            return match_def_path(cx, def_id, &DEPRECATED_LINT_TYPE);
-        }
-    }
-
-    false
-}
-
 fn collect_renames(lints: &mut Vec<LintMetadata>) {
     for lint in lints {
         let mut collected = String::new();
@@ -783,7 +784,7 @@ fn collect_renames(lints: &mut Vec<LintMetadata>) {
 
         loop {
             if let Some(lint_name) = names.pop() {
-                for (k, v) in RENAMED_LINTS {
+                for (k, v) in crate::deprecated_lints::RENAMED {
                     if let Some(name) = v.strip_prefix(CLIPPY_LINT_GROUP_PREFIX)
                         && name == lint_name
                         && let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX)
@@ -1055,33 +1056,21 @@ impl<'a, 'hir> Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> {
             return;
         }
 
-        match &expr.kind {
-            ExprKind::Call(fn_expr, _args) => {
-                let found_function = SUGGESTION_FUNCTIONS
-                    .iter()
-                    .any(|func_path| match_function_call(self.cx, fn_expr, func_path).is_some());
-                if found_function {
-                    // These functions are all multi part suggestions
-                    self.add_single_span_suggestion();
-                }
-            },
-            ExprKind::MethodCall(path, recv, _, _arg_span) => {
-                let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(recv));
-                if match_type(self.cx, self_ty, &paths::DIAG) {
-                    let called_method = path.ident.name.as_str().to_string();
-                    for (method_name, is_multi_part) in &SUGGESTION_DIAG_METHODS {
-                        if *method_name == called_method {
-                            if *is_multi_part {
-                                self.add_multi_part_suggestion();
-                            } else {
-                                self.add_single_span_suggestion();
-                            }
-                            break;
+        if let ExprKind::MethodCall(path, recv, _, _arg_span) = &expr.kind {
+            let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(recv));
+            if match_type(self.cx, self_ty, &paths::DIAG) {
+                let called_method = path.ident.name.as_str().to_string();
+                for (method_name, is_multi_part) in &SUGGESTION_DIAG_METHODS {
+                    if *method_name == called_method {
+                        if *is_multi_part {
+                            self.add_multi_part_suggestion();
+                        } else {
+                            self.add_single_span_suggestion();
                         }
+                        break;
                     }
                 }
-            },
-            _ => {},
+            }
         }
 
         intravisit::walk_expr(self, expr);
diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs
index a831234906bfc..228db14d1b7c3 100644
--- a/clippy_lints/src/vec.rs
+++ b/clippy_lints/src/vec.rs
@@ -3,7 +3,7 @@ use std::ops::ControlFlow;
 
 use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
-use clippy_utils::consts::{constant, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::is_copy;
@@ -159,7 +159,7 @@ impl UselessVec {
 
         let snippet = match *vec_args {
             higher::VecArgs::Repeat(elem, len) => {
-                if let Some(Constant::Int(len_constant)) = constant(cx, cx.typeck_results(), len) {
+                if let Some(Constant::Int(len_constant)) = ConstEvalCtxt::new(cx).eval(len) {
                     // vec![ty; N] works when ty is Clone, [ty; N] requires it to be Copy also
                     if !is_copy(cx, cx.typeck_results().expr_ty(elem)) {
                         return;
diff --git a/clippy_lints/src/visibility.rs b/clippy_lints/src/visibility.rs
index 11dcceca7abb1..63f3a5d7f8305 100644
--- a/clippy_lints/src/visibility.rs
+++ b/clippy_lints/src/visibility.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_opt;
 use rustc_ast::ast::{Item, VisibilityKind};
 use rustc_errors::Applicability;
@@ -85,14 +85,19 @@ impl EarlyLintPass for Visibility {
             if **path == kw::SelfLower
                 && let Some(false) = is_from_proc_macro(cx, item.vis.span)
             {
-                span_lint_and_sugg(
+                span_lint_and_then(
                     cx,
                     NEEDLESS_PUB_SELF,
                     item.vis.span,
                     format!("unnecessary `pub({}self)`", if *shorthand { "" } else { "in " }),
-                    "remove it",
-                    String::new(),
-                    Applicability::MachineApplicable,
+                    |diag| {
+                        diag.span_suggestion_hidden(
+                            item.vis.span,
+                            "remove it",
+                            String::new(),
+                            Applicability::MachineApplicable,
+                        );
+                    },
                 );
             }
 
@@ -101,14 +106,20 @@ impl EarlyLintPass for Visibility {
                 && let [.., last] = &*path.segments
                 && let Some(false) = is_from_proc_macro(cx, item.vis.span)
             {
-                span_lint_and_sugg(
+                #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+                span_lint_and_then(
                     cx,
                     PUB_WITHOUT_SHORTHAND,
                     item.vis.span,
                     "usage of `pub` with `in`",
-                    "remove it",
-                    format!("pub({})", last.ident),
-                    Applicability::MachineApplicable,
+                    |diag| {
+                        diag.span_suggestion(
+                            item.vis.span,
+                            "remove it",
+                            format!("pub({})", last.ident),
+                            Applicability::MachineApplicable,
+                        );
+                    },
                 );
             }
 
@@ -116,14 +127,20 @@ impl EarlyLintPass for Visibility {
                 && let [.., last] = &*path.segments
                 && let Some(false) = is_from_proc_macro(cx, item.vis.span)
             {
-                span_lint_and_sugg(
+                #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+                span_lint_and_then(
                     cx,
                     PUB_WITH_SHORTHAND,
                     item.vis.span,
                     "usage of `pub` without `in`",
-                    "add it",
-                    format!("pub(in {})", last.ident),
-                    Applicability::MachineApplicable,
+                    |diag| {
+                        diag.span_suggestion(
+                            item.vis.span,
+                            "add it",
+                            format!("pub(in {})", last.ident),
+                            Applicability::MachineApplicable,
+                        );
+                    },
                 );
             }
         }
diff --git a/clippy_lints/src/zero_div_zero.rs b/clippy_lints/src/zero_div_zero.rs
index 60d8a13d3599d..5eb207a0aedb0 100644
--- a/clippy_lints/src/zero_div_zero.rs
+++ b/clippy_lints/src/zero_div_zero.rs
@@ -1,4 +1,4 @@
-use clippy_utils::consts::{constant_simple, Constant};
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -36,8 +36,9 @@ impl<'tcx> LateLintPass<'tcx> for ZeroDiv {
             // TODO - constant_simple does not fold many operations involving floats.
             // That's probably fine for this lint - it's pretty unlikely that someone would
             // do something like 0.0/(2.0 - 2.0), but it would be nice to warn on that case too.
-            && let Some(lhs_value) = constant_simple(cx, cx.typeck_results(), left)
-            && let Some(rhs_value) = constant_simple(cx, cx.typeck_results(), right)
+            && let ecx = ConstEvalCtxt::new(cx)
+            && let Some(lhs_value) = ecx.eval_simple(left)
+            && let Some(rhs_value) = ecx.eval_simple(right)
             // FIXME(f16_f128): add these types when eq is available on all platforms
             && (Constant::F32(0.0) == lhs_value || Constant::F64(0.0) == lhs_value)
             && (Constant::F32(0.0) == rhs_value || Constant::F64(0.0) == rhs_value)
diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml
index f0c64fdd573ca..9fefd94d339be 100644
--- a/clippy_utils/Cargo.toml
+++ b/clippy_utils/Cargo.toml
@@ -12,9 +12,6 @@ rustc-semver = "1.1"
 # FIXME(f16_f128): remove when no longer needed for parsing
 rustc_apfloat = "0.2.0"
 
-[features]
-deny-warnings = ["clippy_config/deny-warnings"]
-
 [package.metadata.rust-analyzer]
 # This crate uses #[feature(rustc_private)]
 rustc_private = true
diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs
index de6ccfe476fb0..e907e4058e5a2 100644
--- a/clippy_utils/src/consts.rs
+++ b/clippy_utils/src/consts.rs
@@ -14,12 +14,13 @@ use rustc_lexer::tokenize;
 use rustc_lint::LateContext;
 use rustc_middle::mir::interpret::{alloc_range, Scalar};
 use rustc_middle::mir::ConstValue;
-use rustc_middle::ty::{self, EarlyBinder, FloatTy, GenericArgsRef, IntTy, List, ScalarInt, Ty, TyCtxt, UintTy};
+use rustc_middle::ty::{self, FloatTy, IntTy, ParamEnv, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy};
 use rustc_middle::{bug, mir, span_bug};
 use rustc_span::def_id::DefId;
-use rustc_span::symbol::{Ident, Symbol};
+use rustc_span::symbol::Ident;
 use rustc_span::{sym, SyntaxContext};
 use rustc_target::abi::Size;
+use std::cell::Cell;
 use std::cmp::Ordering;
 use std::hash::{Hash, Hasher};
 use std::iter;
@@ -263,10 +264,10 @@ impl<'tcx> Constant<'tcx> {
     }
 
     /// Returns the integer value or `None` if `self` or `val_type` is not integer type.
-    pub fn int_value(&self, cx: &LateContext<'_>, val_type: Ty<'_>) -> Option<FullInt> {
+    pub fn int_value(&self, tcx: TyCtxt<'_>, val_type: Ty<'_>) -> Option<FullInt> {
         if let Constant::Int(const_int) = *self {
             match *val_type.kind() {
-                ty::Int(ity) => Some(FullInt::S(sext(cx.tcx, const_int, ity))),
+                ty::Int(ity) => Some(FullInt::S(sext(tcx, const_int, ity))),
                 ty::Uint(_) => Some(FullInt::U(const_int)),
                 _ => None,
             }
@@ -322,6 +323,7 @@ pub fn lit_to_mir_constant<'tcx>(lit: &LitKind, ty: Option<Ty<'tcx>>) -> Constan
 }
 
 /// The source of a constant value.
+#[derive(Clone, Copy)]
 pub enum ConstantSource {
     /// The value is determined solely from the expression.
     Local,
@@ -331,54 +333,11 @@ pub enum ConstantSource {
     CoreConstant,
 }
 impl ConstantSource {
-    pub fn is_local(&self) -> bool {
+    pub fn is_local(self) -> bool {
         matches!(self, Self::Local)
     }
 }
 
-/// Attempts to check whether the expression is a constant representing an empty slice, str, array,
-/// etc…
-pub fn constant_is_empty(lcx: &LateContext<'_>, e: &Expr<'_>) -> Option<bool> {
-    ConstEvalLateContext::new(lcx, lcx.typeck_results()).expr_is_empty(e)
-}
-
-/// Attempts to evaluate the expression as a constant.
-pub fn constant<'tcx>(
-    lcx: &LateContext<'tcx>,
-    typeck_results: &ty::TypeckResults<'tcx>,
-    e: &Expr<'_>,
-) -> Option<Constant<'tcx>> {
-    ConstEvalLateContext::new(lcx, typeck_results).expr(e)
-}
-
-/// Attempts to evaluate the expression as a constant.
-pub fn constant_with_source<'tcx>(
-    lcx: &LateContext<'tcx>,
-    typeck_results: &ty::TypeckResults<'tcx>,
-    e: &Expr<'_>,
-) -> Option<(Constant<'tcx>, ConstantSource)> {
-    let mut ctxt = ConstEvalLateContext::new(lcx, typeck_results);
-    let res = ctxt.expr(e);
-    res.map(|x| (x, ctxt.source))
-}
-
-/// Attempts to evaluate an expression only if its value is not dependent on other items.
-pub fn constant_simple<'tcx>(
-    lcx: &LateContext<'tcx>,
-    typeck_results: &ty::TypeckResults<'tcx>,
-    e: &Expr<'_>,
-) -> Option<Constant<'tcx>> {
-    constant_with_source(lcx, typeck_results, e).and_then(|(c, s)| s.is_local().then_some(c))
-}
-
-pub fn constant_full_int<'tcx>(
-    lcx: &LateContext<'tcx>,
-    typeck_results: &ty::TypeckResults<'tcx>,
-    e: &Expr<'_>,
-) -> Option<FullInt> {
-    constant_simple(lcx, typeck_results, e)?.int_value(lcx, typeck_results.expr_ty(e))
-}
-
 #[derive(Copy, Clone, Debug, Eq)]
 pub enum FullInt {
     S(i128),
@@ -417,44 +376,87 @@ impl Ord for FullInt {
     }
 }
 
-pub struct ConstEvalLateContext<'a, 'tcx> {
-    lcx: &'a LateContext<'tcx>,
-    typeck_results: &'a ty::TypeckResults<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    source: ConstantSource,
-    args: GenericArgsRef<'tcx>,
+/// The context required to evaluate a constant expression.
+///
+/// This is currently limited to constant folding and reading the value of named constants.
+pub struct ConstEvalCtxt<'tcx> {
+    tcx: TyCtxt<'tcx>,
+    param_env: ParamEnv<'tcx>,
+    typeck: &'tcx TypeckResults<'tcx>,
+    source: Cell<ConstantSource>,
 }
 
-impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
-    pub fn new(lcx: &'a LateContext<'tcx>, typeck_results: &'a ty::TypeckResults<'tcx>) -> Self {
+impl<'tcx> ConstEvalCtxt<'tcx> {
+    /// Creates the evaluation context from the lint context. This requires the lint context to be
+    /// in a body (i.e. `cx.enclosing_body.is_some()`).
+    pub fn new(cx: &LateContext<'tcx>) -> Self {
+        Self {
+            tcx: cx.tcx,
+            param_env: cx.param_env,
+            typeck: cx.typeck_results(),
+            source: Cell::new(ConstantSource::Local),
+        }
+    }
+
+    /// Creates an evaluation context.
+    pub fn with_env(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, typeck: &'tcx TypeckResults<'tcx>) -> Self {
         Self {
-            lcx,
-            typeck_results,
-            param_env: lcx.param_env,
-            source: ConstantSource::Local,
-            args: List::empty(),
+            tcx,
+            param_env,
+            typeck,
+            source: Cell::new(ConstantSource::Local),
+        }
+    }
+
+    /// Attempts to evaluate the expression and returns both the value and whether it's dependant on
+    /// other items.
+    pub fn eval_with_source(&self, e: &Expr<'_>) -> Option<(Constant<'tcx>, ConstantSource)> {
+        self.source.set(ConstantSource::Local);
+        self.expr(e).map(|c| (c, self.source.get()))
+    }
+
+    /// Attempts to evaluate the expression.
+    pub fn eval(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
+        self.expr(e)
+    }
+
+    /// Attempts to evaluate the expression without accessing other items.
+    pub fn eval_simple(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
+        match self.eval_with_source(e) {
+            Some((x, ConstantSource::Local)) => Some(x),
+            _ => None,
+        }
+    }
+
+    /// Attempts to evaluate the expression as an integer without accessing other items.
+    pub fn eval_full_int(&self, e: &Expr<'_>) -> Option<FullInt> {
+        match self.eval_with_source(e) {
+            Some((x, ConstantSource::Local)) => x.int_value(self.tcx, self.typeck.expr_ty(e)),
+            _ => None,
         }
     }
 
     /// Simple constant folding: Insert an expression, get a constant or none.
-    pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
+    fn expr(&self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
         match e.kind {
-            ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.lcx.tcx.hir().body(body).value),
+            ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr(self.tcx.hir().body(body).value),
             ExprKind::DropTemps(e) => self.expr(e),
             ExprKind::Path(ref qpath) => {
-                let is_core_crate = if let Some(def_id) = self.lcx.qpath_res(qpath, e.hir_id()).opt_def_id() {
-                    self.lcx.tcx.crate_name(def_id.krate) == sym::core
+                let is_core_crate = if let Some(def_id) = self.typeck.qpath_res(qpath, e.hir_id()).opt_def_id() {
+                    self.tcx.crate_name(def_id.krate) == sym::core
                 } else {
                     false
                 };
-                self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| {
-                    let result = mir_to_const(this.lcx, result)?;
+                self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| {
+                    let result = mir_to_const(self_.tcx, result)?;
                     // If source is already Constant we wouldn't want to override it with CoreConstant
-                    this.source = if is_core_crate && !matches!(this.source, ConstantSource::Constant) {
-                        ConstantSource::CoreConstant
-                    } else {
-                        ConstantSource::Constant
-                    };
+                    self_.source.set(
+                        if is_core_crate && !matches!(self_.source.get(), ConstantSource::Constant) {
+                            ConstantSource::CoreConstant
+                        } else {
+                            ConstantSource::Constant
+                        },
+                    );
                     Some(result)
                 })
             },
@@ -463,21 +465,21 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
                 if is_direct_expn_of(e.span, "cfg").is_some() {
                     None
                 } else {
-                    Some(lit_to_mir_constant(&lit.node, self.typeck_results.expr_ty_opt(e)))
+                    Some(lit_to_mir_constant(&lit.node, self.typeck.expr_ty_opt(e)))
                 }
             },
             ExprKind::Array(vec) => self.multi(vec).map(Constant::Vec),
             ExprKind::Tup(tup) => self.multi(tup).map(Constant::Tuple),
             ExprKind::Repeat(value, _) => {
-                let n = match self.typeck_results.expr_ty(e).kind() {
-                    ty::Array(_, n) => n.try_eval_target_usize(self.lcx.tcx, self.lcx.param_env)?,
+                let n = match self.typeck.expr_ty(e).kind() {
+                    ty::Array(_, n) => n.try_eval_target_usize(self.tcx, self.param_env)?,
                     _ => span_bug!(e.span, "typeck error"),
                 };
                 self.expr(value).map(|v| Constant::Repeat(Box::new(v), n))
             },
             ExprKind::Unary(op, operand) => self.expr(operand).and_then(|o| match op {
-                UnOp::Not => self.constant_not(&o, self.typeck_results.expr_ty(e)),
-                UnOp::Neg => self.constant_negate(&o, self.typeck_results.expr_ty(e)),
+                UnOp::Not => self.constant_not(&o, self.typeck.expr_ty(e)),
+                UnOp::Neg => self.constant_negate(&o, self.typeck.expr_ty(e)),
                 UnOp::Deref => Some(if let Constant::Ref(r) = o { *r } else { o }),
             }),
             ExprKind::If(cond, then, ref otherwise) => self.ifthenelse(cond, then, *otherwise),
@@ -486,21 +488,16 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
                 // We only handle a few const functions for now.
                 if args.is_empty()
                     && let ExprKind::Path(qpath) = &callee.kind
-                    && let res = self.typeck_results.qpath_res(qpath, callee.hir_id)
-                    && let Some(def_id) = res.opt_def_id()
-                    && let def_path = self.lcx.get_def_path(def_id)
-                    && let def_path = def_path.iter().take(4).map(Symbol::as_str).collect::<Vec<_>>()
-                    && let ["core", "num", int_impl, "max_value"] = *def_path
+                    && let Some(did) = self.typeck.qpath_res(qpath, callee.hir_id).opt_def_id()
                 {
-                    let value = match int_impl {
-                        "<impl i8>" => i8::MAX as u128,
-                        "<impl i16>" => i16::MAX as u128,
-                        "<impl i32>" => i32::MAX as u128,
-                        "<impl i64>" => i64::MAX as u128,
-                        "<impl i128>" => i128::MAX as u128,
-                        _ => return None,
-                    };
-                    Some(Constant::Int(value))
+                    match self.tcx.get_diagnostic_name(did) {
+                        Some(sym::i8_legacy_fn_max_value) => Some(Constant::Int(i8::MAX as u128)),
+                        Some(sym::i16_legacy_fn_max_value) => Some(Constant::Int(i16::MAX as u128)),
+                        Some(sym::i32_legacy_fn_max_value) => Some(Constant::Int(i32::MAX as u128)),
+                        Some(sym::i64_legacy_fn_max_value) => Some(Constant::Int(i64::MAX as u128)),
+                        Some(sym::i128_legacy_fn_max_value) => Some(Constant::Int(i128::MAX as u128)),
+                        _ => None,
+                    }
                 } else {
                     None
                 }
@@ -512,9 +509,9 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
                 if let Some(Constant::Adt(constant)) = &self.expr(local_expr)
                     && let ty::Adt(adt_def, _) = constant.ty().kind()
                     && adt_def.is_struct()
-                    && let Some(desired_field) = field_of_struct(*adt_def, self.lcx, *constant, field)
+                    && let Some(desired_field) = field_of_struct(*adt_def, self.tcx, *constant, field)
                 {
-                    mir_to_const(self.lcx, desired_field)
+                    mir_to_const(self.tcx, desired_field)
                 } else {
                     result
                 }
@@ -526,21 +523,21 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
     /// Simple constant folding to determine if an expression is an empty slice, str, array, …
     /// `None` will be returned if the constness cannot be determined, or if the resolution
     /// leaves the local crate.
-    pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option<bool> {
+    pub fn eval_is_empty(&self, e: &Expr<'_>) -> Option<bool> {
         match e.kind {
-            ExprKind::ConstBlock(ConstBlock { body, .. }) => self.expr_is_empty(self.lcx.tcx.hir().body(body).value),
-            ExprKind::DropTemps(e) => self.expr_is_empty(e),
+            ExprKind::ConstBlock(ConstBlock { body, .. }) => self.eval_is_empty(self.tcx.hir().body(body).value),
+            ExprKind::DropTemps(e) => self.eval_is_empty(e),
             ExprKind::Path(ref qpath) => {
                 if !self
-                    .typeck_results
+                    .typeck
                     .qpath_res(qpath, e.hir_id)
                     .opt_def_id()
                     .is_some_and(DefId::is_local)
                 {
                     return None;
                 }
-                self.fetch_path_and_apply(qpath, e.hir_id, self.typeck_results.expr_ty(e), |this, result| {
-                    mir_is_empty(this.lcx, result)
+                self.fetch_path_and_apply(qpath, e.hir_id, self.typeck.expr_ty(e), |self_, result| {
+                    mir_is_empty(self_.tcx, result)
                 })
             },
             ExprKind::Lit(lit) => {
@@ -556,8 +553,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
             },
             ExprKind::Array(vec) => self.multi(vec).map(|v| v.is_empty()),
             ExprKind::Repeat(..) => {
-                if let ty::Array(_, n) = self.typeck_results.expr_ty(e).kind() {
-                    Some(n.try_eval_target_usize(self.lcx.tcx, self.lcx.param_env)? == 0)
+                if let ty::Array(_, n) = self.typeck.expr_ty(e).kind() {
+                    Some(n.try_eval_target_usize(self.tcx, self.param_env)? == 0)
                 } else {
                     span_bug!(e.span, "typeck error");
                 }
@@ -574,8 +571,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
             Int(value) => {
                 let value = !value;
                 match *ty.kind() {
-                    ty::Int(ity) => Some(Int(unsext(self.lcx.tcx, value as i128, ity))),
-                    ty::Uint(ity) => Some(Int(clip(self.lcx.tcx, value, ity))),
+                    ty::Int(ity) => Some(Int(unsext(self.tcx, value as i128, ity))),
+                    ty::Uint(ity) => Some(Int(clip(self.tcx, value, ity))),
                     _ => None,
                 }
             },
@@ -590,7 +587,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
                 let ty::Int(ity) = *ty.kind() else { return None };
                 let (min, _) = ity.min_max()?;
                 // sign extend
-                let value = sext(self.lcx.tcx, value, ity);
+                let value = sext(self.tcx, value, ity);
 
                 // Applying unary - to the most negative value of any signed integer type panics.
                 if value == min {
@@ -599,7 +596,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
 
                 let value = value.checked_neg()?;
                 // clear unused bits
-                Some(Int(unsext(self.lcx.tcx, value, ity)))
+                Some(Int(unsext(self.tcx, value, ity)))
             },
             F32(f) => Some(F32(-f)),
             F64(f) => Some(F64(-f)),
@@ -609,21 +606,21 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
 
     /// Create `Some(Vec![..])` of all constants, unless there is any
     /// non-constant part.
-    fn multi(&mut self, vec: &[Expr<'_>]) -> Option<Vec<Constant<'tcx>>> {
+    fn multi(&self, vec: &[Expr<'_>]) -> Option<Vec<Constant<'tcx>>> {
         vec.iter().map(|elem| self.expr(elem)).collect::<Option<_>>()
     }
 
     /// Lookup a possibly constant expression from an `ExprKind::Path` and apply a function on it.
-    fn fetch_path_and_apply<T, F>(&mut self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option<T>
+    fn fetch_path_and_apply<T, F>(&self, qpath: &QPath<'_>, id: HirId, ty: Ty<'tcx>, f: F) -> Option<T>
     where
-        F: FnOnce(&mut Self, mir::Const<'tcx>) -> Option<T>,
+        F: FnOnce(&Self, mir::Const<'tcx>) -> Option<T>,
     {
-        let res = self.typeck_results.qpath_res(qpath, id);
+        let res = self.typeck.qpath_res(qpath, id);
         match res {
             Res::Def(DefKind::Const | DefKind::AssocConst, def_id) => {
                 // Check if this constant is based on `cfg!(..)`,
                 // which is NOT constant for our purposes.
-                if let Some(node) = self.lcx.tcx.hir().get_if_local(def_id)
+                if let Some(node) = self.tcx.hir().get_if_local(def_id)
                     && let Node::Item(Item {
                         kind: ItemKind::Const(.., body_id),
                         ..
@@ -632,20 +629,14 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
                         kind: ExprKind::Lit(_),
                         span,
                         ..
-                    }) = self.lcx.tcx.hir_node(body_id.hir_id)
+                    }) = self.tcx.hir_node(body_id.hir_id)
                     && is_direct_expn_of(*span, "cfg").is_some()
                 {
                     return None;
                 }
 
-                let args = self.typeck_results.node_args(id);
-                let args = if self.args.is_empty() {
-                    args
-                } else {
-                    EarlyBinder::bind(args).instantiate(self.lcx.tcx, self.args)
-                };
+                let args = self.typeck.node_args(id);
                 let result = self
-                    .lcx
                     .tcx
                     .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), qpath.span())
                     .ok()
@@ -656,7 +647,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
         }
     }
 
-    fn index(&mut self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant<'tcx>> {
+    fn index(&self, lhs: &'_ Expr<'_>, index: &'_ Expr<'_>) -> Option<Constant<'tcx>> {
         let lhs = self.expr(lhs);
         let index = self.expr(index);
 
@@ -685,8 +676,8 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
         }
     }
 
-    /// A block can only yield a constant if it only has one constant expression.
-    fn block(&mut self, block: &Block<'_>) -> Option<Constant<'tcx>> {
+    /// A block can only yield a constant if it has exactly one constant expression.
+    fn block(&self, block: &Block<'_>) -> Option<Constant<'tcx>> {
         if block.stmts.is_empty()
             && let Some(expr) = block.expr
         {
@@ -696,7 +687,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
                 if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt)
                     && let expr_lo = expr_span.lo()
                     && expr_lo >= span.lo
-                    && let Some(src) = (span.lo..expr_lo).get_source_text(self.lcx)
+                    && let Some(src) = (span.lo..expr_lo).get_source_text(&self.tcx)
                     && let Some(src) = src.as_str()
                 {
                     use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace};
@@ -705,11 +696,11 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
                         .filter(|t| !matches!(t, Whitespace | LineComment { .. } | BlockComment { .. } | Semi))
                         .eq([OpenBrace])
                     {
-                        self.source = ConstantSource::Constant;
+                        self.source.set(ConstantSource::Constant);
                     }
                 } else {
                     // Unable to access the source. Assume a non-local dependency.
-                    self.source = ConstantSource::Constant;
+                    self.source.set(ConstantSource::Constant);
                 }
             }
 
@@ -719,7 +710,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
         }
     }
 
-    fn ifthenelse(&mut self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant<'tcx>> {
+    fn ifthenelse(&self, cond: &Expr<'_>, then: &Expr<'_>, otherwise: Option<&Expr<'_>>) -> Option<Constant<'tcx>> {
         if let Some(Constant::Bool(b)) = self.expr(cond) {
             if b {
                 self.expr(then)
@@ -731,16 +722,16 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
         }
     }
 
-    fn binop(&mut self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant<'tcx>> {
+    fn binop(&self, op: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> Option<Constant<'tcx>> {
         let l = self.expr(left)?;
         let r = self.expr(right);
         match (l, r) {
-            (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck_results.expr_ty_opt(left)?.kind() {
+            (Constant::Int(l), Some(Constant::Int(r))) => match *self.typeck.expr_ty_opt(left)?.kind() {
                 ty::Int(ity) => {
                     let (ty_min_value, _) = ity.min_max()?;
                     let bits = ity.bits();
-                    let l = sext(self.lcx.tcx, l, ity);
-                    let r = sext(self.lcx.tcx, r, ity);
+                    let l = sext(self.tcx, l, ity);
+                    let r = sext(self.tcx, r, ity);
 
                     // Using / or %, where the left-hand argument is the smallest integer of a signed integer type and
                     // the right-hand argument is -1 always panics, even with overflow-checks disabled
@@ -751,7 +742,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
                         return None;
                     }
 
-                    let zext = |n: i128| Constant::Int(unsext(self.lcx.tcx, n, ity));
+                    let zext = |n: i128| Constant::Int(unsext(self.tcx, n, ity));
                     match op.node {
                         // When +, * or binary - create a value greater than the maximum value, or less than
                         // the minimum value that can be stored, it panics.
@@ -845,7 +836,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
     }
 }
 
-pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> {
+pub fn mir_to_const<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option<Constant<'tcx>> {
     let mir::Const::Val(val, _) = result else {
         // We only work on evaluated consts.
         return None;
@@ -863,13 +854,13 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) ->
             _ => None,
         },
         (_, ty::Ref(_, inner_ty, _)) if matches!(inner_ty.kind(), ty::Str) => {
-            let data = val.try_get_slice_bytes_for_diagnostics(lcx.tcx)?;
+            let data = val.try_get_slice_bytes_for_diagnostics(tcx)?;
             String::from_utf8(data.to_owned()).ok().map(Constant::Str)
         },
         (_, ty::Adt(adt_def, _)) if adt_def.is_struct() => Some(Constant::Adt(result)),
         (ConstValue::Indirect { alloc_id, offset }, ty::Array(sub_type, len)) => {
-            let alloc = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner();
-            let len = len.try_to_target_usize(lcx.tcx)?;
+            let alloc = tcx.global_alloc(alloc_id).unwrap_memory().inner();
+            let len = len.try_to_target_usize(tcx)?;
             let ty::Float(flt) = sub_type.kind() else {
                 return None;
             };
@@ -877,7 +868,7 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) ->
             let mut res = Vec::new();
             for idx in 0..len {
                 let range = alloc_range(offset + size * idx, size);
-                let val = alloc.read_scalar(&lcx.tcx, range, /* read_provenance */ false).ok()?;
+                let val = alloc.read_scalar(&tcx, range, /* read_provenance */ false).ok()?;
                 res.push(match flt {
                     FloatTy::F16 => Constant::F16(f16::from_bits(val.to_u16().ok()?)),
                     FloatTy::F32 => Constant::F32(f32::from_bits(val.to_u32().ok()?)),
@@ -891,7 +882,7 @@ pub fn mir_to_const<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) ->
     }
 }
 
-fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Option<bool> {
+fn mir_is_empty<'tcx>(tcx: TyCtxt<'tcx>, result: mir::Const<'tcx>) -> Option<bool> {
     let mir::Const::Val(val, _) = result else {
         // We only work on evaluated consts.
         return None;
@@ -902,26 +893,26 @@ fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Opti
                 if let ConstValue::Indirect { alloc_id, offset } = val {
                     // Get the length from the slice, using the same formula as
                     // [`ConstValue::try_get_slice_bytes_for_diagnostics`].
-                    let a = lcx.tcx.global_alloc(alloc_id).unwrap_memory().inner();
-                    let ptr_size = lcx.tcx.data_layout.pointer_size;
+                    let a = tcx.global_alloc(alloc_id).unwrap_memory().inner();
+                    let ptr_size = tcx.data_layout.pointer_size;
                     if a.size() < offset + 2 * ptr_size {
                         // (partially) dangling reference
                         return None;
                     }
                     let len = a
-                        .read_scalar(&lcx.tcx, alloc_range(offset + ptr_size, ptr_size), false)
+                        .read_scalar(&tcx, alloc_range(offset + ptr_size, ptr_size), false)
                         .ok()?
-                        .to_target_usize(&lcx.tcx)
+                        .to_target_usize(&tcx)
                         .ok()?;
                     Some(len == 0)
                 } else {
                     None
                 }
             },
-            ty::Array(_, len) => Some(len.try_to_target_usize(lcx.tcx)? == 0),
+            ty::Array(_, len) => Some(len.try_to_target_usize(tcx)? == 0),
             _ => None,
         },
-        (ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(lcx.tcx)? == 0),
+        (ConstValue::Indirect { .. }, ty::Array(_, len)) => Some(len.try_to_target_usize(tcx)? == 0),
         (ConstValue::ZeroSized, _) => Some(true),
         _ => None,
     }
@@ -929,12 +920,12 @@ fn mir_is_empty<'tcx>(lcx: &LateContext<'tcx>, result: mir::Const<'tcx>) -> Opti
 
 fn field_of_struct<'tcx>(
     adt_def: ty::AdtDef<'tcx>,
-    lcx: &LateContext<'tcx>,
+    tcx: TyCtxt<'tcx>,
     result: mir::Const<'tcx>,
     field: &Ident,
 ) -> Option<mir::Const<'tcx>> {
     if let mir::Const::Val(result, ty) = result
-        && let Some(dc) = lcx.tcx.try_destructure_mir_constant_for_user_output(result, ty)
+        && let Some(dc) = tcx.try_destructure_mir_constant_for_user_output(result, ty)
         && let Some(dc_variant) = dc.variant
         && let Some(variant) = adt_def.variants().get(dc_variant)
         && let Some(field_idx) = variant.fields.iter().position(|el| el.name == field.name)
diff --git a/clippy_utils/src/diagnostics.rs b/clippy_utils/src/diagnostics.rs
index 0641d37cd9a67..4877fb65d37d7 100644
--- a/clippy_utils/src/diagnostics.rs
+++ b/clippy_utils/src/diagnostics.rs
@@ -330,32 +330,3 @@ pub fn span_lint_and_sugg<T: LintContext>(
         diag.span_suggestion(sp, help.into(), sugg, applicability);
     });
 }
-
-/// Create a suggestion made from several `span → replacement`.
-///
-/// Note: in the JSON format (used by `compiletest_rs`), the help message will
-/// appear once per
-/// replacement. In human-readable format though, it only appears once before
-/// the whole suggestion.
-pub fn multispan_sugg<I>(diag: &mut Diag<'_, ()>, help_msg: impl Into<SubdiagMessage>, sugg: I)
-where
-    I: IntoIterator<Item = (Span, String)>,
-{
-    multispan_sugg_with_applicability(diag, help_msg, Applicability::Unspecified, sugg);
-}
-
-/// Create a suggestion made from several `span → replacement`.
-///
-/// rustfix currently doesn't support the automatic application of suggestions with
-/// multiple spans. This is tracked in issue [rustfix#141](https://github.com/rust-lang/rustfix/issues/141).
-/// Suggestions with multiple spans will be silently ignored.
-pub fn multispan_sugg_with_applicability<I>(
-    diag: &mut Diag<'_, ()>,
-    help_msg: impl Into<SubdiagMessage>,
-    applicability: Applicability,
-    sugg: I,
-) where
-    I: IntoIterator<Item = (Span, String)>,
-{
-    diag.multipart_suggestion(help_msg.into(), sugg.into_iter().collect(), applicability);
-}
diff --git a/clippy_utils/src/eager_or_lazy.rs b/clippy_utils/src/eager_or_lazy.rs
index 6c40029a9de70..a6dd12a28c5bc 100644
--- a/clippy_utils/src/eager_or_lazy.rs
+++ b/clippy_utils/src/eager_or_lazy.rs
@@ -9,7 +9,7 @@
 //!  - or-fun-call
 //!  - option-if-let-else
 
-use crate::consts::{constant, FullInt};
+use crate::consts::{ConstEvalCtxt, FullInt};
 use crate::ty::{all_predicates_of, is_copy};
 use crate::visitors::is_const_evaluatable;
 use rustc_hir::def::{DefKind, Res};
@@ -206,7 +206,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
                 },
 
                 // `-i32::MIN` panics with overflow checks
-                ExprKind::Unary(UnOp::Neg, right) if constant(self.cx, self.cx.typeck_results(), right).is_none() => {
+                ExprKind::Unary(UnOp::Neg, right) if ConstEvalCtxt::new(self.cx).eval(right).is_none() => {
                     self.eagerness |= NoChange;
                 },
 
@@ -232,7 +232,7 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
                 // Thus, we would realistically only delay the lint.
                 ExprKind::Binary(op, _, right)
                     if matches!(op.node, BinOpKind::Shl | BinOpKind::Shr)
-                        && constant(self.cx, self.cx.typeck_results(), right).is_none() =>
+                        && ConstEvalCtxt::new(self.cx).eval(right).is_none() =>
                 {
                     self.eagerness |= NoChange;
                 },
@@ -240,9 +240,9 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
                 ExprKind::Binary(op, left, right)
                     if matches!(op.node, BinOpKind::Div | BinOpKind::Rem)
                         && let right_ty = self.cx.typeck_results().expr_ty(right)
-                        && let left = constant(self.cx, self.cx.typeck_results(), left)
-                        && let right = constant(self.cx, self.cx.typeck_results(), right)
-                            .and_then(|c| c.int_value(self.cx, right_ty))
+                        && let ecx = ConstEvalCtxt::new(self.cx)
+                        && let left = ecx.eval(left)
+                        && let right = ecx.eval(right).and_then(|c| c.int_value(self.cx.tcx, right_ty))
                         && matches!(
                             (left, right),
                             // `1 / x`: x might be zero
@@ -261,8 +261,8 @@ fn expr_eagerness<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> EagernessS
                 ExprKind::Binary(op, left, right)
                     if matches!(op.node, BinOpKind::Add | BinOpKind::Sub | BinOpKind::Mul)
                         && !self.cx.typeck_results().expr_ty(e).is_floating_point()
-                        && (constant(self.cx, self.cx.typeck_results(), left).is_none()
-                            || constant(self.cx, self.cx.typeck_results(), right).is_none()) =>
+                        && let ecx = ConstEvalCtxt::new(self.cx)
+                        && (ecx.eval(left).is_none() || ecx.eval(right).is_none()) =>
                 {
                     self.eagerness |= NoChange;
                 },
diff --git a/clippy_utils/src/higher.rs b/clippy_utils/src/higher.rs
index 277ba8427e054..8970b4d122984 100644
--- a/clippy_utils/src/higher.rs
+++ b/clippy_utils/src/higher.rs
@@ -2,7 +2,7 @@
 
 #![deny(clippy::missing_docs_in_private_items)]
 
-use crate::consts::{constant_simple, Constant};
+use crate::consts::{ConstEvalCtxt, Constant};
 use crate::ty::is_type_diagnostic_item;
 use crate::{is_expn_of, match_def_path, paths};
 
@@ -25,6 +25,8 @@ pub struct ForLoop<'tcx> {
     pub loop_id: HirId,
     /// entire `for` loop span
     pub span: Span,
+    /// label
+    pub label: Option<ast::Label>,
 }
 
 impl<'tcx> ForLoop<'tcx> {
@@ -33,7 +35,7 @@ impl<'tcx> ForLoop<'tcx> {
         if let ExprKind::DropTemps(e) = expr.kind
             && let ExprKind::Match(iterexpr, [arm], MatchSource::ForLoopDesugar) = e.kind
             && let ExprKind::Call(_, [arg]) = iterexpr.kind
-            && let ExprKind::Loop(block, ..) = arm.body.kind
+            && let ExprKind::Loop(block, label, ..) = arm.body.kind
             && let [stmt] = block.stmts
             && let hir::StmtKind::Expr(e) = stmt.kind
             && let ExprKind::Match(_, [_, some_arm], _) = e.kind
@@ -45,6 +47,7 @@ impl<'tcx> ForLoop<'tcx> {
                 body: some_arm.body,
                 loop_id: arm.body.hir_id,
                 span: expr.span.ctxt().outer_expn_data().call_site,
+                label,
             });
         }
         None
@@ -367,6 +370,7 @@ pub struct WhileLet<'hir> {
     pub let_expr: &'hir Expr<'hir>,
     /// `while let` loop body
     pub if_then: &'hir Expr<'hir>,
+    pub label: Option<ast::Label>,
     /// `while let PAT = EXPR`
     ///        ^^^^^^^^^^^^^^
     pub let_span: Span,
@@ -399,7 +403,7 @@ impl<'hir> WhileLet<'hir> {
                     }),
                 ..
             },
-            _,
+            label,
             LoopSource::While,
             _,
         ) = expr.kind
@@ -408,6 +412,7 @@ impl<'hir> WhileLet<'hir> {
                 let_pat,
                 let_expr,
                 if_then,
+                label,
                 let_span,
             });
         }
@@ -466,7 +471,7 @@ pub fn get_vec_init_kind<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -
                     return Some(VecInitKind::Default);
                 } else if name.ident.name.as_str() == "with_capacity" {
                     let arg = args.first()?;
-                    return match constant_simple(cx, cx.typeck_results(), arg) {
+                    return match ConstEvalCtxt::new(cx).eval_simple(arg) {
                         Some(Constant::Int(num)) => Some(VecInitKind::WithConstCapacity(num)),
                         _ => Some(VecInitKind::WithExprCapacity(arg.hir_id)),
                     };
diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs
index 28178a61a9323..f325e4eaf1544 100644
--- a/clippy_utils/src/hir_utils.rs
+++ b/clippy_utils/src/hir_utils.rs
@@ -1,4 +1,4 @@
-use crate::consts::constant_simple;
+use crate::consts::ConstEvalCtxt;
 use crate::macros::macro_backtrace;
 use crate::source::{snippet_opt, walk_span_to_context, SpanRange, SpanRangeExt};
 use crate::tokenize_with_text;
@@ -255,8 +255,8 @@ impl HirEqInterExpr<'_, '_, '_> {
         if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results
             && typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right)
             && let (Some(l), Some(r)) = (
-                constant_simple(self.inner.cx, typeck_lhs, left),
-                constant_simple(self.inner.cx, typeck_rhs, right),
+                ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.param_env, typeck_lhs).eval_simple(left),
+                ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.param_env, typeck_rhs).eval_simple(right),
             )
             && l == r
         {
@@ -714,9 +714,9 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
 
     #[expect(clippy::too_many_lines)]
     pub fn hash_expr(&mut self, e: &Expr<'_>) {
-        let simple_const = self
-            .maybe_typeck_results
-            .and_then(|typeck_results| constant_simple(self.cx, typeck_results, e));
+        let simple_const = self.maybe_typeck_results.and_then(|typeck_results| {
+            ConstEvalCtxt::with_env(self.cx.tcx, self.cx.param_env, typeck_results).eval_simple(e)
+        });
 
         // const hashing may result in the same hash as some unrelated node, so add a sort of
         // discriminant depending on which path we're choosing next
diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index 1d5f1a2a2bb13..af74e4b67c162 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -10,7 +10,6 @@
 #![feature(assert_matches)]
 #![feature(unwrap_infallible)]
 #![recursion_limit = "512"]
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![allow(
     clippy::missing_errors_doc,
     clippy::missing_panics_doc,
@@ -126,7 +125,7 @@ use rustc_span::{sym, Span};
 use rustc_target::abi::Integer;
 use visitors::Visitable;
 
-use crate::consts::{constant, mir_to_const, Constant};
+use crate::consts::{mir_to_const, ConstEvalCtxt, Constant};
 use crate::higher::Range;
 use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
 use crate::visitors::for_each_expr_without_closures;
@@ -211,20 +210,24 @@ pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
     false
 }
 
-/// Returns `true` if the given `HirId` is inside a constant context.
+/// Checks if we are currently in a const context (e.g. `const fn`, `static`/`const` initializer).
 ///
-/// This is the same as `is_inside_always_const_context`, but also includes
-/// `const fn`.
+/// The current context is determined based on the current body which is set before calling a lint's
+/// entry point (any function on `LateLintPass`). If you need to check in a different context use
+/// `tcx.hir().is_inside_const_context(_)`.
 ///
-/// # Example
-///
-/// ```rust,ignore
-/// if in_constant(cx, expr.hir_id) {
-///     // Do something
-/// }
-/// ```
-pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
-    cx.tcx.hir().is_inside_const_context(id)
+/// Do not call this unless the `LateContext` has an enclosing body. For release build this case
+/// will safely return `false`, but debug builds will ICE. Note that `check_expr`, `check_block`,
+/// `check_pat` and a few other entry points will always have an enclosing body. Some entry points
+/// like `check_path` or `check_ty` may or may not have one.
+pub fn is_in_const_context(cx: &LateContext<'_>) -> bool {
+    debug_assert!(cx.enclosing_body.is_some(), "`LateContext` has no enclosing body");
+    cx.enclosing_body.is_some_and(|id| {
+        cx.tcx
+            .hir()
+            .body_const_context(cx.tcx.hir().body_owner_def_id(id))
+            .is_some()
+    })
 }
 
 /// Returns `true` if the given `HirId` is inside an always constant context.
@@ -589,9 +592,8 @@ fn find_primitive_impls<'tcx>(tcx: TyCtxt<'tcx>, name: &str) -> impl Iterator<It
         "u128" => SimplifiedType::Uint(UintTy::U128),
         "f32" => SimplifiedType::Float(FloatTy::F32),
         "f64" => SimplifiedType::Float(FloatTy::F64),
-        #[allow(trivial_casts)]
         _ => {
-            return Result::<_, rustc_errors::ErrorGuaranteed>::Ok(&[] as &[_])
+            return Result::<&[_], rustc_errors::ErrorGuaranteed>::Ok(&[])
                 .into_iter()
                 .flatten()
                 .copied();
@@ -1579,8 +1581,8 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
             if let rustc_ty::Adt(_, subst) = ty.kind()
                 && let bnd_ty = subst.type_at(0)
                 && let Some(min_val) = bnd_ty.numeric_min_val(cx.tcx)
-                && let Some(min_const) = mir_to_const(cx, Const::from_ty_const(min_val, bnd_ty, cx.tcx))
-                && let Some(start_const) = constant(cx, cx.typeck_results(), start)
+                && let Some(min_const) = mir_to_const(cx.tcx, Const::from_ty_const(min_val, bnd_ty, cx.tcx))
+                && let Some(start_const) = ConstEvalCtxt::new(cx).eval(start)
             {
                 start_const == min_const
             } else {
@@ -1592,8 +1594,8 @@ pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Opti
                 if let rustc_ty::Adt(_, subst) = ty.kind()
                     && let bnd_ty = subst.type_at(0)
                     && let Some(max_val) = bnd_ty.numeric_max_val(cx.tcx)
-                    && let Some(max_const) = mir_to_const(cx, Const::from_ty_const(max_val, bnd_ty, cx.tcx))
-                    && let Some(end_const) = constant(cx, cx.typeck_results(), end)
+                    && let Some(max_const) = mir_to_const(cx.tcx, Const::from_ty_const(max_val, bnd_ty, cx.tcx))
+                    && let Some(end_const) = ConstEvalCtxt::new(cx).eval(end)
                 {
                     end_const == max_const
                 } else {
@@ -1624,7 +1626,9 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool
         return true;
     }
     let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id);
-    if let Some(Constant::Int(v)) = constant(cx, cx.tcx.typeck(enclosing_body), e) {
+    if let Some(Constant::Int(v)) =
+        ConstEvalCtxt::with_env(cx.tcx, cx.tcx.param_env(enclosing_body), cx.tcx.typeck(enclosing_body)).eval(e)
+    {
         return value == v;
     }
     false
diff --git a/clippy_utils/src/paths.rs b/clippy_utils/src/paths.rs
index d5a3d8b9e5a2e..684c645c19964 100644
--- a/clippy_utils/src/paths.rs
+++ b/clippy_utils/src/paths.rs
@@ -16,7 +16,6 @@ pub const BINARYHEAP_ITER: [&str; 5] = ["alloc", "collections", "binary_heap", "
 pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
 pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
 pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "iter"];
-pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
 pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"];
 pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"];
 pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"];
diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs
index 496c8f5b55373..96dd3c55d37ae 100644
--- a/clippy_utils/src/source.rs
+++ b/clippy_utils/src/source.rs
@@ -6,7 +6,8 @@ use rustc_ast::{LitKind, StrStyle};
 use rustc_data_structures::sync::Lrc;
 use rustc_errors::Applicability;
 use rustc_hir::{BlockCheckMode, Expr, ExprKind, UnsafeSource};
-use rustc_lint::{LateContext, LintContext};
+use rustc_lint::{EarlyContext, LateContext};
+use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 use rustc_span::source_map::{original_sp, SourceMap};
 use rustc_span::{
@@ -17,6 +18,30 @@ use std::borrow::Cow;
 use std::fmt;
 use std::ops::Range;
 
+pub trait HasSession {
+    fn sess(&self) -> &Session;
+}
+impl HasSession for Session {
+    fn sess(&self) -> &Session {
+        self
+    }
+}
+impl HasSession for TyCtxt<'_> {
+    fn sess(&self) -> &Session {
+        self.sess
+    }
+}
+impl HasSession for EarlyContext<'_> {
+    fn sess(&self) -> &Session {
+        ::rustc_lint::LintContext::sess(self)
+    }
+}
+impl HasSession for LateContext<'_> {
+    fn sess(&self) -> &Session {
+        self.tcx.sess()
+    }
+}
+
 /// Conversion of a value into the range portion of a `Span`.
 pub trait SpanRange: Sized {
     fn into_range(self) -> Range<BytePos>;
@@ -71,19 +96,19 @@ impl IntoSpan for Range<BytePos> {
 pub trait SpanRangeExt: SpanRange {
     /// Gets the source file, and range in the file, of the given span. Returns `None` if the span
     /// extends through multiple files, or is malformed.
-    fn get_source_text(self, cx: &impl LintContext) -> Option<SourceFileRange> {
+    fn get_source_text(self, cx: &impl HasSession) -> Option<SourceFileRange> {
         get_source_text(cx.sess().source_map(), self.into_range())
     }
 
     /// Calls the given function with the source text referenced and returns the value. Returns
     /// `None` if the source text cannot be retrieved.
-    fn with_source_text<T>(self, cx: &impl LintContext, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> {
+    fn with_source_text<T>(self, cx: &impl HasSession, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> {
         with_source_text(cx.sess().source_map(), self.into_range(), f)
     }
 
     /// Checks if the referenced source text satisfies the given predicate. Returns `false` if the
     /// source text cannot be retrieved.
-    fn check_source_text(self, cx: &impl LintContext, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool {
+    fn check_source_text(self, cx: &impl HasSession, pred: impl for<'a> FnOnce(&'a str) -> bool) -> bool {
         self.with_source_text(cx, pred).unwrap_or(false)
     }
 
@@ -91,7 +116,7 @@ pub trait SpanRangeExt: SpanRange {
     /// and returns the value. Returns `None` if the source text cannot be retrieved.
     fn with_source_text_and_range<T>(
         self,
-        cx: &impl LintContext,
+        cx: &impl HasSession,
         f: impl for<'a> FnOnce(&'a str, Range<usize>) -> T,
     ) -> Option<T> {
         with_source_text_and_range(cx.sess().source_map(), self.into_range(), f)
@@ -104,30 +129,30 @@ pub trait SpanRangeExt: SpanRange {
     /// The new range must reside within the same source file.
     fn map_range(
         self,
-        cx: &impl LintContext,
+        cx: &impl HasSession,
         f: impl for<'a> FnOnce(&'a str, Range<usize>) -> Option<Range<usize>>,
     ) -> Option<Range<BytePos>> {
         map_range(cx.sess().source_map(), self.into_range(), f)
     }
 
     /// Extends the range to include all preceding whitespace characters.
-    fn with_leading_whitespace(self, cx: &impl LintContext) -> Range<BytePos> {
+    fn with_leading_whitespace(self, cx: &impl HasSession) -> Range<BytePos> {
         with_leading_whitespace(cx.sess().source_map(), self.into_range())
     }
 
     /// Trims the leading whitespace from the range.
-    fn trim_start(self, cx: &impl LintContext) -> Range<BytePos> {
+    fn trim_start(self, cx: &impl HasSession) -> Range<BytePos> {
         trim_start(cx.sess().source_map(), self.into_range())
     }
 
     /// Writes the referenced source text to the given writer. Will return `Err` if the source text
     /// could not be retrieved.
-    fn write_source_text_to(self, cx: &impl LintContext, dst: &mut impl fmt::Write) -> fmt::Result {
+    fn write_source_text_to(self, cx: &impl HasSession, dst: &mut impl fmt::Write) -> fmt::Result {
         write_source_text_to(cx.sess().source_map(), self.into_range(), dst)
     }
 
     /// Extracts the referenced source text as an owned string.
-    fn source_text_to_string(self, cx: &impl LintContext) -> Option<String> {
+    fn source_text_to_string(self, cx: &impl HasSession) -> Option<String> {
         self.with_source_text(cx, ToOwned::to_owned)
     }
 }
@@ -227,15 +252,15 @@ impl SourceFileRange {
 }
 
 /// Like `snippet_block`, but add braces if the expr is not an `ExprKind::Block`.
-pub fn expr_block<T: LintContext>(
-    cx: &T,
+pub fn expr_block(
+    sess: &impl HasSession,
     expr: &Expr<'_>,
     outer: SyntaxContext,
     default: &str,
     indent_relative_to: Option<Span>,
     app: &mut Applicability,
 ) -> String {
-    let (code, from_macro) = snippet_block_with_context(cx, expr.span, outer, default, indent_relative_to, app);
+    let (code, from_macro) = snippet_block_with_context(sess, expr.span, outer, default, indent_relative_to, app);
     if !from_macro
         && let ExprKind::Block(block, _) = expr.kind
         && block.rules != BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided)
@@ -260,13 +285,13 @@ pub fn expr_block<T: LintContext>(
 ///     let x = ();
 /// //  ^^^^^^^^^^
 /// ```
-pub fn first_line_of_span<T: LintContext>(cx: &T, span: Span) -> Span {
-    first_char_in_first_line(cx, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
+pub fn first_line_of_span(sess: &impl HasSession, span: Span) -> Span {
+    first_char_in_first_line(sess, span).map_or(span, |first_char_pos| span.with_lo(first_char_pos))
 }
 
-fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePos> {
-    let line_span = line_span(cx, span);
-    snippet_opt(cx, line_span).and_then(|snip| {
+fn first_char_in_first_line(sess: &impl HasSession, span: Span) -> Option<BytePos> {
+    let line_span = line_span(sess, span);
+    snippet_opt(sess, line_span).and_then(|snip| {
         snip.find(|c: char| !c.is_whitespace())
             .map(|pos| line_span.lo() + BytePos::from_usize(pos))
     })
@@ -281,9 +306,9 @@ fn first_char_in_first_line<T: LintContext>(cx: &T, span: Span) -> Option<BytePo
 ///        let x = ();
 /// // ^^^^^^^^^^^^^^
 /// ```
-fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
+fn line_span(sess: &impl HasSession, span: Span) -> Span {
     let span = original_sp(span, DUMMY_SP);
-    let SourceFileAndLine { sf, line } = cx.sess().source_map().lookup_line(span.lo()).unwrap();
+    let SourceFileAndLine { sf, line } = sess.sess().source_map().lookup_line(span.lo()).unwrap();
     let line_start = sf.lines()[line];
     let line_start = sf.absolute_position(line_start);
     span.with_lo(line_start)
@@ -297,13 +322,13 @@ fn line_span<T: LintContext>(cx: &T, span: Span) -> Span {
 ///     let x = ();
 /// //          ^^ -- will return 4
 /// ```
-pub fn indent_of<T: LintContext>(cx: &T, span: Span) -> Option<usize> {
-    snippet_opt(cx, line_span(cx, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
+pub fn indent_of(sess: &impl HasSession, span: Span) -> Option<usize> {
+    snippet_opt(sess, line_span(sess, span)).and_then(|snip| snip.find(|c: char| !c.is_whitespace()))
 }
 
 /// Gets a snippet of the indentation of the line of a span
-pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> {
-    snippet_opt(cx, line_span(cx, span)).map(|mut s| {
+pub fn snippet_indent(sess: &impl HasSession, span: Span) -> Option<String> {
+    snippet_opt(sess, line_span(sess, span)).map(|mut s| {
         let len = s.len() - s.trim_start().len();
         s.truncate(len);
         s
@@ -315,8 +340,8 @@ pub fn snippet_indent<T: LintContext>(cx: &T, span: Span) -> Option<String> {
 // sources that the user has no control over.
 // For some reason these attributes don't have any expansion info on them, so
 // we have to check it this way until there is a better way.
-pub fn is_present_in_source<T: LintContext>(cx: &T, span: Span) -> bool {
-    if let Some(snippet) = snippet_opt(cx, span) {
+pub fn is_present_in_source(sess: &impl HasSession, span: Span) -> bool {
+    if let Some(snippet) = snippet_opt(sess, span) {
         if snippet.is_empty() {
             return false;
         }
@@ -407,8 +432,8 @@ fn reindent_multiline_inner(s: &str, ignore_first: bool, indent: Option<usize>,
 /// snippet(cx, span1, "..") // -> "value"
 /// snippet(cx, span2, "..") // -> "Vec::new()"
 /// ```
-pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<'a, str> {
-    snippet_opt(cx, span).map_or_else(|| Cow::Borrowed(default), From::from)
+pub fn snippet<'a>(sess: &impl HasSession, span: Span, default: &'a str) -> Cow<'a, str> {
+    snippet_opt(sess, span).map_or_else(|| Cow::Borrowed(default), From::from)
 }
 
 /// Same as [`snippet`], but it adapts the applicability level by following rules:
@@ -417,13 +442,13 @@ pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<
 /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
 /// - If the default value is used and the applicability level is `MachineApplicable`, change it to
 ///   `HasPlaceholders`
-pub fn snippet_with_applicability<'a, T: LintContext>(
-    cx: &T,
+pub fn snippet_with_applicability<'a>(
+    sess: &impl HasSession,
     span: Span,
     default: &'a str,
     applicability: &mut Applicability,
 ) -> Cow<'a, str> {
-    snippet_with_applicability_sess(cx.sess(), span, default, applicability)
+    snippet_with_applicability_sess(sess.sess(), span, default, applicability)
 }
 
 fn snippet_with_applicability_sess<'a>(
@@ -435,7 +460,7 @@ fn snippet_with_applicability_sess<'a>(
     if *applicability != Applicability::Unspecified && span.from_expansion() {
         *applicability = Applicability::MaybeIncorrect;
     }
-    snippet_opt_sess(sess, span).map_or_else(
+    snippet_opt(sess, span).map_or_else(
         || {
             if *applicability == Applicability::MachineApplicable {
                 *applicability = Applicability::HasPlaceholders;
@@ -447,12 +472,8 @@ fn snippet_with_applicability_sess<'a>(
 }
 
 /// Converts a span to a code snippet. Returns `None` if not available.
-pub fn snippet_opt(cx: &impl LintContext, span: Span) -> Option<String> {
-    snippet_opt_sess(cx.sess(), span)
-}
-
-fn snippet_opt_sess(sess: &Session, span: Span) -> Option<String> {
-    sess.source_map().span_to_snippet(span).ok()
+pub fn snippet_opt(sess: &impl HasSession, span: Span) -> Option<String> {
+    sess.sess().source_map().span_to_snippet(span).ok()
 }
 
 /// Converts a span (from a block) to a code snippet if available, otherwise use default.
@@ -489,41 +510,41 @@ fn snippet_opt_sess(sess: &Session, span: Span) -> Option<String> {
 ///     } // aligned with `if`
 /// ```
 /// Note that the first line of the snippet always has 0 indentation.
-pub fn snippet_block<'a, T: LintContext>(
-    cx: &T,
+pub fn snippet_block<'a>(
+    sess: &impl HasSession,
     span: Span,
     default: &'a str,
     indent_relative_to: Option<Span>,
 ) -> Cow<'a, str> {
-    let snip = snippet(cx, span, default);
-    let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
+    let snip = snippet(sess, span, default);
+    let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
     reindent_multiline(snip, true, indent)
 }
 
 /// Same as `snippet_block`, but adapts the applicability level by the rules of
 /// `snippet_with_applicability`.
 pub fn snippet_block_with_applicability<'a>(
-    cx: &impl LintContext,
+    sess: &impl HasSession,
     span: Span,
     default: &'a str,
     indent_relative_to: Option<Span>,
     applicability: &mut Applicability,
 ) -> Cow<'a, str> {
-    let snip = snippet_with_applicability(cx, span, default, applicability);
-    let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
+    let snip = snippet_with_applicability(sess, span, default, applicability);
+    let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
     reindent_multiline(snip, true, indent)
 }
 
 pub fn snippet_block_with_context<'a>(
-    cx: &impl LintContext,
+    sess: &impl HasSession,
     span: Span,
     outer: SyntaxContext,
     default: &'a str,
     indent_relative_to: Option<Span>,
     app: &mut Applicability,
 ) -> (Cow<'a, str>, bool) {
-    let (snip, from_macro) = snippet_with_context(cx, span, outer, default, app);
-    let indent = indent_relative_to.and_then(|s| indent_of(cx, s));
+    let (snip, from_macro) = snippet_with_context(sess, span, outer, default, app);
+    let indent = indent_relative_to.and_then(|s| indent_of(sess, s));
     (reindent_multiline(snip, true, indent), from_macro)
 }
 
@@ -537,13 +558,13 @@ pub fn snippet_block_with_context<'a>(
 ///
 /// This will also return whether or not the snippet is a macro call.
 pub fn snippet_with_context<'a>(
-    cx: &impl LintContext,
+    sess: &impl HasSession,
     span: Span,
     outer: SyntaxContext,
     default: &'a str,
     applicability: &mut Applicability,
 ) -> (Cow<'a, str>, bool) {
-    snippet_with_context_sess(cx.sess(), span, outer, default, applicability)
+    snippet_with_context_sess(sess.sess(), span, outer, default, applicability)
 }
 
 fn snippet_with_context_sess<'a>(
@@ -661,15 +682,15 @@ pub fn trim_span(sm: &SourceMap, span: Span) -> Span {
 /// writeln!(o, "")   ->   writeln!(o, "")
 ///             ^^                   ^^^^
 /// ```
-pub fn expand_past_previous_comma(cx: &LateContext<'_>, span: Span) -> Span {
-    let extended = cx.sess().source_map().span_extend_to_prev_char(span, ',', true);
+pub fn expand_past_previous_comma(sess: &impl HasSession, span: Span) -> Span {
+    let extended = sess.sess().source_map().span_extend_to_prev_char(span, ',', true);
     extended.with_lo(extended.lo() - BytePos(1))
 }
 
 /// Converts `expr` to a `char` literal if it's a `str` literal containing a single
 /// character (or a single byte with `ascii_only`)
 pub fn str_literal_to_char_literal(
-    cx: &LateContext<'_>,
+    sess: &impl HasSession,
     expr: &Expr<'_>,
     applicability: &mut Applicability,
     ascii_only: bool,
@@ -684,7 +705,7 @@ pub fn str_literal_to_char_literal(
         }
         && len == 1
     {
-        let snip = snippet_with_applicability(cx, expr.span, string, applicability);
+        let snip = snippet_with_applicability(sess, expr.span, string, applicability);
         let ch = if let StrStyle::Raw(nhash) = style {
             let nhash = nhash as usize;
             // for raw string: r##"a"##
diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs
index 812fb647fdab6..bd48990aea95f 100644
--- a/clippy_utils/src/ty.rs
+++ b/clippy_utils/src/ty.rs
@@ -525,19 +525,6 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
     needs_ordered_drop_inner(cx, ty, &mut FxHashSet::default())
 }
 
-/// Peels off all references on the type. Returns the underlying type and the number of references
-/// removed.
-pub fn peel_mid_ty_refs(ty: Ty<'_>) -> (Ty<'_>, usize) {
-    fn peel(ty: Ty<'_>, count: usize) -> (Ty<'_>, usize) {
-        if let ty::Ref(_, ty, _) = ty.kind() {
-            peel(*ty, count + 1)
-        } else {
-            (ty, count)
-        }
-    }
-    peel(ty, 0)
-}
-
 /// Peels off all references on the type. Returns the underlying type, the number of references
 /// removed, and whether the pointer is ultimately mutable or not.
 pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs
index 7066c9ad2b96b..2a5d3536ff6b7 100644
--- a/clippy_utils/src/visitors.rs
+++ b/clippy_utils/src/visitors.rs
@@ -1,6 +1,7 @@
 use crate::ty::needs_ordered_drop;
 use crate::{get_enclosing_block, path_to_local_id};
 use core::ops::ControlFlow;
+use rustc_ast::visit::{try_visit, VisitorResult};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
@@ -50,16 +51,17 @@ impl Continue for Descend {
 /// A type which can be visited.
 pub trait Visitable<'tcx> {
     /// Calls the corresponding `visit_*` function on the visitor.
-    fn visit<V: Visitor<'tcx>>(self, visitor: &mut V);
+    fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result;
 }
 impl<'tcx, T> Visitable<'tcx> for &'tcx [T]
 where
     &'tcx T: Visitable<'tcx>,
 {
-    fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
+    fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result {
         for x in self {
-            x.visit(visitor);
+            try_visit!(x.visit(visitor));
         }
+        V::Result::output()
     }
 }
 impl<'tcx, A, B> Visitable<'tcx> for (A, B)
@@ -67,27 +69,28 @@ where
     A: Visitable<'tcx>,
     B: Visitable<'tcx>,
 {
-    fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
+    fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result {
         let (a, b) = self;
-        a.visit(visitor);
-        b.visit(visitor);
+        try_visit!(a.visit(visitor));
+        b.visit(visitor)
     }
 }
 impl<'tcx, T> Visitable<'tcx> for Option<T>
 where
     T: Visitable<'tcx>,
 {
-    fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
+    fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result {
         if let Some(x) = self {
-            x.visit(visitor);
+            try_visit!(x.visit(visitor));
         }
+        V::Result::output()
     }
 }
 macro_rules! visitable_ref {
     ($t:ident, $f:ident) => {
         impl<'tcx> Visitable<'tcx> for &'tcx $t<'tcx> {
-            fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) {
-                visitor.$f(self);
+            fn visit<V: Visitor<'tcx>>(self, visitor: &mut V) -> V::Result {
+                visitor.$f(self)
             }
         }
     };
@@ -104,45 +107,37 @@ pub fn for_each_expr_without_closures<'tcx, B, C: Continue>(
     node: impl Visitable<'tcx>,
     f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
 ) -> Option<B> {
-    struct V<B, F> {
+    struct V<F> {
         f: F,
-        res: Option<B>,
     }
-    impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<B, F> {
-        type Result = ControlFlow<()>;
+    impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<F> {
+        type Result = ControlFlow<B>;
 
-        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) -> ControlFlow<()> {
-            if self.res.is_some() {
-                return ControlFlow::Break(());
-            }
+        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) -> Self::Result {
             match (self.f)(e) {
                 ControlFlow::Continue(c) if c.descend() => walk_expr(self, e),
-                ControlFlow::Break(b) => {
-                    self.res = Some(b);
-                    ControlFlow::Break(())
-                },
+                ControlFlow::Break(b) => ControlFlow::Break(b),
                 ControlFlow::Continue(_) => ControlFlow::Continue(()),
             }
         }
 
         // Avoid unnecessary `walk_*` calls.
-        fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) -> ControlFlow<()> {
+        fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) -> Self::Result {
             ControlFlow::Continue(())
         }
-        fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) -> ControlFlow<()> {
+        fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) -> Self::Result {
             ControlFlow::Continue(())
         }
-        fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) -> ControlFlow<()> {
+        fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) -> Self::Result {
             ControlFlow::Continue(())
         }
         // Avoid monomorphising all `visit_*` functions.
-        fn visit_nested_item(&mut self, _: ItemId) -> ControlFlow<()> {
+        fn visit_nested_item(&mut self, _: ItemId) -> Self::Result {
             ControlFlow::Continue(())
         }
     }
-    let mut v = V { f, res: None };
-    node.visit(&mut v);
-    v.res
+    let mut v = V { f };
+    node.visit(&mut v).break_value()
 }
 
 /// Calls the given function once for each expression contained. This will enter bodies, but not
@@ -152,44 +147,47 @@ pub fn for_each_expr<'tcx, B, C: Continue>(
     node: impl Visitable<'tcx>,
     f: impl FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>,
 ) -> Option<B> {
-    struct V<'tcx, B, F> {
+    struct V<'tcx, F> {
         tcx: TyCtxt<'tcx>,
         f: F,
-        res: Option<B>,
     }
-    impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<'tcx, B, F> {
+    impl<'tcx, B, C: Continue, F: FnMut(&'tcx Expr<'tcx>) -> ControlFlow<B, C>> Visitor<'tcx> for V<'tcx, F> {
         type NestedFilter = nested_filter::OnlyBodies;
+        type Result = ControlFlow<B>;
+
         fn nested_visit_map(&mut self) -> Self::Map {
             self.tcx.hir()
         }
 
-        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
-            if self.res.is_some() {
-                return;
-            }
+        fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) -> Self::Result {
             match (self.f)(e) {
                 ControlFlow::Continue(c) if c.descend() => walk_expr(self, e),
-                ControlFlow::Break(b) => self.res = Some(b),
-                ControlFlow::Continue(_) => (),
+                ControlFlow::Break(b) => ControlFlow::Break(b),
+                ControlFlow::Continue(_) => ControlFlow::Continue(()),
             }
         }
 
         // Only walk closures
-        fn visit_anon_const(&mut self, _: &'tcx AnonConst) {}
+        fn visit_anon_const(&mut self, _: &'tcx AnonConst) -> Self::Result {
+            ControlFlow::Continue(())
+        }
         // Avoid unnecessary `walk_*` calls.
-        fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) {}
-        fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) {}
-        fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) {}
+        fn visit_ty(&mut self, _: &'tcx hir::Ty<'tcx>) -> Self::Result {
+            ControlFlow::Continue(())
+        }
+        fn visit_pat(&mut self, _: &'tcx Pat<'tcx>) -> Self::Result {
+            ControlFlow::Continue(())
+        }
+        fn visit_qpath(&mut self, _: &'tcx QPath<'tcx>, _: HirId, _: Span) -> Self::Result {
+            ControlFlow::Continue(())
+        }
         // Avoid monomorphising all `visit_*` functions.
-        fn visit_nested_item(&mut self, _: ItemId) {}
+        fn visit_nested_item(&mut self, _: ItemId) -> Self::Result {
+            ControlFlow::Continue(())
+        }
     }
-    let mut v = V {
-        tcx: cx.tcx,
-        f,
-        res: None,
-    };
-    node.visit(&mut v);
-    v.res
+    let mut v = V { tcx: cx.tcx, f };
+    node.visit(&mut v).break_value()
 }
 
 /// returns `true` if expr contains match expr desugared from try
diff --git a/declare_clippy_lint/Cargo.toml b/declare_clippy_lint/Cargo.toml
index 80106f683c2ae..31270241b0b2e 100644
--- a/declare_clippy_lint/Cargo.toml
+++ b/declare_clippy_lint/Cargo.toml
@@ -11,6 +11,3 @@ proc-macro = true
 itertools = "0.12"
 quote = "1.0.21"
 syn = "2.0"
-
-[features]
-deny-warnings = []
diff --git a/declare_clippy_lint/src/lib.rs b/declare_clippy_lint/src/lib.rs
index 25b2fc9395cd9..ca070f6c250bf 100644
--- a/declare_clippy_lint/src/lib.rs
+++ b/declare_clippy_lint/src/lib.rs
@@ -1,5 +1,4 @@
 #![feature(let_chains)]
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 // warn on lints, that are included in `rust-lang/rust`s bootstrap
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
diff --git a/lintcheck/Cargo.toml b/lintcheck/Cargo.toml
index 350418eeeb871..b0e4e3e3e573b 100644
--- a/lintcheck/Cargo.toml
+++ b/lintcheck/Cargo.toml
@@ -25,6 +25,3 @@ tar = "0.4"
 toml = "0.7.3"
 ureq = { version = "2.2", features = ["json"] }
 walkdir = "2.3"
-
-[features]
-deny-warnings = []
diff --git a/lintcheck/src/config.rs b/lintcheck/src/config.rs
index 6bec1753fc7b8..bd4fcc5e337e9 100644
--- a/lintcheck/src/config.rs
+++ b/lintcheck/src/config.rs
@@ -33,13 +33,13 @@ pub(crate) struct LintcheckConfig {
     /// Runs cargo clippy --fix and checks if all suggestions apply
     #[clap(long, conflicts_with("max_jobs"))]
     pub fix: bool,
-    /// Apply a filter to only collect specified lints, this also overrides `allow` attributes
+    /// Apply a filter to only collect specified lints
     #[clap(long = "filter", value_name = "clippy_lint_name", use_value_delimiter = true)]
     pub lint_filter: Vec<String>,
-    /// Set all lints to the "warn" lint level, even resitriction ones. Usually,
-    /// it's better to use `--filter` instead
+    /// Check all Clippy lints, by default only `clippy::all` and `clippy::pedantic` are checked.
+    /// Usually, it's better to use `--filter` instead
     #[clap(long, conflicts_with("lint_filter"))]
-    pub warn_all: bool,
+    pub all_lints: bool,
     /// Set the output format of the log file
     #[clap(long, short, default_value = "text")]
     pub format: OutputFormat,
diff --git a/lintcheck/src/input.rs b/lintcheck/src/input.rs
index 3b263674aa87a..3383d50fa02c7 100644
--- a/lintcheck/src/input.rs
+++ b/lintcheck/src/input.rs
@@ -98,12 +98,12 @@ pub fn read_crates(toml_path: &Path) -> (Vec<CrateWithSource>, RecursiveOptions)
     let crate_list: SourceList =
         toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{e}", toml_path.display()));
     // parse the hashmap of the toml file into a list of crates
-    let tomlcrates: Vec<TomlCrate> = crate_list.crates.into_values().collect();
+    let toml_crates: Vec<TomlCrate> = crate_list.crates.into_values().collect();
 
     // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate =>
-    // multiple Cratesources)
+    // multiple CrateSources)
     let mut crate_sources = Vec::new();
-    for tk in tomlcrates {
+    for tk in toml_crates {
         if let Some(ref path) = tk.path {
             crate_sources.push(CrateWithSource {
                 name: tk.name.clone(),
diff --git a/lintcheck/src/main.rs b/lintcheck/src/main.rs
index 0dd62ded293c8..acb6eaa5278aa 100644
--- a/lintcheck/src/main.rs
+++ b/lintcheck/src/main.rs
@@ -119,7 +119,8 @@ impl Crate {
         cmd.arg(if config.fix { "fix" } else { "check" })
             .arg("--quiet")
             .current_dir(&self.path)
-            .env("CLIPPY_ARGS", clippy_args.join("__CLIPPY_HACKERY__"));
+            .env("CLIPPY_ARGS", clippy_args.join("__CLIPPY_HACKERY__"))
+            .env("CLIPPY_DISABLE_DOCS_LINKS", "1");
 
         if let Some(server) = server {
             // `cargo clippy` is a wrapper around `cargo check` that mainly sets `RUSTC_WORKSPACE_WRAPPER` to
@@ -284,29 +285,24 @@ fn lintcheck(config: LintcheckConfig) {
     let (crates, recursive_options) = read_crates(&config.sources_toml_path);
 
     let counter = AtomicUsize::new(1);
-    let mut lint_level_args: Vec<String> = vec![];
+    let mut lint_level_args: Vec<String> = vec!["--cap-lints=allow".into()];
     if config.lint_filter.is_empty() {
-        lint_level_args.push("--cap-lints=warn".to_string());
-
-        // Set allow-by-default to warn
-        if config.warn_all {
-            [
+        let groups = if config.all_lints {
+            &[
+                "clippy::all",
                 "clippy::cargo",
                 "clippy::nursery",
                 "clippy::pedantic",
                 "clippy::restriction",
-            ]
+            ][..]
+        } else {
+            &["clippy::all", "clippy::pedantic"]
+        };
+        groups
             .iter()
-            .map(|group| format!("--warn={group}"))
+            .map(|group| format!("--force-warn={group}"))
             .collect_into(&mut lint_level_args);
-        } else {
-            ["clippy::cargo", "clippy::pedantic"]
-                .iter()
-                .map(|group| format!("--warn={group}"))
-                .collect_into(&mut lint_level_args);
-        }
     } else {
-        lint_level_args.push("--cap-lints=allow".to_string());
         config
             .lint_filter
             .iter()
diff --git a/rust-toolchain b/rust-toolchain
index 69fb11a4824af..5fbe4e544a9e4 100644
--- a/rust-toolchain
+++ b/rust-toolchain
@@ -1,4 +1,4 @@
 [toolchain]
-channel = "nightly-2024-07-25"
+channel = "nightly-2024-08-08"
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
 profile = "minimal"
diff --git a/rustc_tools_util/Cargo.toml b/rustc_tools_util/Cargo.toml
index 877049ae7d0eb..37b592da132f4 100644
--- a/rustc_tools_util/Cargo.toml
+++ b/rustc_tools_util/Cargo.toml
@@ -10,6 +10,3 @@ categories = ["development-tools"]
 edition = "2018"
 
 [dependencies]
-
-[features]
-deny-warnings = []
diff --git a/rustc_tools_util/src/lib.rs b/rustc_tools_util/src/lib.rs
index 4c1d8c3733df6..2cc38130472f8 100644
--- a/rustc_tools_util/src/lib.rs
+++ b/rustc_tools_util/src/lib.rs
@@ -1,4 +1,4 @@
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
+use std::str;
 
 /// This macro creates the version string during compilation from the
 /// current environment
@@ -101,49 +101,46 @@ impl std::fmt::Debug for VersionInfo {
 
 #[must_use]
 pub fn get_commit_hash() -> Option<String> {
-    std::process::Command::new("git")
-        .args(["rev-parse", "--short", "HEAD"])
+    let output = std::process::Command::new("git")
+        .args(["rev-parse", "HEAD"])
         .output()
-        .ok()
-        .and_then(|r| String::from_utf8(r.stdout).ok())
+        .ok()?;
+    let mut stdout = output.status.success().then_some(output.stdout)?;
+    stdout.truncate(10);
+    String::from_utf8(stdout).ok()
 }
 
 #[must_use]
 pub fn get_commit_date() -> Option<String> {
-    std::process::Command::new("git")
+    let output = std::process::Command::new("git")
         .args(["log", "-1", "--date=short", "--pretty=format:%cd"])
         .output()
-        .ok()
-        .and_then(|r| String::from_utf8(r.stdout).ok())
+        .ok()?;
+    let stdout = output.status.success().then_some(output.stdout)?;
+    String::from_utf8(stdout).ok()
 }
 
 #[must_use]
 pub fn get_channel() -> String {
-    match std::env::var("CFG_RELEASE_CHANNEL") {
-        Ok(channel) => channel,
-        Err(_) => {
-            // if that failed, try to ask rustc -V, do some parsing and find out
-            match std::process::Command::new("rustc")
-                .arg("-V")
-                .output()
-                .ok()
-                .and_then(|r| String::from_utf8(r.stdout).ok())
-            {
-                Some(rustc_output) => {
-                    if rustc_output.contains("beta") {
-                        String::from("beta")
-                    } else if rustc_output.contains("stable") {
-                        String::from("stable")
-                    } else {
-                        // default to nightly if we fail to parse
-                        String::from("nightly")
-                    }
-                },
-                // default to nightly
-                None => String::from("nightly"),
+    if let Ok(channel) = std::env::var("CFG_RELEASE_CHANNEL") {
+        return channel;
+    }
+
+    // if that failed, try to ask rustc -V, do some parsing and find out
+    if let Ok(output) = std::process::Command::new("rustc").arg("-V").output() {
+        if output.status.success() {
+            if let Ok(rustc_output) = str::from_utf8(&output.stdout) {
+                if rustc_output.contains("beta") {
+                    return String::from("beta");
+                } else if rustc_output.contains("stable") {
+                    return String::from("stable");
+                }
             }
-        },
+        }
     }
+
+    // default to nightly
+    String::from("nightly")
 }
 
 #[cfg(test)]
diff --git a/src/driver.rs b/src/driver.rs
index 3fafe2427a256..0ac3f35b4465a 100644
--- a/src/driver.rs
+++ b/src/driver.rs
@@ -2,7 +2,6 @@
 #![allow(rustc::untranslatable_diagnostic)]
 #![feature(rustc_private)]
 #![feature(let_chains)]
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 // warn on lints, that are included in `rust-lang/rust`s bootstrap
 #![warn(rust_2018_idioms, unused_lifetimes)]
 // warn on rustc internal lints
@@ -151,7 +150,6 @@ impl rustc_driver::Callbacks for ClippyCallbacks {
             let conf = clippy_config::Conf::read(sess, &conf_path);
             clippy_lints::register_lints(lint_store, conf);
             clippy_lints::register_pre_expansion_lints(lint_store, conf);
-            clippy_lints::register_renamed(lint_store);
         }));
 
         // FIXME: #4825; This is required, because Clippy lints that are based on MIR have to be
diff --git a/src/main.rs b/src/main.rs
index 30beaae34d2fe..c9af2138a7233 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,4 +1,3 @@
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 // warn on lints, that are included in `rust-lang/rust`s bootstrap
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
diff --git a/tests/check-fmt.rs b/tests/check-fmt.rs
index e106583de4a2e..cd3fed896c2de 100644
--- a/tests/check-fmt.rs
+++ b/tests/check-fmt.rs
@@ -1,4 +1,3 @@
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
 use std::path::PathBuf;
diff --git a/tests/compile-test.rs b/tests/compile-test.rs
index 4e5120406b0d8..c7080e5dcdc9e 100644
--- a/tests/compile-test.rs
+++ b/tests/compile-test.rs
@@ -1,4 +1,3 @@
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![warn(rust_2018_idioms, unused_lifetimes)]
 #![allow(unused_extern_crates)]
 
@@ -208,7 +207,8 @@ fn run_ui_toml() {
     ui_test::run_tests_generic(
         vec![config],
         ui_test::default_file_filter,
-        |config, path, _file_contents| {
+        |config, file_contents| {
+            let path = file_contents.span().file;
             config
                 .program
                 .envs
@@ -260,7 +260,7 @@ fn run_ui_cargo() {
             path.ends_with("Cargo.toml")
                 .then(|| ui_test::default_any_file_filter(path, config) && !ignored_32bit(path))
         },
-        |_config, _path, _file_contents| {},
+        |_config, _file_contents| {},
         status_emitter::Text::from(args.format),
     )
     .unwrap();
diff --git a/tests/dogfood.rs b/tests/dogfood.rs
index c8a761bf509ec..8d10d5e7161a6 100644
--- a/tests/dogfood.rs
+++ b/tests/dogfood.rs
@@ -3,7 +3,6 @@
 //!
 //! See [Eating your own dog food](https://en.wikipedia.org/wiki/Eating_your_own_dog_food) for context
 
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
 use itertools::Itertools;
diff --git a/tests/integration.rs b/tests/integration.rs
index 77b7bb6a7bf69..13cf36823c5e4 100644
--- a/tests/integration.rs
+++ b/tests/integration.rs
@@ -8,7 +8,6 @@
 //! Clippy doesn't produce an ICE. Lint warnings are ignored by this test.
 
 #![cfg(feature = "integration")]
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
 use std::env;
diff --git a/tests/lint_message_convention.rs b/tests/lint_message_convention.rs
index 6ce7e44474d81..7ed1f485c1cfd 100644
--- a/tests/lint_message_convention.rs
+++ b/tests/lint_message_convention.rs
@@ -1,4 +1,3 @@
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
 use std::ffi::OsStr;
diff --git a/tests/missing-test-files.rs b/tests/missing-test-files.rs
index 141c11ddb47f9..a8225d037e828 100644
--- a/tests/missing-test-files.rs
+++ b/tests/missing-test-files.rs
@@ -1,4 +1,3 @@
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![warn(rust_2018_idioms, unused_lifetimes)]
 #![allow(clippy::assertions_on_constants)]
 #![feature(path_file_prefix)]
diff --git a/tests/ui-internal/default_deprecation_reason.rs b/tests/ui-internal/default_deprecation_reason.rs
deleted file mode 100644
index c8961d5e1f0bd..0000000000000
--- a/tests/ui-internal/default_deprecation_reason.rs
+++ /dev/null
@@ -1,30 +0,0 @@
-#![deny(clippy::internal)]
-#![feature(rustc_private)]
-
-#[macro_use]
-extern crate clippy_lints;
-use clippy_lints::deprecated_lints::ClippyDeprecatedLint;
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// TODO
-    #[clippy::version = "1.63.0"]
-    pub COOL_LINT_DEFAULT,
-    "default deprecation note"
-}
-
-declare_deprecated_lint! {
-    /// ### What it does
-    /// Nothing. This lint has been deprecated.
-    ///
-    /// ### Deprecation reason
-    /// This lint has been replaced by `cooler_lint`
-    #[clippy::version = "1.63.0"]
-    pub COOL_LINT,
-    "this lint has been replaced by `cooler_lint`"
-}
-
-fn main() {}
diff --git a/tests/ui-internal/default_deprecation_reason.stderr b/tests/ui-internal/default_deprecation_reason.stderr
deleted file mode 100644
index 3b7c747c23f41..0000000000000
--- a/tests/ui-internal/default_deprecation_reason.stderr
+++ /dev/null
@@ -1,22 +0,0 @@
-error: the lint `COOL_LINT_DEFAULT` has the default deprecation reason
-  --> tests/ui-internal/default_deprecation_reason.rs:8:1
-   |
-LL | / declare_deprecated_lint! {
-LL | |     /// ### What it does
-LL | |     /// Nothing. This lint has been deprecated.
-LL | |     ///
-...  |
-LL | |     "default deprecation note"
-LL | | }
-   | |_^
-   |
-note: the lint level is defined here
-  --> tests/ui-internal/default_deprecation_reason.rs:1:9
-   |
-LL | #![deny(clippy::internal)]
-   |         ^^^^^^^^^^^^^^^^
-   = note: `#[deny(clippy::default_deprecation_reason)]` implied by `#[deny(clippy::internal)]`
-   = note: this error originates in the macro `declare_deprecated_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to 1 previous error
-
diff --git a/tests/ui-toml/excessive_nesting/excessive_nesting.rs b/tests/ui-toml/excessive_nesting/excessive_nesting.rs
index 4375f324acaa6..858aab528a91a 100644
--- a/tests/ui-toml/excessive_nesting/excessive_nesting.rs
+++ b/tests/ui-toml/excessive_nesting/excessive_nesting.rs
@@ -1,15 +1,19 @@
 //@aux-build:../../ui/auxiliary/proc_macros.rs
 #![rustfmt::skip]
 #![feature(custom_inner_attributes)]
-#![allow(unused)]
-#![allow(clippy::let_and_return)]
-#![allow(clippy::redundant_closure_call)]
-#![allow(clippy::no_effect)]
-#![allow(clippy::unnecessary_operation)]
-#![allow(clippy::never_loop)]
-#![allow(clippy::needless_if)]
 #![warn(clippy::excessive_nesting)]
-#![allow(clippy::collapsible_if, clippy::blocks_in_conditions)]
+#![allow(
+    unused,
+    clippy::let_and_return,
+    clippy::redundant_closure_call,
+    clippy::no_effect,
+    clippy::unnecessary_operation,
+    clippy::never_loop,
+    clippy::needless_if,
+    clippy::collapsible_if,
+    clippy::blocks_in_conditions,
+    clippy::single_match,
+)]
 
 #[macro_use]
 extern crate proc_macros;
diff --git a/tests/ui-toml/excessive_nesting/excessive_nesting.stderr b/tests/ui-toml/excessive_nesting/excessive_nesting.stderr
index dafcd4420554b..ccdaecdd48170 100644
--- a/tests/ui-toml/excessive_nesting/excessive_nesting.stderr
+++ b/tests/ui-toml/excessive_nesting/excessive_nesting.stderr
@@ -1,5 +1,5 @@
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:21:25
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:25:25
    |
 LL |                 let w = { 3 };
    |                         ^^^^^
@@ -9,7 +9,7 @@ LL |                 let w = { 3 };
    = help: to override `-D warnings` add `#[allow(clippy::excessive_nesting)]`
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:67:17
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:71:17
    |
 LL | /                 impl C {
 LL | |                     pub fn c() {}
@@ -19,7 +19,7 @@ LL | |                 }
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:81:25
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:85:25
    |
 LL |                 let x = { 1 }; // not a warning, but cc is
    |                         ^^^^^
@@ -27,7 +27,7 @@ LL |                 let x = { 1 }; // not a warning, but cc is
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:98:17
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:102:17
    |
 LL | /                 pub mod e {
 LL | |                     pub mod f {}
@@ -37,7 +37,7 @@ LL | |                 } // not here
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:111:18
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:115:18
    |
 LL |     a_but_not({{{{{{{{0}}}}}}}});
    |                  ^^^^^^^^^^^
@@ -45,7 +45,7 @@ LL |     a_but_not({{{{{{{{0}}}}}}}});
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:112:12
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:116:12
    |
 LL |     a.a({{{{{{{{{0}}}}}}}}});
    |            ^^^^^^^^^^^^^
@@ -53,7 +53,7 @@ LL |     a.a({{{{{{{{{0}}}}}}}}});
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:113:12
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:117:12
    |
 LL |     (0, {{{{{{{1}}}}}}});
    |            ^^^^^^^^^
@@ -61,7 +61,7 @@ LL |     (0, {{{{{{{1}}}}}}});
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:118:25
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:122:25
    |
 LL |                   if true {
    |  _________________________^
@@ -74,7 +74,7 @@ LL | |                 }
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:130:29
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:134:29
    |
 LL |                   let z = (|| {
    |  _____________________________^
@@ -86,7 +86,7 @@ LL | |                 })();
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:149:13
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:153:13
    |
 LL |     y += {{{{{5}}}}};
    |             ^^^^^
@@ -94,7 +94,7 @@ LL |     y += {{{{{5}}}}};
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:150:20
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:154:20
    |
 LL |     let z = y + {{{{{{{{{5}}}}}}}}};
    |                    ^^^^^^^^^^^^^
@@ -102,7 +102,7 @@ LL |     let z = y + {{{{{{{{{5}}}}}}}}};
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:151:12
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:155:12
    |
 LL |     [0, {{{{{{{{{{0}}}}}}}}}}];
    |            ^^^^^^^^^^^^^^^
@@ -110,7 +110,7 @@ LL |     [0, {{{{{{{{{{0}}}}}}}}}}];
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:152:25
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:156:25
    |
 LL |     let mut xx = [0; {{{{{{{{100}}}}}}}}];
    |                         ^^^^^^^^^^^^^
@@ -118,7 +118,7 @@ LL |     let mut xx = [0; {{{{{{{{100}}}}}}}}];
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:153:11
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:157:11
    |
 LL |     xx[{{{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}}}];
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -126,7 +126,7 @@ LL |     xx[{{{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}}}];
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:154:13
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:158:13
    |
 LL |     &mut {{{{{{{{{{y}}}}}}}}}};
    |             ^^^^^^^^^^^^^^^
@@ -134,7 +134,7 @@ LL |     &mut {{{{{{{{{{y}}}}}}}}}};
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:156:17
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:160:17
    |
 LL |     for i in {{{{xx}}}} {{{{{{{{}}}}}}}}
    |                 ^^^^
@@ -142,7 +142,7 @@ LL |     for i in {{{{xx}}}} {{{{{{{{}}}}}}}}
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:156:28
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:160:28
    |
 LL |     for i in {{{{xx}}}} {{{{{{{{}}}}}}}}
    |                            ^^^^^^^^^^
@@ -150,7 +150,7 @@ LL |     for i in {{{{xx}}}} {{{{{{{{}}}}}}}}
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:158:28
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:162:28
    |
 LL |     while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}}
    |                            ^^^^^^^^^^^^^
@@ -158,7 +158,7 @@ LL |     while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}}
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:158:48
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:162:48
    |
 LL |     while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}}
    |                                                ^^^^^^^^
@@ -166,7 +166,7 @@ LL |     while let Some(i) = {{{{{{Some(1)}}}}}} {{{{{{{}}}}}}}
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:160:14
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:164:14
    |
 LL |     while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}}
    |              ^^^^^^^^^^^^^^
@@ -174,7 +174,7 @@ LL |     while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}}
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:160:35
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:164:35
    |
 LL |     while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}}
    |                                   ^^^^^^^^^^^^
@@ -182,7 +182,7 @@ LL |     while {{{{{{{{true}}}}}}}} {{{{{{{{{}}}}}}}}}
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:162:23
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:166:23
    |
 LL |     let d = D { d: {{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}} };
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -190,7 +190,7 @@ LL |     let d = D { d: {{{{{{{{{{{{{{{{{{{{{{{3}}}}}}}}}}}}}}}}}}}}}}} };
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:164:8
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:168:8
    |
 LL |     {{{{1;}}}}..{{{{{{3}}}}}};
    |        ^^^^
@@ -198,7 +198,7 @@ LL |     {{{{1;}}}}..{{{{{{3}}}}}};
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:164:20
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:168:20
    |
 LL |     {{{{1;}}}}..{{{{{{3}}}}}};
    |                    ^^^^^^^
@@ -206,7 +206,7 @@ LL |     {{{{1;}}}}..{{{{{{3}}}}}};
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:165:8
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:169:8
    |
 LL |     {{{{1;}}}}..={{{{{{{{{{{{{{{{{{{{{{{{{{6}}}}}}}}}}}}}}}}}}}}}}}}}};
    |        ^^^^
@@ -214,7 +214,7 @@ LL |     {{{{1;}}}}..={{{{{{{{{{{{{{{{{{{{{{{{{{6}}}}}}}}}}}}}}}}}}}}}}}}}};
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:165:21
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:169:21
    |
 LL |     {{{{1;}}}}..={{{{{{{{{{{{{{{{{{{{{{{{{{6}}}}}}}}}}}}}}}}}}}}}}}}}};
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -222,7 +222,7 @@ LL |     {{{{1;}}}}..={{{{{{{{{{{{{{{{{{{{{{{{{{6}}}}}}}}}}}}}}}}}}}}}}}}}};
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:166:10
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:170:10
    |
 LL |     ..{{{{{{{5}}}}}}};
    |          ^^^^^^^^^
@@ -230,7 +230,7 @@ LL |     ..{{{{{{{5}}}}}}};
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:167:11
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:171:11
    |
 LL |     ..={{{{{3}}}}};
    |           ^^^^^
@@ -238,7 +238,7 @@ LL |     ..={{{{{3}}}}};
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:168:8
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:172:8
    |
 LL |     {{{{{1;}}}}}..;
    |        ^^^^^^
@@ -246,7 +246,7 @@ LL |     {{{{{1;}}}}}..;
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:170:20
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:174:20
    |
 LL |     loop { break {{{{1}}}} };
    |                    ^^^^^
@@ -254,7 +254,7 @@ LL |     loop { break {{{{1}}}} };
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:171:13
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:175:13
    |
 LL |     loop {{{{{{}}}}}}
    |             ^^^^^^
@@ -262,7 +262,7 @@ LL |     loop {{{{{{}}}}}}
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:173:14
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:177:14
    |
 LL |     match {{{{{{true}}}}}} {
    |              ^^^^^^^^^^
@@ -270,7 +270,7 @@ LL |     match {{{{{{true}}}}}} {
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:174:20
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:178:20
    |
 LL |         true => {{{{}}}},
    |                    ^^
@@ -278,7 +278,7 @@ LL |         true => {{{{}}}},
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:175:21
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:179:21
    |
 LL |         false => {{{{}}}},
    |                     ^^
@@ -286,7 +286,7 @@ LL |         false => {{{{}}}},
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:181:17
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:185:17
    |
 LL | /                 {
 LL | |                     println!("warning! :)");
@@ -296,7 +296,7 @@ LL | |                 }
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:190:28
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:194:28
    |
 LL |     async fn c() -> u32 {{{{{{{0}}}}}}}
    |                            ^^^^^^^^^
@@ -304,7 +304,7 @@ LL |     async fn c() -> u32 {{{{{{{0}}}}}}}
    = help: try refactoring your code to minimize nesting
 
 error: this block is too nested
-  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:196:8
+  --> tests/ui-toml/excessive_nesting/excessive_nesting.rs:200:8
    |
 LL |     {{{{b().await}}}};
    |        ^^^^^^^^^^^
diff --git a/tests/ui-toml/unwrap_used/unwrap_used.stderr b/tests/ui-toml/unwrap_used/unwrap_used.stderr
index 41d5afd3efeca..320578bfabce0 100644
--- a/tests/ui-toml/unwrap_used/unwrap_used.stderr
+++ b/tests/ui-toml/unwrap_used/unwrap_used.stderr
@@ -1,11 +1,15 @@
-error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a slice
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:38:17
    |
 LL |         let _ = boxed_slice.get(1).unwrap();
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&boxed_slice[1]`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::get-unwrap` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::get_unwrap)]`
+help: using `[]` is clearer and more concise
+   |
+LL |         let _ = &boxed_slice[1];
+   |                 ~~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:38:17
@@ -18,11 +22,16 @@ LL |         let _ = boxed_slice.get(1).unwrap();
    = note: `-D clippy::unwrap-used` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::unwrap_used)]`
 
-error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a slice
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:39:17
    |
 LL |         let _ = some_slice.get(0).unwrap();
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_slice[0]`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _ = &some_slice[0];
+   |                 ~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:39:17
@@ -33,11 +42,16 @@ LL |         let _ = some_slice.get(0).unwrap();
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a Vec
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:40:17
    |
 LL |         let _ = some_vec.get(0).unwrap();
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_vec[0]`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _ = &some_vec[0];
+   |                 ~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:40:17
@@ -48,11 +62,16 @@ LL |         let _ = some_vec.get(0).unwrap();
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a VecDeque
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:41:17
    |
 LL |         let _ = some_vecdeque.get(0).unwrap();
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_vecdeque[0]`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _ = &some_vecdeque[0];
+   |                 ~~~~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:41:17
@@ -63,11 +82,16 @@ LL |         let _ = some_vecdeque.get(0).unwrap();
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a HashMap
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:42:17
    |
 LL |         let _ = some_hashmap.get(&1).unwrap();
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_hashmap[&1]`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _ = &some_hashmap[&1];
+   |                 ~~~~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:42:17
@@ -78,11 +102,16 @@ LL |         let _ = some_hashmap.get(&1).unwrap();
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a BTreeMap
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:43:17
    |
 LL |         let _ = some_btreemap.get(&1).unwrap();
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_btreemap[&1]`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _ = &some_btreemap[&1];
+   |                 ~~~~~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:43:17
@@ -93,11 +122,16 @@ LL |         let _ = some_btreemap.get(&1).unwrap();
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a slice
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:47:21
    |
 LL |         let _: u8 = *boxed_slice.get(1).unwrap();
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice[1]`
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _: u8 = boxed_slice[1];
+   |                     ~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:47:22
@@ -108,11 +142,16 @@ LL |         let _: u8 = *boxed_slice.get(1).unwrap();
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
+error: called `.get_mut().unwrap()` on a slice
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:52:9
    |
 LL |         *boxed_slice.get_mut(0).unwrap() = 1;
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice[0]`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         boxed_slice[0] = 1;
+   |         ~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:52:10
@@ -123,11 +162,16 @@ LL |         *boxed_slice.get_mut(0).unwrap() = 1;
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
+error: called `.get_mut().unwrap()` on a slice
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:53:9
    |
 LL |         *some_slice.get_mut(0).unwrap() = 1;
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_slice[0]`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         some_slice[0] = 1;
+   |         ~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:53:10
@@ -138,11 +182,16 @@ LL |         *some_slice.get_mut(0).unwrap() = 1;
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
+error: called `.get_mut().unwrap()` on a Vec
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:54:9
    |
 LL |         *some_vec.get_mut(0).unwrap() = 1;
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0]`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         some_vec[0] = 1;
+   |         ~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:54:10
@@ -153,11 +202,16 @@ LL |         *some_vec.get_mut(0).unwrap() = 1;
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
+error: called `.get_mut().unwrap()` on a VecDeque
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:55:9
    |
 LL |         *some_vecdeque.get_mut(0).unwrap() = 1;
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vecdeque[0]`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         some_vecdeque[0] = 1;
+   |         ~~~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:55:10
@@ -168,11 +222,16 @@ LL |         *some_vecdeque.get_mut(0).unwrap() = 1;
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a Vec
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:67:17
    |
 LL |         let _ = some_vec.get(0..1).unwrap().to_vec();
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0..1]`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _ = some_vec[0..1].to_vec();
+   |                 ~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:67:17
@@ -183,11 +242,16 @@ LL |         let _ = some_vec.get(0..1).unwrap().to_vec();
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
+error: called `.get_mut().unwrap()` on a Vec
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:68:17
    |
 LL |         let _ = some_vec.get_mut(0..1).unwrap().to_vec();
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0..1]`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _ = some_vec[0..1].to_vec();
+   |                 ~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:68:17
@@ -198,17 +262,27 @@ LL |         let _ = some_vec.get_mut(0..1).unwrap().to_vec();
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a slice
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:75:13
    |
 LL |     let _ = boxed_slice.get(1).unwrap();
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&boxed_slice[1]`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |     let _ = &boxed_slice[1];
+   |             ~~~~~~~~~~~~~~~
 
-error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a slice
   --> tests/ui-toml/unwrap_used/unwrap_used.rs:93:17
    |
 LL |         let _ = Box::new([0]).get(1).unwrap();
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&Box::new([0])[1]`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _ = &Box::new([0])[1];
+   |                 ~~~~~~~~~~~~~~~~~
 
 error: aborting due to 28 previous errors
 
diff --git a/tests/ui/assigning_clones.fixed b/tests/ui/assigning_clones.fixed
index 60f6a385fc506..b376d55a40250 100644
--- a/tests/ui/assigning_clones.fixed
+++ b/tests/ui/assigning_clones.fixed
@@ -3,6 +3,7 @@
 #![allow(clippy::ptr_arg)] // https://github.com/rust-lang/rust-clippy/issues/10612
 #![allow(clippy::needless_late_init)]
 #![allow(clippy::box_collection)]
+#![allow(clippy::boxed_local)]
 #![warn(clippy::assigning_clones)]
 
 use std::borrow::ToOwned;
@@ -182,6 +183,31 @@ impl Clone for AvoidRecursiveCloneFrom {
     }
 }
 
+// Deref handling
+fn clone_into_deref_method(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) {
+    (*a).clone_from(&b);
+}
+
+fn clone_into_deref_with_clone_method(mut a: DerefWrapperWithClone<HasCloneFrom>, b: HasCloneFrom) {
+    (*a).clone_from(&b);
+}
+
+fn clone_into_box_method(mut a: Box<HasCloneFrom>, b: HasCloneFrom) {
+    (*a).clone_from(&b);
+}
+
+fn clone_into_self_deref_method(a: &mut DerefWrapperWithClone<HasCloneFrom>, b: DerefWrapperWithClone<HasCloneFrom>) {
+    a.clone_from(&b);
+}
+
+fn clone_into_deref_function(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) {
+    Clone::clone_from(&mut *a, &b);
+}
+
+fn clone_into_box_function(mut a: Box<HasCloneFrom>, b: HasCloneFrom) {
+    Clone::clone_from(&mut *a, &b);
+}
+
 // ToOwned
 fn owned_method_mut_ref(mut_string: &mut String, ref_str: &str) {
     ref_str.clone_into(mut_string);
@@ -328,3 +354,45 @@ mod borrowck_conflicts {
         path = path.components().as_path().to_owned();
     }
 }
+
+struct DerefWrapper<T>(T);
+
+impl<T> Deref for DerefWrapper<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl<T> DerefMut for DerefWrapper<T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+
+struct DerefWrapperWithClone<T>(T);
+
+impl<T> Deref for DerefWrapperWithClone<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl<T> DerefMut for DerefWrapperWithClone<T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+
+impl<T: Clone> Clone for DerefWrapperWithClone<T> {
+    fn clone(&self) -> Self {
+        Self(self.0.clone())
+    }
+
+    fn clone_from(&mut self, source: &Self) {
+        *self = Self(source.0.clone());
+    }
+}
diff --git a/tests/ui/assigning_clones.rs b/tests/ui/assigning_clones.rs
index 6eb85be511a89..11a5d4459c350 100644
--- a/tests/ui/assigning_clones.rs
+++ b/tests/ui/assigning_clones.rs
@@ -3,6 +3,7 @@
 #![allow(clippy::ptr_arg)] // https://github.com/rust-lang/rust-clippy/issues/10612
 #![allow(clippy::needless_late_init)]
 #![allow(clippy::box_collection)]
+#![allow(clippy::boxed_local)]
 #![warn(clippy::assigning_clones)]
 
 use std::borrow::ToOwned;
@@ -182,6 +183,31 @@ impl Clone for AvoidRecursiveCloneFrom {
     }
 }
 
+// Deref handling
+fn clone_into_deref_method(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) {
+    *a = b.clone();
+}
+
+fn clone_into_deref_with_clone_method(mut a: DerefWrapperWithClone<HasCloneFrom>, b: HasCloneFrom) {
+    *a = b.clone();
+}
+
+fn clone_into_box_method(mut a: Box<HasCloneFrom>, b: HasCloneFrom) {
+    *a = b.clone();
+}
+
+fn clone_into_self_deref_method(a: &mut DerefWrapperWithClone<HasCloneFrom>, b: DerefWrapperWithClone<HasCloneFrom>) {
+    *a = b.clone();
+}
+
+fn clone_into_deref_function(mut a: DerefWrapper<HasCloneFrom>, b: HasCloneFrom) {
+    *a = Clone::clone(&b);
+}
+
+fn clone_into_box_function(mut a: Box<HasCloneFrom>, b: HasCloneFrom) {
+    *a = Clone::clone(&b);
+}
+
 // ToOwned
 fn owned_method_mut_ref(mut_string: &mut String, ref_str: &str) {
     *mut_string = ref_str.to_owned();
@@ -328,3 +354,45 @@ mod borrowck_conflicts {
         path = path.components().as_path().to_owned();
     }
 }
+
+struct DerefWrapper<T>(T);
+
+impl<T> Deref for DerefWrapper<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl<T> DerefMut for DerefWrapper<T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+
+struct DerefWrapperWithClone<T>(T);
+
+impl<T> Deref for DerefWrapperWithClone<T> {
+    type Target = T;
+
+    fn deref(&self) -> &Self::Target {
+        &self.0
+    }
+}
+
+impl<T> DerefMut for DerefWrapperWithClone<T> {
+    fn deref_mut(&mut self) -> &mut Self::Target {
+        &mut self.0
+    }
+}
+
+impl<T: Clone> Clone for DerefWrapperWithClone<T> {
+    fn clone(&self) -> Self {
+        Self(self.0.clone())
+    }
+
+    fn clone_from(&mut self, source: &Self) {
+        *self = Self(source.0.clone());
+    }
+}
diff --git a/tests/ui/assigning_clones.stderr b/tests/ui/assigning_clones.stderr
index a68516376abb5..19724a6d4ec2f 100644
--- a/tests/ui/assigning_clones.stderr
+++ b/tests/ui/assigning_clones.stderr
@@ -1,5 +1,5 @@
 error: assigning the result of `Clone::clone()` may be inefficient
-  --> tests/ui/assigning_clones.rs:24:5
+  --> tests/ui/assigning_clones.rs:25:5
    |
 LL |     *mut_thing = value_thing.clone();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(&value_thing)`
@@ -8,142 +8,178 @@ LL |     *mut_thing = value_thing.clone();
    = help: to override `-D warnings` add `#[allow(clippy::assigning_clones)]`
 
 error: assigning the result of `Clone::clone()` may be inefficient
-  --> tests/ui/assigning_clones.rs:28:5
+  --> tests/ui/assigning_clones.rs:29:5
    |
 LL |     *mut_thing = ref_thing.clone();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing)`
 
 error: assigning the result of `Clone::clone()` may be inefficient
-  --> tests/ui/assigning_clones.rs:32:5
+  --> tests/ui/assigning_clones.rs:33:5
    |
 LL |     mut_thing = ref_thing.clone();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing)`
 
 error: assigning the result of `Clone::clone()` may be inefficient
-  --> tests/ui/assigning_clones.rs:36:5
+  --> tests/ui/assigning_clones.rs:37:5
    |
 LL |     *mut_thing = Clone::clone(ref_thing);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)`
 
 error: assigning the result of `Clone::clone()` may be inefficient
-  --> tests/ui/assigning_clones.rs:40:5
+  --> tests/ui/assigning_clones.rs:41:5
    |
 LL |     mut_thing = Clone::clone(ref_thing);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut mut_thing, ref_thing)`
 
 error: assigning the result of `Clone::clone()` may be inefficient
-  --> tests/ui/assigning_clones.rs:44:5
+  --> tests/ui/assigning_clones.rs:45:5
    |
 LL |     *mut_thing = Clone::clone(ref_thing);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)`
 
 error: assigning the result of `Clone::clone()` may be inefficient
-  --> tests/ui/assigning_clones.rs:48:5
+  --> tests/ui/assigning_clones.rs:49:5
    |
 LL |     *mut_thing = HasCloneFrom::clone(ref_thing);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)`
 
 error: assigning the result of `Clone::clone()` may be inefficient
-  --> tests/ui/assigning_clones.rs:52:5
+  --> tests/ui/assigning_clones.rs:53:5
    |
 LL |     *mut_thing = <HasCloneFrom as Clone>::clone(ref_thing);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(mut_thing, ref_thing)`
 
 error: assigning the result of `Clone::clone()` may be inefficient
-  --> tests/ui/assigning_clones.rs:57:5
+  --> tests/ui/assigning_clones.rs:58:5
    |
 LL |     *(mut_thing + &mut HasCloneFrom) = ref_thing.clone();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `(mut_thing + &mut HasCloneFrom).clone_from(ref_thing)`
 
 error: assigning the result of `Clone::clone()` may be inefficient
-  --> tests/ui/assigning_clones.rs:62:5
+  --> tests/ui/assigning_clones.rs:63:5
    |
 LL |     *mut_thing = (ref_thing + ref_thing).clone();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing + ref_thing)`
 
 error: assigning the result of `Clone::clone()` may be inefficient
-  --> tests/ui/assigning_clones.rs:67:5
+  --> tests/ui/assigning_clones.rs:68:5
    |
 LL |     s = format!("{} {}", "hello", "world").clone();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `s.clone_from(&format!("{} {}", "hello", "world"))`
 
 error: assigning the result of `Clone::clone()` may be inefficient
-  --> tests/ui/assigning_clones.rs:72:5
+  --> tests/ui/assigning_clones.rs:73:5
    |
 LL |     s = Clone::clone(&format!("{} {}", "hello", "world"));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut s, &format!("{} {}", "hello", "world"))`
 
 error: assigning the result of `Clone::clone()` may be inefficient
-  --> tests/ui/assigning_clones.rs:78:9
+  --> tests/ui/assigning_clones.rs:79:9
    |
 LL |         a = b.clone();
    |         ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)`
 
 error: assigning the result of `Clone::clone()` may be inefficient
-  --> tests/ui/assigning_clones.rs:149:5
+  --> tests/ui/assigning_clones.rs:150:5
    |
 LL |     a = b.clone();
    |     ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)`
 
 error: assigning the result of `Clone::clone()` may be inefficient
-  --> tests/ui/assigning_clones.rs:156:5
+  --> tests/ui/assigning_clones.rs:157:5
    |
 LL |     a = b.clone();
    |     ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)`
 
 error: assigning the result of `ToOwned::to_owned()` may be inefficient
-  --> tests/ui/assigning_clones.rs:157:5
+  --> tests/ui/assigning_clones.rs:158:5
    |
 LL |     a = c.to_owned();
    |     ^^^^^^^^^^^^^^^^ help: use `clone_into()`: `c.clone_into(&mut a)`
 
+error: assigning the result of `Clone::clone()` may be inefficient
+  --> tests/ui/assigning_clones.rs:188:5
+   |
+LL |     *a = b.clone();
+   |     ^^^^^^^^^^^^^^ help: use `clone_from()`: `(*a).clone_from(&b)`
+
+error: assigning the result of `Clone::clone()` may be inefficient
+  --> tests/ui/assigning_clones.rs:192:5
+   |
+LL |     *a = b.clone();
+   |     ^^^^^^^^^^^^^^ help: use `clone_from()`: `(*a).clone_from(&b)`
+
+error: assigning the result of `Clone::clone()` may be inefficient
+  --> tests/ui/assigning_clones.rs:196:5
+   |
+LL |     *a = b.clone();
+   |     ^^^^^^^^^^^^^^ help: use `clone_from()`: `(*a).clone_from(&b)`
+
+error: assigning the result of `Clone::clone()` may be inefficient
+  --> tests/ui/assigning_clones.rs:200:5
+   |
+LL |     *a = b.clone();
+   |     ^^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)`
+
+error: assigning the result of `Clone::clone()` may be inefficient
+  --> tests/ui/assigning_clones.rs:204:5
+   |
+LL |     *a = Clone::clone(&b);
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut *a, &b)`
+
+error: assigning the result of `Clone::clone()` may be inefficient
+  --> tests/ui/assigning_clones.rs:208:5
+   |
+LL |     *a = Clone::clone(&b);
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut *a, &b)`
+
 error: assigning the result of `ToOwned::to_owned()` may be inefficient
-  --> tests/ui/assigning_clones.rs:187:5
+  --> tests/ui/assigning_clones.rs:213:5
    |
 LL |     *mut_string = ref_str.to_owned();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(mut_string)`
 
 error: assigning the result of `ToOwned::to_owned()` may be inefficient
-  --> tests/ui/assigning_clones.rs:191:5
+  --> tests/ui/assigning_clones.rs:217:5
    |
 LL |     mut_string = ref_str.to_owned();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut mut_string)`
 
 error: assigning the result of `ToOwned::to_owned()` may be inefficient
-  --> tests/ui/assigning_clones.rs:212:5
+  --> tests/ui/assigning_clones.rs:238:5
    |
 LL |     **mut_box_string = ref_str.to_owned();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))`
 
 error: assigning the result of `ToOwned::to_owned()` may be inefficient
-  --> tests/ui/assigning_clones.rs:216:5
+  --> tests/ui/assigning_clones.rs:242:5
    |
 LL |     **mut_box_string = ref_str.to_owned();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))`
 
 error: assigning the result of `ToOwned::to_owned()` may be inefficient
-  --> tests/ui/assigning_clones.rs:220:5
+  --> tests/ui/assigning_clones.rs:246:5
    |
 LL |     *mut_thing = ToOwned::to_owned(ref_str);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, mut_thing)`
 
 error: assigning the result of `ToOwned::to_owned()` may be inefficient
-  --> tests/ui/assigning_clones.rs:224:5
+  --> tests/ui/assigning_clones.rs:250:5
    |
 LL |     mut_thing = ToOwned::to_owned(ref_str);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, &mut mut_thing)`
 
 error: assigning the result of `ToOwned::to_owned()` may be inefficient
-  --> tests/ui/assigning_clones.rs:229:5
+  --> tests/ui/assigning_clones.rs:255:5
    |
 LL |     s = format!("{} {}", "hello", "world").to_owned();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `format!("{} {}", "hello", "world").clone_into(&mut s)`
 
 error: assigning the result of `ToOwned::to_owned()` may be inefficient
-  --> tests/ui/assigning_clones.rs:234:5
+  --> tests/ui/assigning_clones.rs:260:5
    |
 LL |     s = ToOwned::to_owned(&format!("{} {}", "hello", "world"));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(&format!("{} {}", "hello", "world"), &mut s)`
 
-error: aborting due to 24 previous errors
+error: aborting due to 30 previous errors
 
diff --git a/tests/ui/bind_instead_of_map_multipart.stderr b/tests/ui/bind_instead_of_map_multipart.stderr
index b15857c325ae5..2adaecc96d6a9 100644
--- a/tests/ui/bind_instead_of_map_multipart.stderr
+++ b/tests/ui/bind_instead_of_map_multipart.stderr
@@ -9,7 +9,7 @@ note: the lint level is defined here
    |
 LL | #![deny(clippy::bind_instead_of_map)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-help: try
+help: use `map` instead
    |
 LL |     let _ = Some("42").map(|s| if s.len() < 42 { 0 } else { s.len() });
    |                        ~~~                       ~          ~~~~~~~
@@ -20,7 +20,7 @@ error: using `Result.and_then(|x| Ok(y))`, which is more succinctly expressed as
 LL |     let _ = Ok::<_, ()>("42").and_then(|s| if s.len() < 42 { Ok(0) } else { Ok(s.len()) });
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-help: try
+help: use `map` instead
    |
 LL |     let _ = Ok::<_, ()>("42").map(|s| if s.len() < 42 { 0 } else { s.len() });
    |                               ~~~                       ~          ~~~~~~~
@@ -31,7 +31,7 @@ error: using `Result.or_else(|x| Err(y))`, which is more succinctly expressed as
 LL |     let _ = Err::<(), _>("42").or_else(|s| if s.len() < 42 { Err(s.len() + 20) } else { Err(s.len()) });
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-help: try
+help: use `map_err` instead
    |
 LL |     let _ = Err::<(), _>("42").map_err(|s| if s.len() < 42 { s.len() + 20 } else { s.len() });
    |                                ~~~~~~~                       ~~~~~~~~~~~~          ~~~~~~~
@@ -48,7 +48,7 @@ LL | |         }
 LL | |     });
    | |______^
    |
-help: try
+help: use `map` instead
    |
 LL ~     Some("42").map(|s| {
 LL |         if {
@@ -82,7 +82,7 @@ error: using `Option.and_then(|x| Some(y))`, which is more succinctly expressed
 LL |     let _ = Some("").and_then(|s| if s.len() == 20 { Some(m!()) } else { Some(Some(20)) });
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-help: try
+help: use `map` instead
    |
 LL |     let _ = Some("").map(|s| if s.len() == 20 { m!() } else { Some(20) });
    |                      ~~~                        ~~~~          ~~~~~~~~
diff --git a/tests/ui/crashes/ice-3717.stderr b/tests/ui/crashes/ice-3717.stderr
index 01627ff5b94b9..4b4618ee1bbd0 100644
--- a/tests/ui/crashes/ice-3717.stderr
+++ b/tests/ui/crashes/ice-3717.stderr
@@ -9,14 +9,13 @@ note: the lint level is defined here
    |
 LL | #![deny(clippy::implicit_hasher)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^
-help: consider adding a type parameter
+help: add a type parameter for `BuildHasher`
    |
-LL | pub fn ice_3717<S: ::std::hash::BuildHasher + Default>(_: &HashSet<usize, S>) {
-   |                +++++++++++++++++++++++++++++++++++++++     ~~~~~~~~~~~~~~~~~
-help: ...and use generic constructor
+LL ~ pub fn ice_3717<S: ::std::hash::BuildHasher + Default>(_: &HashSet<usize, S>) {
+LL |
+LL |     let _ = [0u8; 0];
+LL ~     let _: HashSet<usize> = HashSet::default();
    |
-LL |     let _: HashSet<usize> = HashSet::default();
-   |                             ~~~~~~~~~~~~~~~~~~
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/crashes/ice-6254.rs b/tests/ui/crashes/ice-6254.rs
index 8af60890390e7..aaca32ab2d93e 100644
--- a/tests/ui/crashes/ice-6254.rs
+++ b/tests/ui/crashes/ice-6254.rs
@@ -2,7 +2,8 @@
 // panicked at 'assertion failed: rows.iter().all(|r| r.len() == v.len())',
 // compiler/rustc_mir_build/src/thir/pattern/_match.rs:2030:5
 
-#[allow(clippy::derive_partial_eq_without_eq)]
+#![allow(clippy::derive_partial_eq_without_eq, clippy::single_match)]
+
 #[derive(PartialEq)]
 struct Foo(i32);
 const FOO_REF_REF: &&Foo = &&Foo(42);
diff --git a/tests/ui/create_dir.stderr b/tests/ui/create_dir.stderr
index 9c6e640ca784c..ab51705bb55a5 100644
--- a/tests/ui/create_dir.stderr
+++ b/tests/ui/create_dir.stderr
@@ -2,16 +2,25 @@ error: calling `std::fs::create_dir` where there may be a better way
   --> tests/ui/create_dir.rs:10:5
    |
 LL |     std::fs::create_dir("foo");
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `create_dir_all("foo")`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::create-dir` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::create_dir)]`
+help: consider calling `std::fs::create_dir_all` instead
+   |
+LL |     create_dir_all("foo");
+   |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: calling `std::fs::create_dir` where there may be a better way
   --> tests/ui/create_dir.rs:11:5
    |
 LL |     std::fs::create_dir("bar").unwrap();
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `std::fs::create_dir_all` instead: `create_dir_all("bar")`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider calling `std::fs::create_dir_all` instead
+   |
+LL |     create_dir_all("bar").unwrap();
+   |     ~~~~~~~~~~~~~~~~~~~~~
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/dbg_macro/dbg_macro.stderr b/tests/ui/dbg_macro/dbg_macro.stderr
index 7d3c3f7c918e8..b3d74b9ff6173 100644
--- a/tests/ui/dbg_macro/dbg_macro.stderr
+++ b/tests/ui/dbg_macro/dbg_macro.stderr
@@ -81,7 +81,7 @@ error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro.rs:48:5
    |
 LL |     dbg!();
-   |     ^^^^^^^
+   |     ^^^^^^
    |
 help: remove the invocation before committing it to a version control system
    |
@@ -136,7 +136,7 @@ error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/dbg_macro.rs:43:13
    |
 LL |             dbg!();
-   |             ^^^^^^^
+   |             ^^^^^^
 ...
 LL |     expand_to_dbg!();
    |     ---------------- in this macro invocation
diff --git a/tests/ui/dbg_macro/dbg_macro_unfixable.stderr b/tests/ui/dbg_macro/dbg_macro_unfixable.stderr
index 16e51f4742e34..b8e91906b9317 100644
--- a/tests/ui/dbg_macro/dbg_macro_unfixable.stderr
+++ b/tests/ui/dbg_macro/dbg_macro_unfixable.stderr
@@ -2,7 +2,7 @@ error: the `dbg!` macro is intended as a debugging tool
   --> tests/ui/dbg_macro/auxiliary/submodule.rs:2:5
    |
 LL |     dbg!();
-   |     ^^^^^^^
+   |     ^^^^^^
    |
    = note: `-D clippy::dbg-macro` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::dbg_macro)]`
diff --git a/tests/ui/deprecated.rs b/tests/ui/deprecated.rs
index d3c34fb371674..5617db90a470a 100644
--- a/tests/ui/deprecated.rs
+++ b/tests/ui/deprecated.rs
@@ -2,23 +2,18 @@
 // Use that command to update this file and do not edit by hand.
 // Manual edits will be overwritten.
 
-#![warn(clippy::should_assert_eq)]
-#![warn(clippy::extend_from_slice)]
-#![warn(clippy::range_step_by_zero)]
-#![warn(clippy::unstable_as_slice)]
-#![warn(clippy::unstable_as_mut_slice)]
-#![warn(clippy::misaligned_transmute)]
-#![warn(clippy::assign_ops)]
-#![warn(clippy::if_let_redundant_pattern_matching)]
-#![warn(clippy::unsafe_vector_initialization)]
-#![warn(clippy::unused_collect)]
-#![warn(clippy::replace_consts)]
-#![warn(clippy::regex_macro)]
-#![warn(clippy::find_map)]
-#![warn(clippy::filter_map)]
-#![warn(clippy::pub_enum_variant_names)]
-#![warn(clippy::wrong_pub_self_convention)]
-#![warn(clippy::maybe_misused_cfg)]
-#![warn(clippy::mismatched_target_os)]
+#![warn(clippy::should_assert_eq)] //~ ERROR: lint `clippy::should_assert_eq`
+#![warn(clippy::extend_from_slice)] //~ ERROR: lint `clippy::extend_from_slice`
+#![warn(clippy::range_step_by_zero)] //~ ERROR: lint `clippy::range_step_by_zero`
+#![warn(clippy::unstable_as_slice)] //~ ERROR: lint `clippy::unstable_as_slice`
+#![warn(clippy::unstable_as_mut_slice)] //~ ERROR: lint `clippy::unstable_as_mut_slice`
+#![warn(clippy::misaligned_transmute)] //~ ERROR: lint `clippy::misaligned_transmute`
+#![warn(clippy::assign_ops)] //~ ERROR: lint `clippy::assign_ops`
+#![warn(clippy::unsafe_vector_initialization)] //~ ERROR: lint `clippy::unsafe_vector_initialization`
+#![warn(clippy::unused_collect)] //~ ERROR: lint `clippy::unused_collect`
+#![warn(clippy::replace_consts)] //~ ERROR: lint `clippy::replace_consts`
+#![warn(clippy::regex_macro)] //~ ERROR: lint `clippy::regex_macro`
+#![warn(clippy::pub_enum_variant_names)] //~ ERROR: lint `clippy::pub_enum_variant_names`
+#![warn(clippy::wrong_pub_self_convention)] //~ ERROR: lint `clippy::wrong_pub_self_convention`
 
 fn main() {}
diff --git a/tests/ui/deprecated.stderr b/tests/ui/deprecated.stderr
index 49b90c70c06ef..b3e1646c8045b 100644
--- a/tests/ui/deprecated.stderr
+++ b/tests/ui/deprecated.stderr
@@ -1,4 +1,4 @@
-error: lint `clippy::should_assert_eq` has been removed: `assert!()` will be more flexible with RFC 2011
+error: lint `clippy::should_assert_eq` has been removed: `assert!(a == b)` can now print the values the same way `assert_eq!(a, b) can
   --> tests/ui/deprecated.rs:5:9
    |
 LL | #![warn(clippy::should_assert_eq)]
@@ -7,107 +7,77 @@ LL | #![warn(clippy::should_assert_eq)]
    = note: `-D renamed-and-removed-lints` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]`
 
-error: lint `clippy::extend_from_slice` has been removed: `.extend_from_slice(_)` is a faster way to extend a Vec by a slice
+error: lint `clippy::extend_from_slice` has been removed: `Vec::extend_from_slice` is no longer faster than `Vec::extend` due to specialization
   --> tests/ui/deprecated.rs:6:9
    |
 LL | #![warn(clippy::extend_from_slice)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: lint `clippy::range_step_by_zero` has been removed: `iterator.step_by(0)` panics nowadays
+error: lint `clippy::range_step_by_zero` has been removed: `Iterator::step_by(0)` now panics and is no longer an infinite iterator
   --> tests/ui/deprecated.rs:7:9
    |
 LL | #![warn(clippy::range_step_by_zero)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7
+error: lint `clippy::unstable_as_slice` has been removed: `Vec::as_slice` is now stable
   --> tests/ui/deprecated.rs:8:9
    |
 LL | #![warn(clippy::unstable_as_slice)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been stabilized in 1.7
+error: lint `clippy::unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` is now stable
   --> tests/ui/deprecated.rs:9:9
    |
 LL | #![warn(clippy::unstable_as_mut_slice)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: lint `clippy::misaligned_transmute` has been removed: this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr
+error: lint `clippy::misaligned_transmute` has been removed: split into `clippy::cast_ptr_alignment` and `clippy::transmute_ptr_to_ptr`
   --> tests/ui/deprecated.rs:10:9
    |
 LL | #![warn(clippy::misaligned_transmute)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: lint `clippy::assign_ops` has been removed: using compound assignment operators (e.g., `+=`) is harmless
+error: lint `clippy::assign_ops` has been removed: compound operators are harmless and linting on them is not in scope for clippy
   --> tests/ui/deprecated.rs:11:9
    |
 LL | #![warn(clippy::assign_ops)]
    |         ^^^^^^^^^^^^^^^^^^
 
-error: lint `clippy::if_let_redundant_pattern_matching` has been removed: this lint has been changed to redundant_pattern_matching
+error: lint `clippy::unsafe_vector_initialization` has been removed: the suggested alternative could be substantially slower
   --> tests/ui/deprecated.rs:12:9
    |
-LL | #![warn(clippy::if_let_redundant_pattern_matching)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: lint `clippy::unsafe_vector_initialization` has been removed: the replacement suggested by this lint had substantially different behavior
-  --> tests/ui/deprecated.rs:13:9
-   |
 LL | #![warn(clippy::unsafe_vector_initialization)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: lint `clippy::unused_collect` has been removed: `collect` has been marked as #[must_use] in rustc and that covers all cases of this lint
-  --> tests/ui/deprecated.rs:14:9
+error: lint `clippy::unused_collect` has been removed: `Iterator::collect` is now marked as `#[must_use]`
+  --> tests/ui/deprecated.rs:13:9
    |
 LL | #![warn(clippy::unused_collect)]
    |         ^^^^^^^^^^^^^^^^^^^^^^
 
-error: lint `clippy::replace_consts` has been removed: associated-constants `MIN`/`MAX` of integers are preferred to `{min,max}_value()` and module constants
-  --> tests/ui/deprecated.rs:15:9
+error: lint `clippy::replace_consts` has been removed: `min_value` and `max_value` are now deprecated
+  --> tests/ui/deprecated.rs:14:9
    |
 LL | #![warn(clippy::replace_consts)]
    |         ^^^^^^^^^^^^^^^^^^^^^^
 
-error: lint `clippy::regex_macro` has been removed: the regex! macro has been removed from the regex crate in 2018
-  --> tests/ui/deprecated.rs:16:9
+error: lint `clippy::regex_macro` has been removed: the `regex!` macro was removed from the regex crate in 2018
+  --> tests/ui/deprecated.rs:15:9
    |
 LL | #![warn(clippy::regex_macro)]
    |         ^^^^^^^^^^^^^^^^^^^
 
-error: lint `clippy::find_map` has been removed: this lint has been replaced by `manual_find_map`, a more specific lint
-  --> tests/ui/deprecated.rs:17:9
-   |
-LL | #![warn(clippy::find_map)]
-   |         ^^^^^^^^^^^^^^^^
-
-error: lint `clippy::filter_map` has been removed: this lint has been replaced by `manual_filter_map`, a more specific lint
-  --> tests/ui/deprecated.rs:18:9
-   |
-LL | #![warn(clippy::filter_map)]
-   |         ^^^^^^^^^^^^^^^^^^
-
-error: lint `clippy::pub_enum_variant_names` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `enum_variant_names` lint for public items
-  --> tests/ui/deprecated.rs:19:9
+error: lint `clippy::pub_enum_variant_names` has been removed: `clippy::enum_variant_names` now covers this case via the `avoid-breaking-exported-api` config
+  --> tests/ui/deprecated.rs:16:9
    |
 LL | #![warn(clippy::pub_enum_variant_names)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: lint `clippy::wrong_pub_self_convention` has been removed: set the `avoid-breaking-exported-api` config option to `false` to enable the `wrong_self_convention` lint for public items
-  --> tests/ui/deprecated.rs:20:9
+error: lint `clippy::wrong_pub_self_convention` has been removed: `clippy::wrong_self_convention` now covers this case via the `avoid-breaking-exported-api` config
+  --> tests/ui/deprecated.rs:17:9
    |
 LL | #![warn(clippy::wrong_pub_self_convention)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: lint `clippy::maybe_misused_cfg` has been removed: this lint has been replaced by `unexpected_cfgs`
-  --> tests/ui/deprecated.rs:21:9
-   |
-LL | #![warn(clippy::maybe_misused_cfg)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: lint `clippy::mismatched_target_os` has been removed: this lint has been replaced by `unexpected_cfgs`
-  --> tests/ui/deprecated.rs:22:9
-   |
-LL | #![warn(clippy::mismatched_target_os)]
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 18 previous errors
+error: aborting due to 13 previous errors
 
diff --git a/tests/ui/deprecated_old.rs b/tests/ui/deprecated_old.rs
deleted file mode 100644
index 356ad5f060b6c..0000000000000
--- a/tests/ui/deprecated_old.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-#[warn(unstable_as_slice)]
-//~^ ERROR: lint `unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized
-//~| NOTE: `-D renamed-and-removed-lints` implied by `-D warnings`
-#[warn(unstable_as_mut_slice)]
-//~^ ERROR: lint `unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been st
-#[warn(misaligned_transmute)]
-//~^ ERROR: lint `misaligned_transmute` has been removed: this lint has been split into ca
-
-fn main() {}
diff --git a/tests/ui/deprecated_old.stderr b/tests/ui/deprecated_old.stderr
deleted file mode 100644
index 685bca64df59a..0000000000000
--- a/tests/ui/deprecated_old.stderr
+++ /dev/null
@@ -1,23 +0,0 @@
-error: lint `unstable_as_slice` has been removed: `Vec::as_slice` has been stabilized in 1.7
-  --> tests/ui/deprecated_old.rs:1:8
-   |
-LL | #[warn(unstable_as_slice)]
-   |        ^^^^^^^^^^^^^^^^^
-   |
-   = note: `-D renamed-and-removed-lints` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]`
-
-error: lint `unstable_as_mut_slice` has been removed: `Vec::as_mut_slice` has been stabilized in 1.7
-  --> tests/ui/deprecated_old.rs:4:8
-   |
-LL | #[warn(unstable_as_mut_slice)]
-   |        ^^^^^^^^^^^^^^^^^^^^^
-
-error: lint `misaligned_transmute` has been removed: this lint has been split into cast_ptr_alignment and transmute_ptr_to_ptr
-  --> tests/ui/deprecated_old.rs:6:8
-   |
-LL | #[warn(misaligned_transmute)]
-   |        ^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 3 previous errors
-
diff --git a/tests/ui/deref_by_slicing.fixed b/tests/ui/deref_by_slicing.fixed
index a3c2e84566681..87b33b1f881d8 100644
--- a/tests/ui/deref_by_slicing.fixed
+++ b/tests/ui/deref_by_slicing.fixed
@@ -25,4 +25,8 @@ fn main() {
 
     let bytes: &[u8] = &[];
     let _ = (&*bytes).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice
+
+    // issue 12751
+    let a = &mut [1, 2, 3][..];
+    let _ = &*a;
 }
diff --git a/tests/ui/deref_by_slicing.rs b/tests/ui/deref_by_slicing.rs
index 5b4a73712ee6e..8d8882a1781e8 100644
--- a/tests/ui/deref_by_slicing.rs
+++ b/tests/ui/deref_by_slicing.rs
@@ -25,4 +25,8 @@ fn main() {
 
     let bytes: &[u8] = &[];
     let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice
+
+    // issue 12751
+    let a = &mut [1, 2, 3][..];
+    let _ = &a[..];
 }
diff --git a/tests/ui/deref_by_slicing.stderr b/tests/ui/deref_by_slicing.stderr
index 17b00610899d6..ceb9ab6db73dc 100644
--- a/tests/ui/deref_by_slicing.stderr
+++ b/tests/ui/deref_by_slicing.stderr
@@ -55,5 +55,11 @@ error: slicing when dereferencing would work
 LL |     let _ = (&bytes[..]).read_to_end(&mut vec![]).unwrap(); // Err, re-borrows slice
    |             ^^^^^^^^^^^^ help: reborrow the original value instead: `(&*bytes)`
 
-error: aborting due to 9 previous errors
+error: slicing when dereferencing would work
+  --> tests/ui/deref_by_slicing.rs:31:13
+   |
+LL |     let _ = &a[..];
+   |             ^^^^^^ help: reborrow the original value instead: `&*a`
+
+error: aborting due to 10 previous errors
 
diff --git a/tests/ui/doc/footnote_issue_13183.rs b/tests/ui/doc/footnote_issue_13183.rs
new file mode 100644
index 0000000000000..a18b4c3ac539b
--- /dev/null
+++ b/tests/ui/doc/footnote_issue_13183.rs
@@ -0,0 +1,10 @@
+// This is a regression test for <https://github.com/rust-lang/rust-clippy/issues/13183>.
+// It should not warn on missing backticks on footnote references.
+
+#![warn(clippy::doc_markdown)]
+// Should not warn!
+//! Here's a footnote[^example_footnote_identifier]
+//!
+//! [^example_footnote_identifier]: This is merely an example.
+
+fn main() {}
diff --git a/tests/ui/empty_drop.stderr b/tests/ui/empty_drop.stderr
index 4223ddaf3fba5..d4d020fec30cf 100644
--- a/tests/ui/empty_drop.stderr
+++ b/tests/ui/empty_drop.stderr
@@ -4,10 +4,11 @@ error: empty drop implementation
 LL | / impl Drop for Foo {
 LL | |     fn drop(&mut self) {}
 LL | | }
-   | |_^ help: try removing this impl
+   | |_^
    |
    = note: `-D clippy::empty-drop` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::empty_drop)]`
+   = help: try removing this impl
 
 error: empty drop implementation
   --> tests/ui/empty_drop.rs:23:1
@@ -17,7 +18,9 @@ LL | |     fn drop(&mut self) {
 LL | |         {}
 LL | |     }
 LL | | }
-   | |_^ help: try removing this impl
+   | |_^
+   |
+   = help: try removing this impl
 
 error: aborting due to 2 previous errors
 
diff --git a/tests/ui/eta.fixed b/tests/ui/eta.fixed
index 7126f2799455f..ca422ee29c104 100644
--- a/tests/ui/eta.fixed
+++ b/tests/ui/eta.fixed
@@ -2,6 +2,7 @@
 #![allow(unused)]
 #![allow(
     clippy::needless_borrow,
+    clippy::needless_option_as_deref,
     clippy::needless_pass_by_value,
     clippy::no_effect,
     clippy::option_map_unit_fn,
@@ -482,3 +483,19 @@ mod issue_12853 {
         x();
     }
 }
+
+mod issue_13073 {
+    fn get_default() -> Option<&'static str> {
+        Some("foo")
+    }
+
+    pub fn foo() {
+        // shouldn't lint
+        let bind: Option<String> = None;
+        let _field = bind.as_deref().or_else(|| get_default()).unwrap();
+        let bind: Option<&'static str> = None;
+        let _field = bind.as_deref().or_else(|| get_default()).unwrap();
+        // should lint
+        let _field = bind.or_else(get_default).unwrap();
+    }
+}
diff --git a/tests/ui/eta.rs b/tests/ui/eta.rs
index 0787abf5f3e37..c0db91c03ef45 100644
--- a/tests/ui/eta.rs
+++ b/tests/ui/eta.rs
@@ -2,6 +2,7 @@
 #![allow(unused)]
 #![allow(
     clippy::needless_borrow,
+    clippy::needless_option_as_deref,
     clippy::needless_pass_by_value,
     clippy::no_effect,
     clippy::option_map_unit_fn,
@@ -482,3 +483,19 @@ mod issue_12853 {
         x();
     }
 }
+
+mod issue_13073 {
+    fn get_default() -> Option<&'static str> {
+        Some("foo")
+    }
+
+    pub fn foo() {
+        // shouldn't lint
+        let bind: Option<String> = None;
+        let _field = bind.as_deref().or_else(|| get_default()).unwrap();
+        let bind: Option<&'static str> = None;
+        let _field = bind.as_deref().or_else(|| get_default()).unwrap();
+        // should lint
+        let _field = bind.or_else(|| get_default()).unwrap();
+    }
+}
diff --git a/tests/ui/eta.stderr b/tests/ui/eta.stderr
index c757601042f16..5540261fc5744 100644
--- a/tests/ui/eta.stderr
+++ b/tests/ui/eta.stderr
@@ -1,5 +1,5 @@
 error: redundant closure
-  --> tests/ui/eta.rs:29:27
+  --> tests/ui/eta.rs:30:27
    |
 LL |     let a = Some(1u8).map(|a| foo(a));
    |                           ^^^^^^^^^^ help: replace the closure with the function itself: `foo`
@@ -8,31 +8,31 @@ LL |     let a = Some(1u8).map(|a| foo(a));
    = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]`
 
 error: redundant closure
-  --> tests/ui/eta.rs:33:40
+  --> tests/ui/eta.rs:34:40
    |
 LL |     let _: Option<Vec<u8>> = true.then(|| vec![]); // special case vec!
    |                                        ^^^^^^^^^ help: replace the closure with `Vec::new`: `std::vec::Vec::new`
 
 error: redundant closure
-  --> tests/ui/eta.rs:34:35
+  --> tests/ui/eta.rs:35:35
    |
 LL |     let d = Some(1u8).map(|a| foo((|b| foo2(b))(a))); //is adjusted?
    |                                   ^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo2`
 
 error: redundant closure
-  --> tests/ui/eta.rs:35:26
+  --> tests/ui/eta.rs:36:26
    |
 LL |     all(&[1, 2, 3], &&2, |x, y| below(x, y)); //is adjusted
    |                          ^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `below`
 
 error: redundant closure
-  --> tests/ui/eta.rs:42:27
+  --> tests/ui/eta.rs:43:27
    |
 LL |     let e = Some(1u8).map(|a| generic(a));
    |                           ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `generic`
 
 error: redundant closure
-  --> tests/ui/eta.rs:94:51
+  --> tests/ui/eta.rs:95:51
    |
 LL |     let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
    |                                                   ^^^^^^^^^^^ help: replace the closure with the method itself: `TestStruct::foo`
@@ -41,166 +41,172 @@ LL |     let e = Some(TestStruct { some_ref: &i }).map(|a| a.foo());
    = help: to override `-D warnings` add `#[allow(clippy::redundant_closure_for_method_calls)]`
 
 error: redundant closure
-  --> tests/ui/eta.rs:95:51
+  --> tests/ui/eta.rs:96:51
    |
 LL |     let e = Some(TestStruct { some_ref: &i }).map(|a| a.trait_foo());
    |                                                   ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `TestTrait::trait_foo`
 
 error: redundant closure
-  --> tests/ui/eta.rs:97:42
+  --> tests/ui/eta.rs:98:42
    |
 LL |     let e = Some(&mut vec![1, 2, 3]).map(|v| v.clear());
    |                                          ^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::vec::Vec::clear`
 
 error: redundant closure
-  --> tests/ui/eta.rs:101:29
+  --> tests/ui/eta.rs:102:29
    |
 LL |     let e = Some("str").map(|s| s.to_string());
    |                             ^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `std::string::ToString::to_string`
 
 error: redundant closure
-  --> tests/ui/eta.rs:102:27
+  --> tests/ui/eta.rs:103:27
    |
 LL |     let e = Some('a').map(|s| s.to_uppercase());
    |                           ^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_uppercase`
 
 error: redundant closure
-  --> tests/ui/eta.rs:104:65
+  --> tests/ui/eta.rs:105:65
    |
 LL |     let e: std::vec::Vec<char> = vec!['a', 'b', 'c'].iter().map(|c| c.to_ascii_uppercase()).collect();
    |                                                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `char::to_ascii_uppercase`
 
 error: redundant closure
-  --> tests/ui/eta.rs:167:22
+  --> tests/ui/eta.rs:168:22
    |
 LL |     requires_fn_once(|| x());
    |                      ^^^^^^ help: replace the closure with the function itself: `x`
 
 error: redundant closure
-  --> tests/ui/eta.rs:174:27
+  --> tests/ui/eta.rs:175:27
    |
 LL |     let a = Some(1u8).map(|a| foo_ptr(a));
    |                           ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `foo_ptr`
 
 error: redundant closure
-  --> tests/ui/eta.rs:179:27
+  --> tests/ui/eta.rs:180:27
    |
 LL |     let a = Some(1u8).map(|a| closure(a));
    |                           ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `closure`
 
 error: redundant closure
-  --> tests/ui/eta.rs:211:28
+  --> tests/ui/eta.rs:212:28
    |
 LL |     x.into_iter().for_each(|x| add_to_res(x));
    |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
 
 error: redundant closure
-  --> tests/ui/eta.rs:212:28
+  --> tests/ui/eta.rs:213:28
    |
 LL |     y.into_iter().for_each(|x| add_to_res(x));
    |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut add_to_res`
 
 error: redundant closure
-  --> tests/ui/eta.rs:213:28
+  --> tests/ui/eta.rs:214:28
    |
 LL |     z.into_iter().for_each(|x| add_to_res(x));
    |                            ^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `add_to_res`
 
 error: redundant closure
-  --> tests/ui/eta.rs:220:21
+  --> tests/ui/eta.rs:221:21
    |
 LL |         Some(1).map(|n| closure(n));
    |                     ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut closure`
 
 error: redundant closure
-  --> tests/ui/eta.rs:224:21
+  --> tests/ui/eta.rs:225:21
    |
 LL |         Some(1).map(|n| in_loop(n));
    |                     ^^^^^^^^^^^^^^ help: replace the closure with the function itself: `in_loop`
 
 error: redundant closure
-  --> tests/ui/eta.rs:317:18
+  --> tests/ui/eta.rs:318:18
    |
 LL |     takes_fn_mut(|| f());
    |                  ^^^^^^ help: replace the closure with the function itself: `&mut f`
 
 error: redundant closure
-  --> tests/ui/eta.rs:320:19
+  --> tests/ui/eta.rs:321:19
    |
 LL |     takes_fn_once(|| f());
    |                   ^^^^^^ help: replace the closure with the function itself: `&mut f`
 
 error: redundant closure
-  --> tests/ui/eta.rs:324:26
+  --> tests/ui/eta.rs:325:26
    |
 LL |     move || takes_fn_mut(|| f_used_once())
    |                          ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `&mut f_used_once`
 
 error: redundant closure
-  --> tests/ui/eta.rs:336:19
+  --> tests/ui/eta.rs:337:19
    |
 LL |     array_opt.map(|a| a.as_slice());
    |                   ^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8; 3]>::as_slice`
 
 error: redundant closure
-  --> tests/ui/eta.rs:339:19
+  --> tests/ui/eta.rs:340:19
    |
 LL |     slice_opt.map(|s| s.len());
    |                   ^^^^^^^^^^^ help: replace the closure with the method itself: `<[u8]>::len`
 
 error: redundant closure
-  --> tests/ui/eta.rs:342:17
+  --> tests/ui/eta.rs:343:17
    |
 LL |     ptr_opt.map(|p| p.is_null());
    |                 ^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<*const usize>::is_null`
 
 error: redundant closure
-  --> tests/ui/eta.rs:346:17
+  --> tests/ui/eta.rs:347:17
    |
 LL |     dyn_opt.map(|d| d.method_on_dyn());
    |                 ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<dyn TestTrait>::method_on_dyn`
 
 error: redundant closure
-  --> tests/ui/eta.rs:406:19
+  --> tests/ui/eta.rs:407:19
    |
 LL |     let _ = f(&0, |x, y| f2(x, y));
    |                   ^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `f2`
 
 error: redundant closure
-  --> tests/ui/eta.rs:434:22
+  --> tests/ui/eta.rs:435:22
    |
 LL |             test.map(|t| t.method())
    |                      ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `Test::method`
 
 error: redundant closure
-  --> tests/ui/eta.rs:438:22
+  --> tests/ui/eta.rs:439:22
    |
 LL |             test.map(|t| t.method())
    |                      ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `super::Outer::method`
 
 error: redundant closure
-  --> tests/ui/eta.rs:451:18
+  --> tests/ui/eta.rs:452:18
    |
 LL |         test.map(|t| t.method())
    |                  ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `test_mod::Test::method`
 
 error: redundant closure
-  --> tests/ui/eta.rs:458:30
+  --> tests/ui/eta.rs:459:30
    |
 LL |                     test.map(|t| t.method())
    |                              ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `crate::issue_10854::d::Test::method`
 
 error: redundant closure
-  --> tests/ui/eta.rs:477:38
+  --> tests/ui/eta.rs:478:38
    |
 LL |         let x = Box::new(|| None.map(|x| f(x)));
    |                                      ^^^^^^^^ help: replace the closure with the function itself: `&f`
 
 error: redundant closure
-  --> tests/ui/eta.rs:481:38
+  --> tests/ui/eta.rs:482:38
    |
 LL |         let x = Box::new(|| None.map(|x| f(x)));
    |                                      ^^^^^^^^ help: replace the closure with the function itself: `f`
 
-error: aborting due to 33 previous errors
+error: redundant closure
+  --> tests/ui/eta.rs:499:35
+   |
+LL |         let _field = bind.or_else(|| get_default()).unwrap();
+   |                                   ^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `get_default`
+
+error: aborting due to 34 previous errors
 
diff --git a/tests/ui/excessive_precision.stderr b/tests/ui/excessive_precision.stderr
index 6d8e166a649d8..81e4fb6765d0b 100644
--- a/tests/ui/excessive_precision.stderr
+++ b/tests/ui/excessive_precision.stderr
@@ -2,100 +2,179 @@ error: float has excessive precision
   --> tests/ui/excessive_precision.rs:20:26
    |
 LL |     const BAD32_1: f32 = 0.123_456_789_f32;
-   |                          ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79_f32`
+   |                          ^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::excessive-precision` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::excessive_precision)]`
+help: consider changing the type or truncating it to
+   |
+LL |     const BAD32_1: f32 = 0.123_456_79_f32;
+   |                          ~~~~~~~~~~~~~~~~
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:21:26
    |
 LL |     const BAD32_2: f32 = 0.123_456_789;
-   |                          ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79`
+   |                          ^^^^^^^^^^^^^
+   |
+help: consider changing the type or truncating it to
+   |
+LL |     const BAD32_2: f32 = 0.123_456_79;
+   |                          ~~~~~~~~~~~~
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:22:26
    |
 LL |     const BAD32_3: f32 = 0.100_000_000_000_1;
-   |                          ^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1`
+   |                          ^^^^^^^^^^^^^^^^^^^
+   |
+help: consider changing the type or truncating it to
+   |
+LL |     const BAD32_3: f32 = 0.1;
+   |                          ~~~
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:23:29
    |
 LL |     const BAD32_EDGE: f32 = 1.000_000_9;
-   |                             ^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.000_001`
+   |                             ^^^^^^^^^^^
+   |
+help: consider changing the type or truncating it to
+   |
+LL |     const BAD32_EDGE: f32 = 1.000_001;
+   |                             ~~~~~~~~~
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:27:26
    |
 LL |     const BAD64_3: f64 = 0.100_000_000_000_000_000_1;
-   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.1`
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider changing the type or truncating it to
+   |
+LL |     const BAD64_3: f64 = 0.1;
+   |                          ~~~
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:30:22
    |
 LL |     println!("{:?}", 8.888_888_888_888_888_888_888);
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `8.888_888_888_888_89`
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider changing the type or truncating it to
+   |
+LL |     println!("{:?}", 8.888_888_888_888_89);
+   |                      ~~~~~~~~~~~~~~~~~~~~
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:41:22
    |
 LL |     let bad32: f32 = 1.123_456_789;
-   |                      ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8`
+   |                      ^^^^^^^^^^^^^
+   |
+help: consider changing the type or truncating it to
+   |
+LL |     let bad32: f32 = 1.123_456_8;
+   |                      ~~~~~~~~~~~
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:42:26
    |
 LL |     let bad32_suf: f32 = 1.123_456_789_f32;
-   |                          ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32`
+   |                          ^^^^^^^^^^^^^^^^^
+   |
+help: consider changing the type or truncating it to
+   |
+LL |     let bad32_suf: f32 = 1.123_456_8_f32;
+   |                          ~~~~~~~~~~~~~~~
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:43:21
    |
 LL |     let bad32_inf = 1.123_456_789_f32;
-   |                     ^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8_f32`
+   |                     ^^^^^^^^^^^^^^^^^
+   |
+help: consider changing the type or truncating it to
+   |
+LL |     let bad32_inf = 1.123_456_8_f32;
+   |                     ~~~~~~~~~~~~~~~
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:53:36
    |
 LL |     let bad_vec32: Vec<f32> = vec![0.123_456_789];
-   |                                    ^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_79`
+   |                                    ^^^^^^^^^^^^^
+   |
+help: consider changing the type or truncating it to
+   |
+LL |     let bad_vec32: Vec<f32> = vec![0.123_456_79];
+   |                                    ~~~~~~~~~~~~
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:54:36
    |
 LL |     let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_789];
-   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0.123_456_789_123_456_78`
+   |                                    ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider changing the type or truncating it to
+   |
+LL |     let bad_vec64: Vec<f64> = vec![0.123_456_789_123_456_78];
+   |                                    ~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:58:24
    |
 LL |     let bad_e32: f32 = 1.123_456_788_888e-10;
-   |                        ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8e-10`
+   |                        ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider changing the type or truncating it to
+   |
+LL |     let bad_e32: f32 = 1.123_456_8e-10;
+   |                        ~~~~~~~~~~~~~~~
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:61:27
    |
 LL |     let bad_bige32: f32 = 1.123_456_788_888E-10;
-   |                           ^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `1.123_456_8E-10`
+   |                           ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider changing the type or truncating it to
+   |
+LL |     let bad_bige32: f32 = 1.123_456_8E-10;
+   |                           ~~~~~~~~~~~~~~~
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:70:13
    |
 LL |     let _ = 2.225_073_858_507_201_1e-308_f64;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `2.225_073_858_507_201e-308_f64`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider changing the type or truncating it to
+   |
+LL |     let _ = 2.225_073_858_507_201e-308_f64;
+   |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:73:13
    |
 LL |     let _ = 1.000_000_000_000_001e-324_f64;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `0_f64`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider changing the type or truncating it to
+   |
+LL |     let _ = 0_f64;
+   |             ~~~~~
 
 error: float has excessive precision
   --> tests/ui/excessive_precision.rs:83:20
    |
 LL |     const _: f64 = 3.0000000000000000e+00;
-   |                    ^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or truncating it to: `3.0`
+   |                    ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider changing the type or truncating it to
+   |
+LL |     const _: f64 = 3.0;
+   |                    ~~~
 
 error: aborting due to 16 previous errors
 
diff --git a/tests/ui/explicit_counter_loop.rs b/tests/ui/explicit_counter_loop.rs
index c25e79a36171f..28b477b69215b 100644
--- a/tests/ui/explicit_counter_loop.rs
+++ b/tests/ui/explicit_counter_loop.rs
@@ -278,3 +278,16 @@ mod issue_10058 {
         }
     }
 }
+
+mod issue_13123 {
+    pub fn test() {
+        let mut vec = vec![1, 2, 3, 4];
+        let mut _index = 0;
+        'label: for v in vec {
+            _index += 1;
+            if v == 1 {
+                break 'label;
+            }
+        }
+    }
+}
diff --git a/tests/ui/explicit_counter_loop.stderr b/tests/ui/explicit_counter_loop.stderr
index e28f8783f9c36..1b2d1f8570a3e 100644
--- a/tests/ui/explicit_counter_loop.stderr
+++ b/tests/ui/explicit_counter_loop.stderr
@@ -57,5 +57,11 @@ LL |         for _item in slice {
    |
    = note: `idx_u32` is of type `u32`, making it ineligible for `Iterator::enumerate`
 
-error: aborting due to 9 previous errors
+error: the variable `_index` is used as a loop counter
+  --> tests/ui/explicit_counter_loop.rs:286:9
+   |
+LL |         'label: for v in vec {
+   |         ^^^^^^^^^^^^^^^^^^^^ help: consider using: `'label: for (_index, v) in vec.into_iter().enumerate()`
+
+error: aborting due to 10 previous errors
 
diff --git a/tests/ui/fn_to_numeric_cast_any.stderr b/tests/ui/fn_to_numeric_cast_any.stderr
index e5bb8d1326975..a05b7138bc99b 100644
--- a/tests/ui/fn_to_numeric_cast_any.stderr
+++ b/tests/ui/fn_to_numeric_cast_any.stderr
@@ -2,106 +2,190 @@ error: casting function pointer `foo` to `i8`
   --> tests/ui/fn_to_numeric_cast_any.rs:23:13
    |
 LL |     let _ = foo as i8;
-   |             ^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i8`
+   |             ^^^^^^^^^
    |
    = note: `-D clippy::fn-to-numeric-cast-any` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::fn_to_numeric_cast_any)]`
+help: did you mean to invoke the function?
+   |
+LL |     let _ = foo() as i8;
+   |             ~~~~~~~~~~~
 
 error: casting function pointer `foo` to `i16`
   --> tests/ui/fn_to_numeric_cast_any.rs:26:13
    |
 LL |     let _ = foo as i16;
-   |             ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i16`
+   |             ^^^^^^^^^^
+   |
+help: did you mean to invoke the function?
+   |
+LL |     let _ = foo() as i16;
+   |             ~~~~~~~~~~~~
 
 error: casting function pointer `foo` to `i32`
   --> tests/ui/fn_to_numeric_cast_any.rs:28:13
    |
 LL |     let _ = foo as i32;
-   |             ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i32`
+   |             ^^^^^^^^^^
+   |
+help: did you mean to invoke the function?
+   |
+LL |     let _ = foo() as i32;
+   |             ~~~~~~~~~~~~
 
 error: casting function pointer `foo` to `i64`
   --> tests/ui/fn_to_numeric_cast_any.rs:30:13
    |
 LL |     let _ = foo as i64;
-   |             ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i64`
+   |             ^^^^^^^^^^
+   |
+help: did you mean to invoke the function?
+   |
+LL |     let _ = foo() as i64;
+   |             ~~~~~~~~~~~~
 
 error: casting function pointer `foo` to `i128`
   --> tests/ui/fn_to_numeric_cast_any.rs:32:13
    |
 LL |     let _ = foo as i128;
-   |             ^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as i128`
+   |             ^^^^^^^^^^^
+   |
+help: did you mean to invoke the function?
+   |
+LL |     let _ = foo() as i128;
+   |             ~~~~~~~~~~~~~
 
 error: casting function pointer `foo` to `isize`
   --> tests/ui/fn_to_numeric_cast_any.rs:34:13
    |
 LL |     let _ = foo as isize;
-   |             ^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as isize`
+   |             ^^^^^^^^^^^^
+   |
+help: did you mean to invoke the function?
+   |
+LL |     let _ = foo() as isize;
+   |             ~~~~~~~~~~~~~~
 
 error: casting function pointer `foo` to `u8`
   --> tests/ui/fn_to_numeric_cast_any.rs:37:13
    |
 LL |     let _ = foo as u8;
-   |             ^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u8`
+   |             ^^^^^^^^^
+   |
+help: did you mean to invoke the function?
+   |
+LL |     let _ = foo() as u8;
+   |             ~~~~~~~~~~~
 
 error: casting function pointer `foo` to `u16`
   --> tests/ui/fn_to_numeric_cast_any.rs:39:13
    |
 LL |     let _ = foo as u16;
-   |             ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u16`
+   |             ^^^^^^^^^^
+   |
+help: did you mean to invoke the function?
+   |
+LL |     let _ = foo() as u16;
+   |             ~~~~~~~~~~~~
 
 error: casting function pointer `foo` to `u32`
   --> tests/ui/fn_to_numeric_cast_any.rs:41:13
    |
 LL |     let _ = foo as u32;
-   |             ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u32`
+   |             ^^^^^^^^^^
+   |
+help: did you mean to invoke the function?
+   |
+LL |     let _ = foo() as u32;
+   |             ~~~~~~~~~~~~
 
 error: casting function pointer `foo` to `u64`
   --> tests/ui/fn_to_numeric_cast_any.rs:43:13
    |
 LL |     let _ = foo as u64;
-   |             ^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u64`
+   |             ^^^^^^^^^^
+   |
+help: did you mean to invoke the function?
+   |
+LL |     let _ = foo() as u64;
+   |             ~~~~~~~~~~~~
 
 error: casting function pointer `foo` to `u128`
   --> tests/ui/fn_to_numeric_cast_any.rs:45:13
    |
 LL |     let _ = foo as u128;
-   |             ^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as u128`
+   |             ^^^^^^^^^^^
+   |
+help: did you mean to invoke the function?
+   |
+LL |     let _ = foo() as u128;
+   |             ~~~~~~~~~~~~~
 
 error: casting function pointer `foo` to `usize`
   --> tests/ui/fn_to_numeric_cast_any.rs:47:13
    |
 LL |     let _ = foo as usize;
-   |             ^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as usize`
+   |             ^^^^^^^^^^^^
+   |
+help: did you mean to invoke the function?
+   |
+LL |     let _ = foo() as usize;
+   |             ~~~~~~~~~~~~~~
 
 error: casting function pointer `Struct::static_method` to `usize`
   --> tests/ui/fn_to_numeric_cast_any.rs:52:13
    |
 LL |     let _ = Struct::static_method as usize;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `Struct::static_method() as usize`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: did you mean to invoke the function?
+   |
+LL |     let _ = Struct::static_method() as usize;
+   |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: casting function pointer `f` to `usize`
   --> tests/ui/fn_to_numeric_cast_any.rs:57:5
    |
 LL |     f as usize
-   |     ^^^^^^^^^^ help: did you mean to invoke the function?: `f() as usize`
+   |     ^^^^^^^^^^
+   |
+help: did you mean to invoke the function?
+   |
+LL |     f() as usize
+   |
 
 error: casting function pointer `T::static_method` to `usize`
   --> tests/ui/fn_to_numeric_cast_any.rs:62:5
    |
 LL |     T::static_method as usize
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `T::static_method() as usize`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: did you mean to invoke the function?
+   |
+LL |     T::static_method() as usize
+   |
 
 error: casting function pointer `(clos as fn(u32) -> u32)` to `usize`
   --> tests/ui/fn_to_numeric_cast_any.rs:69:13
    |
 LL |     let _ = (clos as fn(u32) -> u32) as usize;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `(clos as fn(u32) -> u32)() as usize`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: did you mean to invoke the function?
+   |
+LL |     let _ = (clos as fn(u32) -> u32)() as usize;
+   |             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: casting function pointer `foo` to `*const ()`
   --> tests/ui/fn_to_numeric_cast_any.rs:74:13
    |
 LL |     let _ = foo as *const ();
-   |             ^^^^^^^^^^^^^^^^ help: did you mean to invoke the function?: `foo() as *const ()`
+   |             ^^^^^^^^^^^^^^^^
+   |
+help: did you mean to invoke the function?
+   |
+LL |     let _ = foo() as *const ();
+   |             ~~~~~~~~~~~~~~~~~~
 
 error: aborting due to 17 previous errors
 
diff --git a/tests/ui/for_kv_map.fixed b/tests/ui/for_kv_map.fixed
index a2112d7b73005..1733b29128fe5 100644
--- a/tests/ui/for_kv_map.fixed
+++ b/tests/ui/for_kv_map.fixed
@@ -40,6 +40,16 @@ fn main() {
         let _k = k;
     }
 
+    let m: HashMap<u64, u64> = HashMap::new();
+    let rm = &m;
+    'label: for k in rm.keys() {
+        //~^ ERROR: you seem to want to iterate on a map's keys
+        let _k = k;
+        if *k == 0u64 {
+            break 'label;
+        }
+    }
+
     // The following should not produce warnings.
 
     let m: HashMap<u64, u64> = HashMap::new();
diff --git a/tests/ui/for_kv_map.rs b/tests/ui/for_kv_map.rs
index 1b7959b8f92d6..de465a7c8e6e3 100644
--- a/tests/ui/for_kv_map.rs
+++ b/tests/ui/for_kv_map.rs
@@ -40,6 +40,16 @@ fn main() {
         let _k = k;
     }
 
+    let m: HashMap<u64, u64> = HashMap::new();
+    let rm = &m;
+    'label: for (k, _value) in rm {
+        //~^ ERROR: you seem to want to iterate on a map's keys
+        let _k = k;
+        if *k == 0u64 {
+            break 'label;
+        }
+    }
+
     // The following should not produce warnings.
 
     let m: HashMap<u64, u64> = HashMap::new();
diff --git a/tests/ui/for_kv_map.stderr b/tests/ui/for_kv_map.stderr
index f4ce473d095bf..adcc3ab8fdb90 100644
--- a/tests/ui/for_kv_map.stderr
+++ b/tests/ui/for_kv_map.stderr
@@ -55,5 +55,16 @@ help: use the corresponding method
 LL |     for k in rm.keys() {
    |         ~    ~~~~~~~~~
 
-error: aborting due to 5 previous errors
+error: you seem to want to iterate on a map's keys
+  --> tests/ui/for_kv_map.rs:45:32
+   |
+LL |     'label: for (k, _value) in rm {
+   |                                ^^
+   |
+help: use the corresponding method
+   |
+LL |     'label: for k in rm.keys() {
+   |                 ~    ~~~~~~~~~
+
+error: aborting due to 6 previous errors
 
diff --git a/tests/ui/get_unwrap.stderr b/tests/ui/get_unwrap.stderr
index a08b6657dcc7c..0f8b279da1e7a 100644
--- a/tests/ui/get_unwrap.stderr
+++ b/tests/ui/get_unwrap.stderr
@@ -1,14 +1,18 @@
-error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a slice
   --> tests/ui/get_unwrap.rs:37:17
    |
 LL |         let _ = boxed_slice.get(1).unwrap();
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&boxed_slice[1]`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
 note: the lint level is defined here
   --> tests/ui/get_unwrap.rs:9:9
    |
 LL | #![deny(clippy::get_unwrap)]
    |         ^^^^^^^^^^^^^^^^^^
+help: using `[]` is clearer and more concise
+   |
+LL |         let _ = &boxed_slice[1];
+   |                 ~~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:37:17
@@ -21,11 +25,16 @@ LL |         let _ = boxed_slice.get(1).unwrap();
    = note: `-D clippy::unwrap-used` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::unwrap_used)]`
 
-error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a slice
   --> tests/ui/get_unwrap.rs:38:17
    |
 LL |         let _ = some_slice.get(0).unwrap();
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_slice[0]`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _ = &some_slice[0];
+   |                 ~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:38:17
@@ -36,11 +45,16 @@ LL |         let _ = some_slice.get(0).unwrap();
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a Vec
   --> tests/ui/get_unwrap.rs:39:17
    |
 LL |         let _ = some_vec.get(0).unwrap();
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_vec[0]`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _ = &some_vec[0];
+   |                 ~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:39:17
@@ -51,11 +65,16 @@ LL |         let _ = some_vec.get(0).unwrap();
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a VecDeque
   --> tests/ui/get_unwrap.rs:40:17
    |
 LL |         let _ = some_vecdeque.get(0).unwrap();
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_vecdeque[0]`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _ = &some_vecdeque[0];
+   |                 ~~~~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:40:17
@@ -66,11 +85,16 @@ LL |         let _ = some_vecdeque.get(0).unwrap();
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get().unwrap()` on a HashMap. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a HashMap
   --> tests/ui/get_unwrap.rs:41:17
    |
 LL |         let _ = some_hashmap.get(&1).unwrap();
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_hashmap[&1]`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _ = &some_hashmap[&1];
+   |                 ~~~~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:41:17
@@ -81,11 +105,16 @@ LL |         let _ = some_hashmap.get(&1).unwrap();
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get().unwrap()` on a BTreeMap. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a BTreeMap
   --> tests/ui/get_unwrap.rs:42:17
    |
 LL |         let _ = some_btreemap.get(&1).unwrap();
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&some_btreemap[&1]`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _ = &some_btreemap[&1];
+   |                 ~~~~~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:42:17
@@ -96,11 +125,16 @@ LL |         let _ = some_btreemap.get(&1).unwrap();
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a slice
   --> tests/ui/get_unwrap.rs:46:21
    |
 LL |         let _: u8 = *boxed_slice.get(1).unwrap();
-   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice[1]`
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _: u8 = boxed_slice[1];
+   |                     ~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:46:22
@@ -111,11 +145,16 @@ LL |         let _: u8 = *boxed_slice.get(1).unwrap();
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
+error: called `.get_mut().unwrap()` on a slice
   --> tests/ui/get_unwrap.rs:51:9
    |
 LL |         *boxed_slice.get_mut(0).unwrap() = 1;
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `boxed_slice[0]`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         boxed_slice[0] = 1;
+   |         ~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:51:10
@@ -126,11 +165,16 @@ LL |         *boxed_slice.get_mut(0).unwrap() = 1;
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
+error: called `.get_mut().unwrap()` on a slice
   --> tests/ui/get_unwrap.rs:52:9
    |
 LL |         *some_slice.get_mut(0).unwrap() = 1;
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_slice[0]`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         some_slice[0] = 1;
+   |         ~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:52:10
@@ -141,11 +185,16 @@ LL |         *some_slice.get_mut(0).unwrap() = 1;
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
+error: called `.get_mut().unwrap()` on a Vec
   --> tests/ui/get_unwrap.rs:53:9
    |
 LL |         *some_vec.get_mut(0).unwrap() = 1;
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0]`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         some_vec[0] = 1;
+   |         ~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:53:10
@@ -156,11 +205,16 @@ LL |         *some_vec.get_mut(0).unwrap() = 1;
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get_mut().unwrap()` on a VecDeque. Using `[]` is more clear and more concise
+error: called `.get_mut().unwrap()` on a VecDeque
   --> tests/ui/get_unwrap.rs:54:9
    |
 LL |         *some_vecdeque.get_mut(0).unwrap() = 1;
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vecdeque[0]`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         some_vecdeque[0] = 1;
+   |         ~~~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:54:10
@@ -171,11 +225,16 @@ LL |         *some_vecdeque.get_mut(0).unwrap() = 1;
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get().unwrap()` on a Vec. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a Vec
   --> tests/ui/get_unwrap.rs:66:17
    |
 LL |         let _ = some_vec.get(0..1).unwrap().to_vec();
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0..1]`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _ = some_vec[0..1].to_vec();
+   |                 ~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:66:17
@@ -186,11 +245,16 @@ LL |         let _ = some_vec.get(0..1).unwrap().to_vec();
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get_mut().unwrap()` on a Vec. Using `[]` is more clear and more concise
+error: called `.get_mut().unwrap()` on a Vec
   --> tests/ui/get_unwrap.rs:67:17
    |
 LL |         let _ = some_vec.get_mut(0..1).unwrap().to_vec();
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `some_vec[0..1]`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _ = some_vec[0..1].to_vec();
+   |                 ~~~~~~~~~~~~~~
 
 error: used `unwrap()` on an `Option` value
   --> tests/ui/get_unwrap.rs:67:17
@@ -201,29 +265,49 @@ LL |         let _ = some_vec.get_mut(0..1).unwrap().to_vec();
    = note: if this value is `None`, it will panic
    = help: consider using `expect()` to provide a better panic message
 
-error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a slice
   --> tests/ui/get_unwrap.rs:77:24
    |
 LL |         let _x: &i32 = f.get(1 + 2).unwrap();
-   |                        ^^^^^^^^^^^^^^^^^^^^^ help: try: `&f[1 + 2]`
+   |                        ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _x: &i32 = &f[1 + 2];
+   |                        ~~~~~~~~~
 
-error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a slice
   --> tests/ui/get_unwrap.rs:80:18
    |
 LL |         let _x = f.get(1 + 2).unwrap().to_string();
-   |                  ^^^^^^^^^^^^^^^^^^^^^ help: try: `f[1 + 2]`
+   |                  ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _x = f[1 + 2].to_string();
+   |                  ~~~~~~~~
 
-error: called `.get().unwrap()` on a slice. Using `[]` is more clear and more concise
+error: called `.get().unwrap()` on a slice
   --> tests/ui/get_unwrap.rs:83:18
    |
 LL |         let _x = f.get(1 + 2).unwrap().abs();
-   |                  ^^^^^^^^^^^^^^^^^^^^^ help: try: `f[1 + 2]`
+   |                  ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |         let _x = f[1 + 2].abs();
+   |                  ~~~~~~~~
 
-error: called `.get_mut().unwrap()` on a slice. Using `[]` is more clear and more concise
+error: called `.get_mut().unwrap()` on a slice
   --> tests/ui/get_unwrap.rs:100:33
    |
 LL |                         let b = rest.get_mut(linidx(j, k) - linidx(i, k) - 1).unwrap();
-   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut rest[linidx(j, k) - linidx(i, k) - 1]`
+   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: using `[]` is clearer and more concise
+   |
+LL |                         let b = &mut rest[linidx(j, k) - linidx(i, k) - 1];
+   |                                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: aborting due to 30 previous errors
 
diff --git a/tests/ui/if_let_mutex.rs b/tests/ui/if_let_mutex.rs
index cb6915e0ebaaa..bb0eadfca1c7a 100644
--- a/tests/ui/if_let_mutex.rs
+++ b/tests/ui/if_let_mutex.rs
@@ -1,4 +1,5 @@
 #![warn(clippy::if_let_mutex)]
+#![allow(clippy::redundant_pattern_matching)]
 
 use std::ops::Deref;
 use std::sync::Mutex;
@@ -50,4 +51,12 @@ fn mutex_ref(mutex: &Mutex<i32>) {
     };
 }
 
+fn multiple_mutexes(m1: &Mutex<()>, m2: &Mutex<()>) {
+    if let Ok(_) = m1.lock() {
+        m2.lock();
+    } else {
+        m1.lock();
+    }
+}
+
 fn main() {}
diff --git a/tests/ui/if_let_mutex.stderr b/tests/ui/if_let_mutex.stderr
index 6e0115c23af68..45df4ac4d6795 100644
--- a/tests/ui/if_let_mutex.stderr
+++ b/tests/ui/if_let_mutex.stderr
@@ -1,5 +1,5 @@
 error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
-  --> tests/ui/if_let_mutex.rs:10:5
+  --> tests/ui/if_let_mutex.rs:11:5
    |
 LL |       if let Err(locked) = m.lock() {
    |       ^                    - this Mutex will remain locked for the entire `if let`-block...
@@ -19,7 +19,7 @@ LL | |     };
    = help: to override `-D warnings` add `#[allow(clippy::if_let_mutex)]`
 
 error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
-  --> tests/ui/if_let_mutex.rs:23:5
+  --> tests/ui/if_let_mutex.rs:24:5
    |
 LL |       if let Some(locked) = m.lock().unwrap().deref() {
    |       ^                     - this Mutex will remain locked for the entire `if let`-block...
@@ -37,7 +37,7 @@ LL | |     };
    = help: move the lock call outside of the `if let ...` expression
 
 error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
-  --> tests/ui/if_let_mutex.rs:45:5
+  --> tests/ui/if_let_mutex.rs:46:5
    |
 LL |       if let Ok(i) = mutex.lock() {
    |       ^              ----- this Mutex will remain locked for the entire `if let`-block...
@@ -53,5 +53,21 @@ LL | |     };
    |
    = help: move the lock call outside of the `if let ...` expression
 
-error: aborting due to 3 previous errors
+error: calling `Mutex::lock` inside the scope of another `Mutex::lock` causes a deadlock
+  --> tests/ui/if_let_mutex.rs:55:5
+   |
+LL |       if let Ok(_) = m1.lock() {
+   |       ^              -- this Mutex will remain locked for the entire `if let`-block...
+   |  _____|
+   | |
+LL | |         m2.lock();
+LL | |     } else {
+LL | |         m1.lock();
+   | |         -- ... and is tried to lock again here, which will always deadlock.
+LL | |     }
+   | |_____^
+   |
+   = help: move the lock call outside of the `if let ...` expression
+
+error: aborting due to 4 previous errors
 
diff --git a/tests/ui/if_then_some_else_none.fixed b/tests/ui/if_then_some_else_none.fixed
new file mode 100644
index 0000000000000..ad13372a68b7e
--- /dev/null
+++ b/tests/ui/if_then_some_else_none.fixed
@@ -0,0 +1,119 @@
+#![warn(clippy::if_then_some_else_none)]
+#![allow(clippy::redundant_pattern_matching, clippy::unnecessary_lazy_evaluations)]
+
+fn main() {
+    // Should issue an error.
+    let _ = foo().then(||  { println!("true!"); "foo" });
+
+    // Should issue an error when macros are used.
+    let _ = matches!(true, true).then(||  { println!("true!"); matches!(true, false) });
+
+    // Should issue an error. Binary expression `o < 32` should be parenthesized.
+    let x = Some(5);
+    let _ = x.and_then(|o| (o < 32).then_some(o));
+    //~^ ERROR: this could be simplified with `bool::then_some`
+
+    // Should issue an error. Unary expression `!x` should be parenthesized.
+    let x = true;
+    let _ = (!x).then_some(0);
+    //~^ ERROR: this could be simplified with `bool::then_some`
+
+    // Should not issue an error since the `else` block has a statement besides `None`.
+    let _ = if foo() {
+        println!("true!");
+        Some("foo")
+    } else {
+        eprintln!("false...");
+        None
+    };
+
+    // Should not issue an error since there are more than 2 blocks in the if-else chain.
+    let _ = if foo() {
+        println!("foo true!");
+        Some("foo")
+    } else if bar() {
+        println!("bar true!");
+        Some("bar")
+    } else {
+        None
+    };
+
+    let _ = if foo() {
+        println!("foo true!");
+        Some("foo")
+    } else {
+        bar().then(|| {
+            println!("bar true!");
+            "bar"
+        })
+    };
+
+    // Should not issue an error since the `then` block has `None`, not `Some`.
+    let _ = if foo() { None } else { Some("foo is false") };
+
+    // Should not issue an error since the `else` block doesn't use `None` directly.
+    let _ = if foo() { Some("foo is true") } else { into_none() };
+
+    // Should not issue an error since the `then` block doesn't use `Some` directly.
+    let _ = if foo() { into_some("foo") } else { None };
+}
+
+#[clippy::msrv = "1.49"]
+fn _msrv_1_49() {
+    // `bool::then` was stabilized in 1.50. Do not lint this
+    let _ = if foo() {
+        println!("true!");
+        Some(149)
+    } else {
+        None
+    };
+}
+
+#[clippy::msrv = "1.50"]
+fn _msrv_1_50() {
+    let _ = foo().then(||  { println!("true!"); 150 });
+}
+
+fn foo() -> bool {
+    unimplemented!()
+}
+
+fn bar() -> bool {
+    unimplemented!()
+}
+
+fn into_some<T>(v: T) -> Option<T> {
+    Some(v)
+}
+
+fn into_none<T>() -> Option<T> {
+    None
+}
+
+// Should not warn
+fn f(b: bool, v: Option<()>) -> Option<()> {
+    if b {
+        v?; // This is a potential early return, is not equivalent with `bool::then`
+
+        Some(())
+    } else {
+        None
+    }
+}
+
+fn issue11394(b: bool, v: Result<(), ()>) -> Result<(), ()> {
+    let x = if b {
+        #[allow(clippy::let_unit_value)]
+        let _ = v?;
+        Some(())
+    } else {
+        None
+    };
+
+    Ok(())
+}
+
+const fn issue12103(x: u32) -> Option<u32> {
+    // Should not issue an error in `const` context
+    if x > 42 { Some(150) } else { None }
+}
diff --git a/tests/ui/if_then_some_else_none.rs b/tests/ui/if_then_some_else_none.rs
index ccde154bd56ca..73edbb7da2a87 100644
--- a/tests/ui/if_then_some_else_none.rs
+++ b/tests/ui/if_then_some_else_none.rs
@@ -1,5 +1,5 @@
 #![warn(clippy::if_then_some_else_none)]
-#![allow(clippy::redundant_pattern_matching)]
+#![allow(clippy::redundant_pattern_matching, clippy::unnecessary_lazy_evaluations)]
 
 fn main() {
     // Should issue an error.
diff --git a/tests/ui/if_then_some_else_none.stderr b/tests/ui/if_then_some_else_none.stderr
index e0a95aebdc134..aed01e026cbe7 100644
--- a/tests/ui/if_then_some_else_none.stderr
+++ b/tests/ui/if_then_some_else_none.stderr
@@ -9,9 +9,8 @@ LL | |         Some("foo")
 LL | |     } else {
 LL | |         None
 LL | |     };
-   | |_____^
+   | |_____^ help: try: `foo().then(||  { println!("true!"); "foo" })`
    |
-   = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ "foo" })`
    = note: `-D clippy::if-then-some-else-none` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::if_then_some_else_none)]`
 
@@ -26,25 +25,19 @@ LL | |         Some(matches!(true, false))
 LL | |     } else {
 LL | |         None
 LL | |     };
-   | |_____^
-   |
-   = help: consider using `bool::then` like: `matches!(true, true).then(|| { /* snippet */ matches!(true, false) })`
+   | |_____^ help: try: `matches!(true, true).then(||  { println!("true!"); matches!(true, false) })`
 
 error: this could be simplified with `bool::then_some`
   --> tests/ui/if_then_some_else_none.rs:25:28
    |
 LL |     let _ = x.and_then(|o| if o < 32 { Some(o) } else { None });
-   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: consider using `bool::then_some` like: `(o < 32).then_some(o)`
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(o < 32).then_some(o)`
 
 error: this could be simplified with `bool::then_some`
   --> tests/ui/if_then_some_else_none.rs:30:13
    |
 LL |     let _ = if !x { Some(0) } else { None };
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = help: consider using `bool::then_some` like: `(!x).then_some(0)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(!x).then_some(0)`
 
 error: this could be simplified with `bool::then`
   --> tests/ui/if_then_some_else_none.rs:86:13
@@ -57,9 +50,7 @@ LL | |         Some(150)
 LL | |     } else {
 LL | |         None
 LL | |     };
-   | |_____^
-   |
-   = help: consider using `bool::then` like: `foo().then(|| { /* snippet */ 150 })`
+   | |_____^ help: try: `foo().then(||  { println!("true!"); 150 })`
 
 error: aborting due to 5 previous errors
 
diff --git a/tests/ui/implicit_hasher.fixed b/tests/ui/implicit_hasher.fixed
new file mode 100644
index 0000000000000..2d6dc0274cf2a
--- /dev/null
+++ b/tests/ui/implicit_hasher.fixed
@@ -0,0 +1,102 @@
+//@aux-build:proc_macros.rs
+#![deny(clippy::implicit_hasher)]
+
+#[macro_use]
+extern crate proc_macros;
+
+use std::cmp::Eq;
+use std::collections::{HashMap, HashSet};
+use std::hash::{BuildHasher, Hash};
+
+pub trait Foo<T>: Sized {
+    fn make() -> (Self, Self);
+}
+
+impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashMap<K, V, S> {
+    fn make() -> (Self, Self) {
+        // OK, don't suggest to modify these
+        let _: HashMap<i32, i32> = HashMap::new();
+        let _: HashSet<i32> = HashSet::new();
+
+        (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
+    }
+}
+impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for (HashMap<K, V, S>,) {
+    fn make() -> (Self, Self) {
+        ((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Default::default()),))
+    }
+}
+impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashMap<String, String, S> {
+    fn make() -> (Self, Self) {
+        (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
+    }
+}
+
+impl<K: Hash + Eq, V, S: BuildHasher + Default> Foo<i32> for HashMap<K, V, S> {
+    fn make() -> (Self, Self) {
+        (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default()))
+    }
+}
+impl<S: BuildHasher + Default> Foo<i64> for HashMap<String, String, S> {
+    fn make() -> (Self, Self) {
+        (HashMap::default(), HashMap::with_capacity_and_hasher(10, S::default()))
+    }
+}
+
+impl<T: Hash + Eq, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashSet<T, S> {
+    fn make() -> (Self, Self) {
+        (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
+    }
+}
+impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashSet<String, S> {
+    fn make() -> (Self, Self) {
+        (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
+    }
+}
+
+impl<T: Hash + Eq, S: BuildHasher + Default> Foo<i32> for HashSet<T, S> {
+    fn make() -> (Self, Self) {
+        (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default()))
+    }
+}
+impl<S: BuildHasher + Default> Foo<i64> for HashSet<String, S> {
+    fn make() -> (Self, Self) {
+        (HashSet::default(), HashSet::with_capacity_and_hasher(10, S::default()))
+    }
+}
+
+pub fn map<S: ::std::hash::BuildHasher>(map: &mut HashMap<i32, i32, S>) {}
+
+pub fn set<S: ::std::hash::BuildHasher>(set: &mut HashSet<i32, S>) {}
+
+#[inline_macros]
+pub mod gen {
+    use super::*;
+    inline! {
+        impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<u8> for HashMap<K, V, S> {
+            fn make() -> (Self, Self) {
+                (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
+            }
+        }
+
+        pub fn bar(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+    }
+}
+
+// When the macro is in a different file, the suggestion spans can't be combined properly
+// and should not cause an ICE
+// See #2707
+#[macro_use]
+#[path = "auxiliary/test_macro.rs"]
+pub mod test_macro;
+__implicit_hasher_test_macro!(impl<K, V> for HashMap<K, V> where V: test_macro::A);
+
+// #4260
+external! {
+    pub fn f(input: &HashMap<u32, u32>) {}
+}
+
+// #7712
+pub async fn election_vote<S: ::std::hash::BuildHasher>(_data: HashMap<i32, i32, S>) {}
+
+fn main() {}
diff --git a/tests/ui/implicit_hasher.rs b/tests/ui/implicit_hasher.rs
index f7cd541741b11..0a334357bd1b8 100644
--- a/tests/ui/implicit_hasher.rs
+++ b/tests/ui/implicit_hasher.rs
@@ -1,11 +1,8 @@
 //@aux-build:proc_macros.rs
-//@no-rustfix
 #![deny(clippy::implicit_hasher)]
-#![allow(unused)]
 
 #[macro_use]
 extern crate proc_macros;
-use proc_macros::external;
 
 use std::cmp::Eq;
 use std::collections::{HashMap, HashSet};
@@ -68,9 +65,11 @@ impl<S: BuildHasher + Default> Foo<i64> for HashSet<String, S> {
     }
 }
 
-pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
+pub fn map(map: &mut HashMap<i32, i32>) {}
 
-#[proc_macros::inline_macros]
+pub fn set(set: &mut HashSet<i32>) {}
+
+#[inline_macros]
 pub mod gen {
     use super::*;
     inline! {
diff --git a/tests/ui/implicit_hasher.stderr b/tests/ui/implicit_hasher.stderr
index a3df8edf389be..48c6ebc209cf3 100644
--- a/tests/ui/implicit_hasher.stderr
+++ b/tests/ui/implicit_hasher.stderr
@@ -1,40 +1,121 @@
-error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]`
-  --> tests/ui/implicit_hasher.rs:14:1
+error: impl for `HashMap` should be generalized over different hashers
+  --> tests/ui/implicit_hasher.rs:15:35
+   |
+LL | impl<K: Hash + Eq, V> Foo<i8> for HashMap<K, V> {
+   |                                   ^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> tests/ui/implicit_hasher.rs:2:9
+   |
+LL | #![deny(clippy::implicit_hasher)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
+help: add a type parameter for `BuildHasher`
+   |
+LL ~ impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashMap<K, V, S> {
+LL |     fn make() -> (Self, Self) {
+...
+LL |
+LL ~         (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
+   |
+
+error: impl for `HashMap` should be generalized over different hashers
+  --> tests/ui/implicit_hasher.rs:24:36
+   |
+LL | impl<K: Hash + Eq, V> Foo<i8> for (HashMap<K, V>,) {
+   |                                    ^^^^^^^^^^^^^
+   |
+help: add a type parameter for `BuildHasher`
+   |
+LL ~ impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<i8> for (HashMap<K, V, S>,) {
+LL |     fn make() -> (Self, Self) {
+LL ~         ((HashMap::default(),), (HashMap::with_capacity_and_hasher(10, Default::default()),))
+   |
+
+error: impl for `HashMap` should be generalized over different hashers
+  --> tests/ui/implicit_hasher.rs:29:19
+   |
+LL | impl Foo<i16> for HashMap<String, String> {
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add a type parameter for `BuildHasher`
+   |
+LL ~ impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashMap<String, String, S> {
+LL |     fn make() -> (Self, Self) {
+LL ~         (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
+   |
+
+error: impl for `HashSet` should be generalized over different hashers
+  --> tests/ui/implicit_hasher.rs:46:32
+   |
+LL | impl<T: Hash + Eq> Foo<i8> for HashSet<T> {
+   |                                ^^^^^^^^^^
+   |
+help: add a type parameter for `BuildHasher`
+   |
+LL ~ impl<T: Hash + Eq, S: ::std::hash::BuildHasher + Default> Foo<i8> for HashSet<T, S> {
+LL |     fn make() -> (Self, Self) {
+LL ~         (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
    |
-LL | pub trait Foo<T>: Sized {
-   | ^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]`
-  --> tests/ui/implicit_hasher.rs:71:1
+error: impl for `HashSet` should be generalized over different hashers
+  --> tests/ui/implicit_hasher.rs:51:19
+   |
+LL | impl Foo<i16> for HashSet<String> {
+   |                   ^^^^^^^^^^^^^^^
+   |
+help: add a type parameter for `BuildHasher`
+   |
+LL ~ impl<S: ::std::hash::BuildHasher + Default> Foo<i16> for HashSet<String, S> {
+LL |     fn make() -> (Self, Self) {
+LL ~         (HashSet::default(), HashSet::with_capacity_and_hasher(10, Default::default()))
    |
-LL | pub fn foo(_map: &mut HashMap<i32, i32>, _set: &mut HashSet<i32>) {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]`
-  --> tests/ui/implicit_hasher.rs:74:1
+error: parameter of type `HashMap` should be generalized over different hashers
+  --> tests/ui/implicit_hasher.rs:68:22
+   |
+LL | pub fn map(map: &mut HashMap<i32, i32>) {}
+   |                      ^^^^^^^^^^^^^^^^^
    |
-LL | pub mod gen {
-   | ^^^^^^^^^^^
+help: add a type parameter for `BuildHasher`
+   |
+LL | pub fn map<S: ::std::hash::BuildHasher>(map: &mut HashMap<i32, i32, S>) {}
+   |           +++++++++++++++++++++++++++++           ~~~~~~~~~~~~~~~~~~~~
 
-error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]`
-  --> tests/ui/implicit_hasher.rs:92:1
+error: parameter of type `HashSet` should be generalized over different hashers
+  --> tests/ui/implicit_hasher.rs:70:22
+   |
+LL | pub fn set(set: &mut HashSet<i32>) {}
+   |                      ^^^^^^^^^^^^
    |
-LL | pub mod test_macro;
-   | ^^^^^^^^^^^^^^^^^^^
+help: add a type parameter for `BuildHasher`
+   |
+LL | pub fn set<S: ::std::hash::BuildHasher>(set: &mut HashSet<i32, S>) {}
+   |           +++++++++++++++++++++++++++++           ~~~~~~~~~~~~~~~
 
-error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]`
-  --> tests/ui/implicit_hasher.rs:96:1
+error: impl for `HashMap` should be generalized over different hashers
+  --> tests/ui/implicit_hasher.rs:76:43
+   |
+LL |         impl<K: Hash + Eq, V> Foo<u8> for HashMap<K, V> {
+   |                                           ^^^^^^^^^^^^^
    |
-LL | external! {
-   | ^^^^^^^^^
+   = note: this error originates in the macro `__inline_mac_mod_gen` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: add a type parameter for `BuildHasher`
+   |
+LL ~         impl<K: Hash + Eq, V, S: ::std::hash::BuildHasher + Default> Foo<u8> for HashMap<K, V, S> {
+LL |             fn make() -> (Self, Self) {
+LL ~                 (HashMap::default(), HashMap::with_capacity_and_hasher(10, Default::default()))
    |
-   = note: this error originates in the macro `external` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: `proc-macro` crate types currently cannot export any items other than functions tagged with `#[proc_macro]`, `#[proc_macro_derive]`, or `#[proc_macro_attribute]`
-  --> tests/ui/implicit_hasher.rs:101:1
+error: parameter of type `HashMap` should be generalized over different hashers
+  --> tests/ui/implicit_hasher.rs:100:35
    |
 LL | pub async fn election_vote(_data: HashMap<i32, i32>) {}
-   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                                   ^^^^^^^^^^^^^^^^^
+   |
+help: add a type parameter for `BuildHasher`
+   |
+LL | pub async fn election_vote<S: ::std::hash::BuildHasher>(_data: HashMap<i32, i32, S>) {}
+   |                           +++++++++++++++++++++++++++++        ~~~~~~~~~~~~~~~~~~~~
 
-error: aborting due to 6 previous errors
+error: aborting due to 9 previous errors
 
diff --git a/tests/ui/implicit_return.stderr b/tests/ui/implicit_return.stderr
index f06d4e983c57a..3b06f26f5a05f 100644
--- a/tests/ui/implicit_return.stderr
+++ b/tests/ui/implicit_return.stderr
@@ -2,88 +2,157 @@ error: missing `return` statement
   --> tests/ui/implicit_return.rs:15:5
    |
 LL |     true
-   |     ^^^^ help: add `return` as shown: `return true`
+   |     ^^^^
    |
    = note: `-D clippy::implicit-return` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::implicit_return)]`
+help: add `return` as shown
+   |
+LL |     return true
+   |
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:19:15
    |
 LL |     if true { true } else { false }
-   |               ^^^^ help: add `return` as shown: `return true`
+   |               ^^^^
+   |
+help: add `return` as shown
+   |
+LL |     if true { return true } else { false }
+   |               ~~~~~~~~~~~
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:19:29
    |
 LL |     if true { true } else { false }
-   |                             ^^^^^ help: add `return` as shown: `return false`
+   |                             ^^^^^
+   |
+help: add `return` as shown
+   |
+LL |     if true { true } else { return false }
+   |                             ~~~~~~~~~~~~
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:25:17
    |
 LL |         true => false,
-   |                 ^^^^^ help: add `return` as shown: `return false`
+   |                 ^^^^^
+   |
+help: add `return` as shown
+   |
+LL |         true => return false,
+   |                 ~~~~~~~~~~~~
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:26:20
    |
 LL |         false => { true },
-   |                    ^^^^ help: add `return` as shown: `return true`
+   |                    ^^^^
+   |
+help: add `return` as shown
+   |
+LL |         false => { return true },
+   |                    ~~~~~~~~~~~
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:39:9
    |
 LL |         break true;
-   |         ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
+   |         ^^^^^^^^^^
+   |
+help: change `break` to `return` as shown
+   |
+LL |         return true;
+   |         ~~~~~~~~~~~
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:46:13
    |
 LL |             break true;
-   |             ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
+   |             ^^^^^^^^^^
+   |
+help: change `break` to `return` as shown
+   |
+LL |             return true;
+   |             ~~~~~~~~~~~
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:54:13
    |
 LL |             break true;
-   |             ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
+   |             ^^^^^^^^^^
+   |
+help: change `break` to `return` as shown
+   |
+LL |             return true;
+   |             ~~~~~~~~~~~
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:72:18
    |
 LL |     let _ = || { true };
-   |                  ^^^^ help: add `return` as shown: `return true`
+   |                  ^^^^
+   |
+help: add `return` as shown
+   |
+LL |     let _ = || { return true };
+   |                  ~~~~~~~~~~~
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:73:16
    |
 LL |     let _ = || true;
-   |                ^^^^ help: add `return` as shown: `return true`
+   |                ^^^^
+   |
+help: add `return` as shown
+   |
+LL |     let _ = || return true;
+   |                ~~~~~~~~~~~
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:81:5
    |
 LL |     format!("test {}", "test")
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: add `return` as shown: `return format!("test {}", "test")`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: add `return` as shown
+   |
+LL |     return format!("test {}", "test")
+   |
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:90:5
    |
 LL |     m!(true, false)
-   |     ^^^^^^^^^^^^^^^ help: add `return` as shown: `return m!(true, false)`
+   |     ^^^^^^^^^^^^^^^
+   |
+help: add `return` as shown
+   |
+LL |     return m!(true, false)
+   |
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:96:13
    |
 LL |             break true;
-   |             ^^^^^^^^^^ help: change `break` to `return` as shown: `return true`
+   |             ^^^^^^^^^^
+   |
+help: change `break` to `return` as shown
+   |
+LL |             return true;
+   |             ~~~~~~~~~~~
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:101:17
    |
 LL |                 break 'outer false;
-   |                 ^^^^^^^^^^^^^^^^^^ help: change `break` to `return` as shown: `return false`
+   |                 ^^^^^^^^^^^^^^^^^^
+   |
+help: change `break` to `return` as shown
+   |
+LL |                 return false;
+   |                 ~~~~~~~~~~~~
 
 error: missing `return` statement
   --> tests/ui/implicit_return.rs:116:5
@@ -104,7 +173,12 @@ error: missing `return` statement
   --> tests/ui/implicit_return.rs:130:5
    |
 LL |     true
-   |     ^^^^ help: add `return` as shown: `return true`
+   |     ^^^^
+   |
+help: add `return` as shown
+   |
+LL |     return true
+   |
 
 error: aborting due to 16 previous errors
 
diff --git a/tests/ui/lossy_float_literal.stderr b/tests/ui/lossy_float_literal.stderr
index b5a07418734c9..3026854e317ae 100644
--- a/tests/ui/lossy_float_literal.stderr
+++ b/tests/ui/lossy_float_literal.stderr
@@ -2,70 +2,124 @@ error: literal cannot be represented as the underlying type without loss of prec
   --> tests/ui/lossy_float_literal.rs:14:18
    |
 LL |     let _: f32 = 16_777_217.0;
-   |                  ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_216.0`
+   |                  ^^^^^^^^^^^^
    |
    = note: `-D clippy::lossy-float-literal` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::lossy_float_literal)]`
+help: consider changing the type or replacing it with
+   |
+LL |     let _: f32 = 16_777_216.0;
+   |                  ~~~~~~~~~~~~
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:15:18
    |
 LL |     let _: f32 = 16_777_219.0;
-   |                  ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
+   |                  ^^^^^^^^^^^^
+   |
+help: consider changing the type or replacing it with
+   |
+LL |     let _: f32 = 16_777_220.0;
+   |                  ~~~~~~~~~~~~
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:16:18
    |
 LL |     let _: f32 = 16_777_219.;
-   |                  ^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
+   |                  ^^^^^^^^^^^
+   |
+help: consider changing the type or replacing it with
+   |
+LL |     let _: f32 = 16_777_220.0;
+   |                  ~~~~~~~~~~~~
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:17:18
    |
 LL |     let _: f32 = 16_777_219.000;
-   |                  ^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
+   |                  ^^^^^^^^^^^^^^
+   |
+help: consider changing the type or replacing it with
+   |
+LL |     let _: f32 = 16_777_220.0;
+   |                  ~~~~~~~~~~~~
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:18:13
    |
 LL |     let _ = 16_777_219f32;
-   |             ^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220_f32`
+   |             ^^^^^^^^^^^^^
+   |
+help: consider changing the type or replacing it with
+   |
+LL |     let _ = 16_777_220_f32;
+   |             ~~~~~~~~~~~~~~
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:19:19
    |
 LL |     let _: f32 = -16_777_219.0;
-   |                   ^^^^^^^^^^^^ help: consider changing the type or replacing it with: `16_777_220.0`
+   |                   ^^^^^^^^^^^^
+   |
+help: consider changing the type or replacing it with
+   |
+LL |     let _: f32 = -16_777_220.0;
+   |                   ~~~~~~~~~~~~
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:21:18
    |
 LL |     let _: f64 = 9_007_199_254_740_993.0;
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider changing the type or replacing it with
+   |
+LL |     let _: f64 = 9_007_199_254_740_992.0;
+   |                  ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:22:18
    |
 LL |     let _: f64 = 9_007_199_254_740_993.;
-   |                  ^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
+   |                  ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider changing the type or replacing it with
+   |
+LL |     let _: f64 = 9_007_199_254_740_992.0;
+   |                  ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:23:18
    |
 LL |     let _: f64 = 9_007_199_254_740_993.00;
-   |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider changing the type or replacing it with
+   |
+LL |     let _: f64 = 9_007_199_254_740_992.0;
+   |                  ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:24:13
    |
 LL |     let _ = 9_007_199_254_740_993f64;
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992_f64`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider changing the type or replacing it with
+   |
+LL |     let _ = 9_007_199_254_740_992_f64;
+   |             ~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: literal cannot be represented as the underlying type without loss of precision
   --> tests/ui/lossy_float_literal.rs:25:19
    |
 LL |     let _: f64 = -9_007_199_254_740_993.0;
-   |                   ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing the type or replacing it with: `9_007_199_254_740_992.0`
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider changing the type or replacing it with
+   |
+LL |     let _: f64 = -9_007_199_254_740_992.0;
+   |                   ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: aborting due to 11 previous errors
 
diff --git a/tests/ui/missing_trait_methods.rs b/tests/ui/missing_trait_methods.rs
index 1b09b717ff70a..55b904bde59af 100644
--- a/tests/ui/missing_trait_methods.rs
+++ b/tests/ui/missing_trait_methods.rs
@@ -49,4 +49,12 @@ impl B for Complete {
     }
 }
 
+trait MissingMultiple {
+    fn one() {}
+    fn two() {}
+    fn three() {}
+}
+
+impl MissingMultiple for Partial {}
+
 fn main() {}
diff --git a/tests/ui/missing_trait_methods.stderr b/tests/ui/missing_trait_methods.stderr
index f5d5d4418b287..9c4968b022d7e 100644
--- a/tests/ui/missing_trait_methods.stderr
+++ b/tests/ui/missing_trait_methods.stderr
@@ -24,5 +24,41 @@ help: implement the method
 LL |     fn b<'a, T: AsRef<[u8]>>(a: &'a T) -> &'a [u8] {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 2 previous errors
+error: missing trait method provided by default: `one`
+  --> tests/ui/missing_trait_methods.rs:58:1
+   |
+LL | impl MissingMultiple for Partial {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: implement the method
+  --> tests/ui/missing_trait_methods.rs:53:5
+   |
+LL |     fn one() {}
+   |     ^^^^^^^^
+
+error: missing trait method provided by default: `two`
+  --> tests/ui/missing_trait_methods.rs:58:1
+   |
+LL | impl MissingMultiple for Partial {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: implement the method
+  --> tests/ui/missing_trait_methods.rs:54:5
+   |
+LL |     fn two() {}
+   |     ^^^^^^^^
+
+error: missing trait method provided by default: `three`
+  --> tests/ui/missing_trait_methods.rs:58:1
+   |
+LL | impl MissingMultiple for Partial {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: implement the method
+  --> tests/ui/missing_trait_methods.rs:55:5
+   |
+LL |     fn three() {}
+   |     ^^^^^^^^^^
+
+error: aborting due to 5 previous errors
 
diff --git a/tests/ui/needless_borrows_for_generic_args.fixed b/tests/ui/needless_borrows_for_generic_args.fixed
index 5478372cbe00f..c1dc8b5e8d09c 100644
--- a/tests/ui/needless_borrows_for_generic_args.fixed
+++ b/tests/ui/needless_borrows_for_generic_args.fixed
@@ -333,4 +333,12 @@ fn main() {
         f(&y); // Don't lint
         f("".to_owned()); // Lint
     }
+    {
+        fn takes_writer<T: std::io::Write>(_: T) {}
+
+        fn issue_12856(mut buffer: &mut Vec<u8>) {
+            takes_writer(&mut buffer); // Don't lint, would make buffer unavailable later
+            buffer.extend(b"\n");
+        }
+    }
 }
diff --git a/tests/ui/needless_borrows_for_generic_args.rs b/tests/ui/needless_borrows_for_generic_args.rs
index 2643815d939b5..c7f66824d5818 100644
--- a/tests/ui/needless_borrows_for_generic_args.rs
+++ b/tests/ui/needless_borrows_for_generic_args.rs
@@ -333,4 +333,12 @@ fn main() {
         f(&y); // Don't lint
         f(&"".to_owned()); // Lint
     }
+    {
+        fn takes_writer<T: std::io::Write>(_: T) {}
+
+        fn issue_12856(mut buffer: &mut Vec<u8>) {
+            takes_writer(&mut buffer); // Don't lint, would make buffer unavailable later
+            buffer.extend(b"\n");
+        }
+    }
 }
diff --git a/tests/ui/needless_pub_self.stderr b/tests/ui/needless_pub_self.stderr
index 0bff2e4b8b71b..1fdd841656594 100644
--- a/tests/ui/needless_pub_self.stderr
+++ b/tests/ui/needless_pub_self.stderr
@@ -2,22 +2,27 @@ error: unnecessary `pub(self)`
   --> tests/ui/needless_pub_self.rs:13:1
    |
 LL | pub(self) fn a() {}
-   | ^^^^^^^^^ help: remove it
+   | ^^^^^^^^^
    |
    = note: `-D clippy::needless-pub-self` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::needless_pub_self)]`
+   = help: remove it
 
 error: unnecessary `pub(in self)`
   --> tests/ui/needless_pub_self.rs:14:1
    |
 LL | pub(in self) fn b() {}
-   | ^^^^^^^^^^^^ help: remove it
+   | ^^^^^^^^^^^^
+   |
+   = help: remove it
 
 error: unnecessary `pub(self)`
   --> tests/ui/needless_pub_self.rs:20:5
    |
 LL |     pub(self) fn f() {}
-   |     ^^^^^^^^^ help: remove it
+   |     ^^^^^^^^^
+   |
+   = help: remove it
 
 error: aborting due to 3 previous errors
 
diff --git a/tests/ui/nonminimal_bool.rs b/tests/ui/nonminimal_bool.rs
index d117e8bf9c7a1..52b0155a762ea 100644
--- a/tests/ui/nonminimal_bool.rs
+++ b/tests/ui/nonminimal_bool.rs
@@ -177,3 +177,9 @@ fn issue_12371(x: usize) -> bool {
     // Should not warn!
     !x != 0
 }
+
+// Not linted because it is slow to do so
+// https://github.com/rust-lang/rust-clippy/issues/13206
+fn many_ops(a: bool, b: bool, c: bool, d: bool, e: bool, f: bool) -> bool {
+    (a && c && f) || (!a && b && !d) || (!b && !c && !e) || (d && e && !f)
+}
diff --git a/tests/ui/patterns.fixed b/tests/ui/patterns.fixed
index 332cba9715575..feaec33ac15a1 100644
--- a/tests/ui/patterns.fixed
+++ b/tests/ui/patterns.fixed
@@ -1,7 +1,7 @@
 //@aux-build:proc_macros.rs
 #![warn(clippy::all)]
 #![allow(unused)]
-#![allow(clippy::uninlined_format_args)]
+#![allow(clippy::uninlined_format_args, clippy::single_match)]
 
 #[macro_use]
 extern crate proc_macros;
diff --git a/tests/ui/patterns.rs b/tests/ui/patterns.rs
index 45d907688e379..53812c7deec3e 100644
--- a/tests/ui/patterns.rs
+++ b/tests/ui/patterns.rs
@@ -1,7 +1,7 @@
 //@aux-build:proc_macros.rs
 #![warn(clippy::all)]
 #![allow(unused)]
-#![allow(clippy::uninlined_format_args)]
+#![allow(clippy::uninlined_format_args, clippy::single_match)]
 
 #[macro_use]
 extern crate proc_macros;
diff --git a/tests/ui/rename.fixed b/tests/ui/rename.fixed
index d70c9f8d06c07..b810fd8224f63 100644
--- a/tests/ui/rename.fixed
+++ b/tests/ui/rename.fixed
@@ -13,7 +13,10 @@
 #![allow(clippy::disallowed_methods)]
 #![allow(clippy::disallowed_types)]
 #![allow(clippy::mixed_read_write_in_expression)]
+#![allow(clippy::manual_find_map)]
+#![allow(clippy::manual_filter_map)]
 #![allow(clippy::useless_conversion)]
+#![allow(clippy::redundant_pattern_matching)]
 #![allow(clippy::match_result_ok)]
 #![allow(clippy::non_canonical_clone_impl)]
 #![allow(clippy::non_canonical_partial_ord_impl)]
@@ -47,6 +50,7 @@
 #![allow(invalid_value)]
 #![allow(invalid_from_utf8_unchecked)]
 #![allow(let_underscore_drop)]
+#![allow(unexpected_cfgs)]
 #![allow(enum_intrinsics_non_enums)]
 #![allow(non_fmt_panics)]
 #![allow(named_arguments_used_positionally)]
@@ -55,65 +59,72 @@
 #![allow(unknown_lints)]
 #![allow(unused_labels)]
 #![allow(ambiguous_wide_pointer_comparisons)]
-#![warn(clippy::almost_complete_range)]
-#![warn(clippy::disallowed_names)]
-#![warn(clippy::blocks_in_conditions)]
-#![warn(clippy::blocks_in_conditions)]
-#![warn(clippy::blocks_in_conditions)]
-#![warn(clippy::box_collection)]
-#![warn(clippy::redundant_static_lifetimes)]
-#![warn(clippy::cognitive_complexity)]
-#![warn(clippy::derived_hash_with_manual_eq)]
-#![warn(clippy::disallowed_methods)]
-#![warn(clippy::disallowed_types)]
-#![warn(clippy::mixed_read_write_in_expression)]
-#![warn(clippy::useless_conversion)]
-#![warn(clippy::match_result_ok)]
-#![warn(clippy::non_canonical_clone_impl)]
-#![warn(clippy::non_canonical_partial_ord_impl)]
-#![warn(clippy::arithmetic_side_effects)]
-#![warn(clippy::overly_complex_bool_expr)]
-#![warn(clippy::new_without_default)]
-#![warn(clippy::bind_instead_of_map)]
-#![warn(clippy::expect_used)]
-#![warn(clippy::map_unwrap_or)]
-#![warn(clippy::map_unwrap_or)]
-#![warn(clippy::unwrap_used)]
-#![warn(clippy::panicking_overflow_checks)]
-#![warn(clippy::needless_borrow)]
-#![warn(clippy::expect_used)]
-#![warn(clippy::map_unwrap_or)]
-#![warn(clippy::unwrap_used)]
-#![warn(clippy::single_char_add_str)]
-#![warn(clippy::module_name_repetitions)]
-#![warn(clippy::missing_const_for_thread_local)]
-#![warn(clippy::recursive_format_impl)]
-#![warn(clippy::unwrap_or_default)]
-#![warn(clippy::invisible_characters)]
-#![warn(invalid_reference_casting)]
-#![warn(suspicious_double_ref_op)]
-#![warn(invalid_nan_comparisons)]
-#![warn(drop_bounds)]
-#![warn(dropping_copy_types)]
-#![warn(dropping_references)]
-#![warn(useless_ptr_null_checks)]
-#![warn(for_loops_over_fallibles)]
-#![warn(for_loops_over_fallibles)]
-#![warn(for_loops_over_fallibles)]
-#![warn(forgetting_copy_types)]
-#![warn(forgetting_references)]
-#![warn(array_into_iter)]
-#![warn(invalid_atomic_ordering)]
-#![warn(invalid_value)]
-#![warn(invalid_from_utf8_unchecked)]
-#![warn(let_underscore_drop)]
-#![warn(enum_intrinsics_non_enums)]
-#![warn(non_fmt_panics)]
-#![warn(named_arguments_used_positionally)]
-#![warn(temporary_cstring_as_ptr)]
-#![warn(undropped_manually_drops)]
-#![warn(unknown_lints)]
-#![warn(unused_labels)]
-#![warn(ambiguous_wide_pointer_comparisons)]
+#![allow(clippy::reversed_empty_ranges)]
+#![warn(clippy::almost_complete_range)] //~ ERROR: lint `clippy::almost_complete_letter_range`
+#![warn(clippy::disallowed_names)] //~ ERROR: lint `clippy::blacklisted_name`
+#![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::block_in_if_condition_expr`
+#![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::block_in_if_condition_stmt`
+#![warn(clippy::blocks_in_conditions)] //~ ERROR: lint `clippy::blocks_in_if_conditions`
+#![warn(clippy::box_collection)] //~ ERROR: lint `clippy::box_vec`
+#![warn(clippy::redundant_static_lifetimes)] //~ ERROR: lint `clippy::const_static_lifetime`
+#![warn(clippy::cognitive_complexity)] //~ ERROR: lint `clippy::cyclomatic_complexity`
+#![warn(clippy::derived_hash_with_manual_eq)] //~ ERROR: lint `clippy::derive_hash_xor_eq`
+#![warn(clippy::disallowed_methods)] //~ ERROR: lint `clippy::disallowed_method`
+#![warn(clippy::disallowed_types)] //~ ERROR: lint `clippy::disallowed_type`
+#![warn(clippy::mixed_read_write_in_expression)] //~ ERROR: lint `clippy::eval_order_dependence`
+#![warn(clippy::manual_find_map)] //~ ERROR: lint `clippy::find_map`
+#![warn(clippy::manual_filter_map)] //~ ERROR: lint `clippy::filter_map`
+#![warn(clippy::useless_conversion)] //~ ERROR: lint `clippy::identity_conversion`
+#![warn(clippy::redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching`
+#![warn(clippy::match_result_ok)] //~ ERROR: lint `clippy::if_let_some_result`
+#![warn(clippy::non_canonical_clone_impl)] //~ ERROR: lint `clippy::incorrect_clone_impl_on_copy_type`
+#![warn(clippy::non_canonical_partial_ord_impl)] //~ ERROR: lint `clippy::incorrect_partial_ord_impl_on_ord_type`
+#![warn(clippy::arithmetic_side_effects)] //~ ERROR: lint `clippy::integer_arithmetic`
+#![warn(clippy::overly_complex_bool_expr)] //~ ERROR: lint `clippy::logic_bug`
+#![warn(clippy::new_without_default)] //~ ERROR: lint `clippy::new_without_default_derive`
+#![warn(clippy::bind_instead_of_map)] //~ ERROR: lint `clippy::option_and_then_some`
+#![warn(clippy::expect_used)] //~ ERROR: lint `clippy::option_expect_used`
+#![warn(clippy::map_unwrap_or)] //~ ERROR: lint `clippy::option_map_unwrap_or`
+#![warn(clippy::map_unwrap_or)] //~ ERROR: lint `clippy::option_map_unwrap_or_else`
+#![warn(clippy::unwrap_used)] //~ ERROR: lint `clippy::option_unwrap_used`
+#![warn(clippy::panicking_overflow_checks)] //~ ERROR: lint `clippy::overflow_check_conditional`
+#![warn(clippy::needless_borrow)] //~ ERROR: lint `clippy::ref_in_deref`
+#![warn(clippy::expect_used)] //~ ERROR: lint `clippy::result_expect_used`
+#![warn(clippy::map_unwrap_or)] //~ ERROR: lint `clippy::result_map_unwrap_or_else`
+#![warn(clippy::unwrap_used)] //~ ERROR: lint `clippy::result_unwrap_used`
+#![warn(clippy::single_char_add_str)] //~ ERROR: lint `clippy::single_char_push_str`
+#![warn(clippy::module_name_repetitions)] //~ ERROR: lint `clippy::stutter`
+#![warn(clippy::missing_const_for_thread_local)] //~ ERROR: lint `clippy::thread_local_initializer_can_be_made_const`
+#![warn(clippy::recursive_format_impl)] //~ ERROR: lint `clippy::to_string_in_display`
+#![warn(clippy::unwrap_or_default)] //~ ERROR: lint `clippy::unwrap_or_else_default`
+#![warn(clippy::invisible_characters)] //~ ERROR: lint `clippy::zero_width_space`
+#![warn(invalid_reference_casting)] //~ ERROR: lint `clippy::cast_ref_to_mut`
+#![warn(suspicious_double_ref_op)] //~ ERROR: lint `clippy::clone_double_ref`
+#![warn(invalid_nan_comparisons)] //~ ERROR: lint `clippy::cmp_nan`
+#![warn(drop_bounds)] //~ ERROR: lint `clippy::drop_bounds`
+#![warn(dropping_copy_types)] //~ ERROR: lint `clippy::drop_copy`
+#![warn(dropping_references)] //~ ERROR: lint `clippy::drop_ref`
+#![warn(useless_ptr_null_checks)] //~ ERROR: lint `clippy::fn_null_check`
+#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_option`
+#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loop_over_result`
+#![warn(for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles`
+#![warn(forgetting_copy_types)] //~ ERROR: lint `clippy::forget_copy`
+#![warn(forgetting_references)] //~ ERROR: lint `clippy::forget_ref`
+#![warn(array_into_iter)] //~ ERROR: lint `clippy::into_iter_on_array`
+#![warn(invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering`
+#![warn(invalid_value)] //~ ERROR: lint `clippy::invalid_ref`
+#![warn(invalid_from_utf8_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked`
+#![warn(let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop`
+#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::maybe_misused_cfg`
+#![warn(enum_intrinsics_non_enums)] //~ ERROR: lint `clippy::mem_discriminant_non_enum`
+#![warn(unexpected_cfgs)] //~ ERROR: lint `clippy::mismatched_target_os`
+#![warn(non_fmt_panics)] //~ ERROR: lint `clippy::panic_params`
+#![warn(named_arguments_used_positionally)] //~ ERROR: lint `clippy::positional_named_format_parameters`
+#![warn(temporary_cstring_as_ptr)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr`
+#![warn(undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops`
+#![warn(unknown_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints`
+#![warn(unused_labels)] //~ ERROR: lint `clippy::unused_label`
+#![warn(ambiguous_wide_pointer_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons`
+#![warn(clippy::reversed_empty_ranges)] //~ ERROR: lint `clippy::reverse_range_loop`
 
 fn main() {}
diff --git a/tests/ui/rename.rs b/tests/ui/rename.rs
index 8d0ac3c8f9555..e03df1658ee36 100644
--- a/tests/ui/rename.rs
+++ b/tests/ui/rename.rs
@@ -13,7 +13,10 @@
 #![allow(clippy::disallowed_methods)]
 #![allow(clippy::disallowed_types)]
 #![allow(clippy::mixed_read_write_in_expression)]
+#![allow(clippy::manual_find_map)]
+#![allow(clippy::manual_filter_map)]
 #![allow(clippy::useless_conversion)]
+#![allow(clippy::redundant_pattern_matching)]
 #![allow(clippy::match_result_ok)]
 #![allow(clippy::non_canonical_clone_impl)]
 #![allow(clippy::non_canonical_partial_ord_impl)]
@@ -47,6 +50,7 @@
 #![allow(invalid_value)]
 #![allow(invalid_from_utf8_unchecked)]
 #![allow(let_underscore_drop)]
+#![allow(unexpected_cfgs)]
 #![allow(enum_intrinsics_non_enums)]
 #![allow(non_fmt_panics)]
 #![allow(named_arguments_used_positionally)]
@@ -55,65 +59,72 @@
 #![allow(unknown_lints)]
 #![allow(unused_labels)]
 #![allow(ambiguous_wide_pointer_comparisons)]
-#![warn(clippy::almost_complete_letter_range)]
-#![warn(clippy::blacklisted_name)]
-#![warn(clippy::block_in_if_condition_expr)]
-#![warn(clippy::block_in_if_condition_stmt)]
-#![warn(clippy::blocks_in_if_conditions)]
-#![warn(clippy::box_vec)]
-#![warn(clippy::const_static_lifetime)]
-#![warn(clippy::cyclomatic_complexity)]
-#![warn(clippy::derive_hash_xor_eq)]
-#![warn(clippy::disallowed_method)]
-#![warn(clippy::disallowed_type)]
-#![warn(clippy::eval_order_dependence)]
-#![warn(clippy::identity_conversion)]
-#![warn(clippy::if_let_some_result)]
-#![warn(clippy::incorrect_clone_impl_on_copy_type)]
-#![warn(clippy::incorrect_partial_ord_impl_on_ord_type)]
-#![warn(clippy::integer_arithmetic)]
-#![warn(clippy::logic_bug)]
-#![warn(clippy::new_without_default_derive)]
-#![warn(clippy::option_and_then_some)]
-#![warn(clippy::option_expect_used)]
-#![warn(clippy::option_map_unwrap_or)]
-#![warn(clippy::option_map_unwrap_or_else)]
-#![warn(clippy::option_unwrap_used)]
-#![warn(clippy::overflow_check_conditional)]
-#![warn(clippy::ref_in_deref)]
-#![warn(clippy::result_expect_used)]
-#![warn(clippy::result_map_unwrap_or_else)]
-#![warn(clippy::result_unwrap_used)]
-#![warn(clippy::single_char_push_str)]
-#![warn(clippy::stutter)]
-#![warn(clippy::thread_local_initializer_can_be_made_const)]
-#![warn(clippy::to_string_in_display)]
-#![warn(clippy::unwrap_or_else_default)]
-#![warn(clippy::zero_width_space)]
-#![warn(clippy::cast_ref_to_mut)]
-#![warn(clippy::clone_double_ref)]
-#![warn(clippy::cmp_nan)]
-#![warn(clippy::drop_bounds)]
-#![warn(clippy::drop_copy)]
-#![warn(clippy::drop_ref)]
-#![warn(clippy::fn_null_check)]
-#![warn(clippy::for_loop_over_option)]
-#![warn(clippy::for_loop_over_result)]
-#![warn(clippy::for_loops_over_fallibles)]
-#![warn(clippy::forget_copy)]
-#![warn(clippy::forget_ref)]
-#![warn(clippy::into_iter_on_array)]
-#![warn(clippy::invalid_atomic_ordering)]
-#![warn(clippy::invalid_ref)]
-#![warn(clippy::invalid_utf8_in_unchecked)]
-#![warn(clippy::let_underscore_drop)]
-#![warn(clippy::mem_discriminant_non_enum)]
-#![warn(clippy::panic_params)]
-#![warn(clippy::positional_named_format_parameters)]
-#![warn(clippy::temporary_cstring_as_ptr)]
-#![warn(clippy::undropped_manually_drops)]
-#![warn(clippy::unknown_clippy_lints)]
-#![warn(clippy::unused_label)]
-#![warn(clippy::vtable_address_comparisons)]
+#![allow(clippy::reversed_empty_ranges)]
+#![warn(clippy::almost_complete_letter_range)] //~ ERROR: lint `clippy::almost_complete_letter_range`
+#![warn(clippy::blacklisted_name)] //~ ERROR: lint `clippy::blacklisted_name`
+#![warn(clippy::block_in_if_condition_expr)] //~ ERROR: lint `clippy::block_in_if_condition_expr`
+#![warn(clippy::block_in_if_condition_stmt)] //~ ERROR: lint `clippy::block_in_if_condition_stmt`
+#![warn(clippy::blocks_in_if_conditions)] //~ ERROR: lint `clippy::blocks_in_if_conditions`
+#![warn(clippy::box_vec)] //~ ERROR: lint `clippy::box_vec`
+#![warn(clippy::const_static_lifetime)] //~ ERROR: lint `clippy::const_static_lifetime`
+#![warn(clippy::cyclomatic_complexity)] //~ ERROR: lint `clippy::cyclomatic_complexity`
+#![warn(clippy::derive_hash_xor_eq)] //~ ERROR: lint `clippy::derive_hash_xor_eq`
+#![warn(clippy::disallowed_method)] //~ ERROR: lint `clippy::disallowed_method`
+#![warn(clippy::disallowed_type)] //~ ERROR: lint `clippy::disallowed_type`
+#![warn(clippy::eval_order_dependence)] //~ ERROR: lint `clippy::eval_order_dependence`
+#![warn(clippy::find_map)] //~ ERROR: lint `clippy::find_map`
+#![warn(clippy::filter_map)] //~ ERROR: lint `clippy::filter_map`
+#![warn(clippy::identity_conversion)] //~ ERROR: lint `clippy::identity_conversion`
+#![warn(clippy::if_let_redundant_pattern_matching)] //~ ERROR: lint `clippy::if_let_redundant_pattern_matching`
+#![warn(clippy::if_let_some_result)] //~ ERROR: lint `clippy::if_let_some_result`
+#![warn(clippy::incorrect_clone_impl_on_copy_type)] //~ ERROR: lint `clippy::incorrect_clone_impl_on_copy_type`
+#![warn(clippy::incorrect_partial_ord_impl_on_ord_type)] //~ ERROR: lint `clippy::incorrect_partial_ord_impl_on_ord_type`
+#![warn(clippy::integer_arithmetic)] //~ ERROR: lint `clippy::integer_arithmetic`
+#![warn(clippy::logic_bug)] //~ ERROR: lint `clippy::logic_bug`
+#![warn(clippy::new_without_default_derive)] //~ ERROR: lint `clippy::new_without_default_derive`
+#![warn(clippy::option_and_then_some)] //~ ERROR: lint `clippy::option_and_then_some`
+#![warn(clippy::option_expect_used)] //~ ERROR: lint `clippy::option_expect_used`
+#![warn(clippy::option_map_unwrap_or)] //~ ERROR: lint `clippy::option_map_unwrap_or`
+#![warn(clippy::option_map_unwrap_or_else)] //~ ERROR: lint `clippy::option_map_unwrap_or_else`
+#![warn(clippy::option_unwrap_used)] //~ ERROR: lint `clippy::option_unwrap_used`
+#![warn(clippy::overflow_check_conditional)] //~ ERROR: lint `clippy::overflow_check_conditional`
+#![warn(clippy::ref_in_deref)] //~ ERROR: lint `clippy::ref_in_deref`
+#![warn(clippy::result_expect_used)] //~ ERROR: lint `clippy::result_expect_used`
+#![warn(clippy::result_map_unwrap_or_else)] //~ ERROR: lint `clippy::result_map_unwrap_or_else`
+#![warn(clippy::result_unwrap_used)] //~ ERROR: lint `clippy::result_unwrap_used`
+#![warn(clippy::single_char_push_str)] //~ ERROR: lint `clippy::single_char_push_str`
+#![warn(clippy::stutter)] //~ ERROR: lint `clippy::stutter`
+#![warn(clippy::thread_local_initializer_can_be_made_const)] //~ ERROR: lint `clippy::thread_local_initializer_can_be_made_const`
+#![warn(clippy::to_string_in_display)] //~ ERROR: lint `clippy::to_string_in_display`
+#![warn(clippy::unwrap_or_else_default)] //~ ERROR: lint `clippy::unwrap_or_else_default`
+#![warn(clippy::zero_width_space)] //~ ERROR: lint `clippy::zero_width_space`
+#![warn(clippy::cast_ref_to_mut)] //~ ERROR: lint `clippy::cast_ref_to_mut`
+#![warn(clippy::clone_double_ref)] //~ ERROR: lint `clippy::clone_double_ref`
+#![warn(clippy::cmp_nan)] //~ ERROR: lint `clippy::cmp_nan`
+#![warn(clippy::drop_bounds)] //~ ERROR: lint `clippy::drop_bounds`
+#![warn(clippy::drop_copy)] //~ ERROR: lint `clippy::drop_copy`
+#![warn(clippy::drop_ref)] //~ ERROR: lint `clippy::drop_ref`
+#![warn(clippy::fn_null_check)] //~ ERROR: lint `clippy::fn_null_check`
+#![warn(clippy::for_loop_over_option)] //~ ERROR: lint `clippy::for_loop_over_option`
+#![warn(clippy::for_loop_over_result)] //~ ERROR: lint `clippy::for_loop_over_result`
+#![warn(clippy::for_loops_over_fallibles)] //~ ERROR: lint `clippy::for_loops_over_fallibles`
+#![warn(clippy::forget_copy)] //~ ERROR: lint `clippy::forget_copy`
+#![warn(clippy::forget_ref)] //~ ERROR: lint `clippy::forget_ref`
+#![warn(clippy::into_iter_on_array)] //~ ERROR: lint `clippy::into_iter_on_array`
+#![warn(clippy::invalid_atomic_ordering)] //~ ERROR: lint `clippy::invalid_atomic_ordering`
+#![warn(clippy::invalid_ref)] //~ ERROR: lint `clippy::invalid_ref`
+#![warn(clippy::invalid_utf8_in_unchecked)] //~ ERROR: lint `clippy::invalid_utf8_in_unchecked`
+#![warn(clippy::let_underscore_drop)] //~ ERROR: lint `clippy::let_underscore_drop`
+#![warn(clippy::maybe_misused_cfg)] //~ ERROR: lint `clippy::maybe_misused_cfg`
+#![warn(clippy::mem_discriminant_non_enum)] //~ ERROR: lint `clippy::mem_discriminant_non_enum`
+#![warn(clippy::mismatched_target_os)] //~ ERROR: lint `clippy::mismatched_target_os`
+#![warn(clippy::panic_params)] //~ ERROR: lint `clippy::panic_params`
+#![warn(clippy::positional_named_format_parameters)] //~ ERROR: lint `clippy::positional_named_format_parameters`
+#![warn(clippy::temporary_cstring_as_ptr)] //~ ERROR: lint `clippy::temporary_cstring_as_ptr`
+#![warn(clippy::undropped_manually_drops)] //~ ERROR: lint `clippy::undropped_manually_drops`
+#![warn(clippy::unknown_clippy_lints)] //~ ERROR: lint `clippy::unknown_clippy_lints`
+#![warn(clippy::unused_label)] //~ ERROR: lint `clippy::unused_label`
+#![warn(clippy::vtable_address_comparisons)] //~ ERROR: lint `clippy::vtable_address_comparisons`
+#![warn(clippy::reverse_range_loop)] //~ ERROR: lint `clippy::reverse_range_loop`
 
 fn main() {}
diff --git a/tests/ui/rename.stderr b/tests/ui/rename.stderr
index d6637324a030b..46d9f0fac59f9 100644
--- a/tests/ui/rename.stderr
+++ b/tests/ui/rename.stderr
@@ -1,5 +1,5 @@
 error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range`
-  --> tests/ui/rename.rs:58:9
+  --> tests/ui/rename.rs:63:9
    |
 LL | #![warn(clippy::almost_complete_letter_range)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range`
@@ -8,358 +8,394 @@ LL | #![warn(clippy::almost_complete_letter_range)]
    = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]`
 
 error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names`
-  --> tests/ui/rename.rs:59:9
+  --> tests/ui/rename.rs:64:9
    |
 LL | #![warn(clippy::blacklisted_name)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names`
 
 error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions`
-  --> tests/ui/rename.rs:60:9
+  --> tests/ui/rename.rs:65:9
    |
 LL | #![warn(clippy::block_in_if_condition_expr)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions`
 
 error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions`
-  --> tests/ui/rename.rs:61:9
+  --> tests/ui/rename.rs:66:9
    |
 LL | #![warn(clippy::block_in_if_condition_stmt)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions`
 
 error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions`
-  --> tests/ui/rename.rs:62:9
+  --> tests/ui/rename.rs:67:9
    |
 LL | #![warn(clippy::blocks_in_if_conditions)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions`
 
 error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
-  --> tests/ui/rename.rs:63:9
+  --> tests/ui/rename.rs:68:9
    |
 LL | #![warn(clippy::box_vec)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
 
 error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
-  --> tests/ui/rename.rs:64:9
+  --> tests/ui/rename.rs:69:9
    |
 LL | #![warn(clippy::const_static_lifetime)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
 
 error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
-  --> tests/ui/rename.rs:65:9
+  --> tests/ui/rename.rs:70:9
    |
 LL | #![warn(clippy::cyclomatic_complexity)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
 
 error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq`
-  --> tests/ui/rename.rs:66:9
+  --> tests/ui/rename.rs:71:9
    |
 LL | #![warn(clippy::derive_hash_xor_eq)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq`
 
 error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
-  --> tests/ui/rename.rs:67:9
+  --> tests/ui/rename.rs:72:9
    |
 LL | #![warn(clippy::disallowed_method)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
 
 error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
-  --> tests/ui/rename.rs:68:9
+  --> tests/ui/rename.rs:73:9
    |
 LL | #![warn(clippy::disallowed_type)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
 
 error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression`
-  --> tests/ui/rename.rs:69:9
+  --> tests/ui/rename.rs:74:9
    |
 LL | #![warn(clippy::eval_order_dependence)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression`
 
+error: lint `clippy::find_map` has been renamed to `clippy::manual_find_map`
+  --> tests/ui/rename.rs:75:9
+   |
+LL | #![warn(clippy::find_map)]
+   |         ^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_find_map`
+
+error: lint `clippy::filter_map` has been renamed to `clippy::manual_filter_map`
+  --> tests/ui/rename.rs:76:9
+   |
+LL | #![warn(clippy::filter_map)]
+   |         ^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::manual_filter_map`
+
 error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
-  --> tests/ui/rename.rs:70:9
+  --> tests/ui/rename.rs:77:9
    |
 LL | #![warn(clippy::identity_conversion)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
 
+error: lint `clippy::if_let_redundant_pattern_matching` has been renamed to `clippy::redundant_pattern_matching`
+  --> tests/ui/rename.rs:78:9
+   |
+LL | #![warn(clippy::if_let_redundant_pattern_matching)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_pattern_matching`
+
 error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
-  --> tests/ui/rename.rs:71:9
+  --> tests/ui/rename.rs:79:9
    |
 LL | #![warn(clippy::if_let_some_result)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
 
 error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl`
-  --> tests/ui/rename.rs:72:9
+  --> tests/ui/rename.rs:80:9
    |
 LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl`
 
 error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl`
-  --> tests/ui/rename.rs:73:9
+  --> tests/ui/rename.rs:81:9
    |
 LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl`
 
 error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects`
-  --> tests/ui/rename.rs:74:9
+  --> tests/ui/rename.rs:82:9
    |
 LL | #![warn(clippy::integer_arithmetic)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects`
 
 error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr`
-  --> tests/ui/rename.rs:75:9
+  --> tests/ui/rename.rs:83:9
    |
 LL | #![warn(clippy::logic_bug)]
    |         ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr`
 
 error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
-  --> tests/ui/rename.rs:76:9
+  --> tests/ui/rename.rs:84:9
    |
 LL | #![warn(clippy::new_without_default_derive)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
 
 error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
-  --> tests/ui/rename.rs:77:9
+  --> tests/ui/rename.rs:85:9
    |
 LL | #![warn(clippy::option_and_then_some)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
 
 error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
-  --> tests/ui/rename.rs:78:9
+  --> tests/ui/rename.rs:86:9
    |
 LL | #![warn(clippy::option_expect_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
 error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
-  --> tests/ui/rename.rs:79:9
+  --> tests/ui/rename.rs:87:9
    |
 LL | #![warn(clippy::option_map_unwrap_or)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-  --> tests/ui/rename.rs:80:9
+  --> tests/ui/rename.rs:88:9
    |
 LL | #![warn(clippy::option_map_unwrap_or_else)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
-  --> tests/ui/rename.rs:81:9
+  --> tests/ui/rename.rs:89:9
    |
 LL | #![warn(clippy::option_unwrap_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
 error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks`
-  --> tests/ui/rename.rs:82:9
+  --> tests/ui/rename.rs:90:9
    |
 LL | #![warn(clippy::overflow_check_conditional)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks`
 
 error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
-  --> tests/ui/rename.rs:83:9
+  --> tests/ui/rename.rs:91:9
    |
 LL | #![warn(clippy::ref_in_deref)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
 
 error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
-  --> tests/ui/rename.rs:84:9
+  --> tests/ui/rename.rs:92:9
    |
 LL | #![warn(clippy::result_expect_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
 error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-  --> tests/ui/rename.rs:85:9
+  --> tests/ui/rename.rs:93:9
    |
 LL | #![warn(clippy::result_map_unwrap_or_else)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
-  --> tests/ui/rename.rs:86:9
+  --> tests/ui/rename.rs:94:9
    |
 LL | #![warn(clippy::result_unwrap_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
 error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
-  --> tests/ui/rename.rs:87:9
+  --> tests/ui/rename.rs:95:9
    |
 LL | #![warn(clippy::single_char_push_str)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
 
 error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
-  --> tests/ui/rename.rs:88:9
+  --> tests/ui/rename.rs:96:9
    |
 LL | #![warn(clippy::stutter)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
 
 error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local`
-  --> tests/ui/rename.rs:89:9
+  --> tests/ui/rename.rs:97:9
    |
 LL | #![warn(clippy::thread_local_initializer_can_be_made_const)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local`
 
 error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
-  --> tests/ui/rename.rs:90:9
+  --> tests/ui/rename.rs:98:9
    |
 LL | #![warn(clippy::to_string_in_display)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
 
 error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default`
-  --> tests/ui/rename.rs:91:9
+  --> tests/ui/rename.rs:99:9
    |
 LL | #![warn(clippy::unwrap_or_else_default)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default`
 
 error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
-  --> tests/ui/rename.rs:92:9
+  --> tests/ui/rename.rs:100:9
    |
 LL | #![warn(clippy::zero_width_space)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
 
 error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting`
-  --> tests/ui/rename.rs:93:9
+  --> tests/ui/rename.rs:101:9
    |
 LL | #![warn(clippy::cast_ref_to_mut)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting`
 
 error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op`
-  --> tests/ui/rename.rs:94:9
+  --> tests/ui/rename.rs:102:9
    |
 LL | #![warn(clippy::clone_double_ref)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op`
 
 error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons`
-  --> tests/ui/rename.rs:95:9
+  --> tests/ui/rename.rs:103:9
    |
 LL | #![warn(clippy::cmp_nan)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons`
 
 error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
-  --> tests/ui/rename.rs:96:9
+  --> tests/ui/rename.rs:104:9
    |
 LL | #![warn(clippy::drop_bounds)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
 
 error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types`
-  --> tests/ui/rename.rs:97:9
+  --> tests/ui/rename.rs:105:9
    |
 LL | #![warn(clippy::drop_copy)]
    |         ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types`
 
 error: lint `clippy::drop_ref` has been renamed to `dropping_references`
-  --> tests/ui/rename.rs:98:9
+  --> tests/ui/rename.rs:106:9
    |
 LL | #![warn(clippy::drop_ref)]
    |         ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references`
 
 error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks`
-  --> tests/ui/rename.rs:99:9
+  --> tests/ui/rename.rs:107:9
    |
 LL | #![warn(clippy::fn_null_check)]
    |         ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks`
 
 error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles`
-  --> tests/ui/rename.rs:100:9
+  --> tests/ui/rename.rs:108:9
    |
 LL | #![warn(clippy::for_loop_over_option)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
 
 error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles`
-  --> tests/ui/rename.rs:101:9
+  --> tests/ui/rename.rs:109:9
    |
 LL | #![warn(clippy::for_loop_over_result)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
 
 error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles`
-  --> tests/ui/rename.rs:102:9
+  --> tests/ui/rename.rs:110:9
    |
 LL | #![warn(clippy::for_loops_over_fallibles)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
 
 error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types`
-  --> tests/ui/rename.rs:103:9
+  --> tests/ui/rename.rs:111:9
    |
 LL | #![warn(clippy::forget_copy)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types`
 
 error: lint `clippy::forget_ref` has been renamed to `forgetting_references`
-  --> tests/ui/rename.rs:104:9
+  --> tests/ui/rename.rs:112:9
    |
 LL | #![warn(clippy::forget_ref)]
    |         ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references`
 
 error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
-  --> tests/ui/rename.rs:105:9
+  --> tests/ui/rename.rs:113:9
    |
 LL | #![warn(clippy::into_iter_on_array)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
 
 error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
-  --> tests/ui/rename.rs:106:9
+  --> tests/ui/rename.rs:114:9
    |
 LL | #![warn(clippy::invalid_atomic_ordering)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
 
 error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
-  --> tests/ui/rename.rs:107:9
+  --> tests/ui/rename.rs:115:9
    |
 LL | #![warn(clippy::invalid_ref)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
 
 error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked`
-  --> tests/ui/rename.rs:108:9
+  --> tests/ui/rename.rs:116:9
    |
 LL | #![warn(clippy::invalid_utf8_in_unchecked)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked`
 
 error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop`
-  --> tests/ui/rename.rs:109:9
+  --> tests/ui/rename.rs:117:9
    |
 LL | #![warn(clippy::let_underscore_drop)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop`
 
+error: lint `clippy::maybe_misused_cfg` has been renamed to `unexpected_cfgs`
+  --> tests/ui/rename.rs:118:9
+   |
+LL | #![warn(clippy::maybe_misused_cfg)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs`
+
 error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
-  --> tests/ui/rename.rs:110:9
+  --> tests/ui/rename.rs:119:9
    |
 LL | #![warn(clippy::mem_discriminant_non_enum)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
 
+error: lint `clippy::mismatched_target_os` has been renamed to `unexpected_cfgs`
+  --> tests/ui/rename.rs:120:9
+   |
+LL | #![warn(clippy::mismatched_target_os)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unexpected_cfgs`
+
 error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
-  --> tests/ui/rename.rs:111:9
+  --> tests/ui/rename.rs:121:9
    |
 LL | #![warn(clippy::panic_params)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
 
 error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally`
-  --> tests/ui/rename.rs:112:9
+  --> tests/ui/rename.rs:122:9
    |
 LL | #![warn(clippy::positional_named_format_parameters)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally`
 
 error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
-  --> tests/ui/rename.rs:113:9
+  --> tests/ui/rename.rs:123:9
    |
 LL | #![warn(clippy::temporary_cstring_as_ptr)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
 
 error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops`
-  --> tests/ui/rename.rs:114:9
+  --> tests/ui/rename.rs:124:9
    |
 LL | #![warn(clippy::undropped_manually_drops)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops`
 
 error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
-  --> tests/ui/rename.rs:115:9
+  --> tests/ui/rename.rs:125:9
    |
 LL | #![warn(clippy::unknown_clippy_lints)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
 
 error: lint `clippy::unused_label` has been renamed to `unused_labels`
-  --> tests/ui/rename.rs:116:9
+  --> tests/ui/rename.rs:126:9
    |
 LL | #![warn(clippy::unused_label)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
 
 error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons`
-  --> tests/ui/rename.rs:117:9
+  --> tests/ui/rename.rs:127:9
    |
 LL | #![warn(clippy::vtable_address_comparisons)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons`
 
-error: aborting due to 60 previous errors
+error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_empty_ranges`
+  --> tests/ui/rename.rs:128:9
+   |
+LL | #![warn(clippy::reverse_range_loop)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges`
+
+error: aborting due to 66 previous errors
 
diff --git a/tests/ui/set_contains_or_insert.rs b/tests/ui/set_contains_or_insert.rs
index 8465007402ab0..d3a3e1c878b3a 100644
--- a/tests/ui/set_contains_or_insert.rs
+++ b/tests/ui/set_contains_or_insert.rs
@@ -3,17 +3,78 @@
 #![allow(clippy::needless_borrow)]
 #![warn(clippy::set_contains_or_insert)]
 
-use std::collections::HashSet;
+use std::collections::{BTreeSet, HashSet};
 
-fn main() {
-    should_warn_cases();
+fn should_warn_hashset() {
+    let mut set = HashSet::new();
+    let value = 5;
+
+    if !set.contains(&value) {
+        set.insert(value);
+        println!("Just a comment");
+    }
+
+    if set.contains(&value) {
+        set.insert(value);
+        println!("Just a comment");
+    }
+
+    if !set.contains(&value) {
+        set.insert(value);
+    }
 
-    should_not_warn_cases();
+    if !!set.contains(&value) {
+        set.insert(value);
+        println!("Just a comment");
+    }
+
+    if (&set).contains(&value) {
+        set.insert(value);
+    }
+
+    let borrow_value = &6;
+    if !set.contains(borrow_value) {
+        set.insert(*borrow_value);
+    }
+
+    let borrow_set = &mut set;
+    if !borrow_set.contains(&value) {
+        borrow_set.insert(value);
+    }
 }
 
-fn should_warn_cases() {
+fn should_not_warn_hashset() {
     let mut set = HashSet::new();
     let value = 5;
+    let another_value = 6;
+
+    if !set.contains(&value) {
+        set.insert(another_value);
+    }
+
+    if !set.contains(&value) {
+        println!("Just a comment");
+    }
+
+    if simply_true() {
+        set.insert(value);
+    }
+
+    if !set.contains(&value) {
+        set.replace(value); //it is not insert
+        println!("Just a comment");
+    }
+
+    if set.contains(&value) {
+        println!("value is already in set");
+    } else {
+        set.insert(value);
+    }
+}
+
+fn should_warn_btreeset() {
+    let mut set = BTreeSet::new();
+    let value = 5;
 
     if !set.contains(&value) {
         set.insert(value);
@@ -49,8 +110,8 @@ fn should_warn_cases() {
     }
 }
 
-fn should_not_warn_cases() {
-    let mut set = HashSet::new();
+fn should_not_warn_btreeset() {
+    let mut set = BTreeSet::new();
     let value = 5;
     let another_value = 6;
 
@@ -81,3 +142,11 @@ fn should_not_warn_cases() {
 fn simply_true() -> bool {
     true
 }
+
+// This is placed last in order to be able to add new tests without changing line numbers
+fn main() {
+    should_warn_hashset();
+    should_warn_btreeset();
+    should_not_warn_hashset();
+    should_not_warn_btreeset();
+}
diff --git a/tests/ui/set_contains_or_insert.stderr b/tests/ui/set_contains_or_insert.stderr
index 507e20964fc20..14ad630054482 100644
--- a/tests/ui/set_contains_or_insert.stderr
+++ b/tests/ui/set_contains_or_insert.stderr
@@ -1,5 +1,5 @@
 error: usage of `HashSet::insert` after `HashSet::contains`
-  --> tests/ui/set_contains_or_insert.rs:18:13
+  --> tests/ui/set_contains_or_insert.rs:12:13
    |
 LL |     if !set.contains(&value) {
    |             ^^^^^^^^^^^^^^^^
@@ -10,7 +10,7 @@ LL |         set.insert(value);
    = help: to override `-D warnings` add `#[allow(clippy::set_contains_or_insert)]`
 
 error: usage of `HashSet::insert` after `HashSet::contains`
-  --> tests/ui/set_contains_or_insert.rs:23:12
+  --> tests/ui/set_contains_or_insert.rs:17:12
    |
 LL |     if set.contains(&value) {
    |            ^^^^^^^^^^^^^^^^
@@ -18,7 +18,7 @@ LL |         set.insert(value);
    |             ^^^^^^^^^^^^^
 
 error: usage of `HashSet::insert` after `HashSet::contains`
-  --> tests/ui/set_contains_or_insert.rs:28:13
+  --> tests/ui/set_contains_or_insert.rs:22:13
    |
 LL |     if !set.contains(&value) {
    |             ^^^^^^^^^^^^^^^^
@@ -26,7 +26,7 @@ LL |         set.insert(value);
    |             ^^^^^^^^^^^^^
 
 error: usage of `HashSet::insert` after `HashSet::contains`
-  --> tests/ui/set_contains_or_insert.rs:32:14
+  --> tests/ui/set_contains_or_insert.rs:26:14
    |
 LL |     if !!set.contains(&value) {
    |              ^^^^^^^^^^^^^^^^
@@ -34,7 +34,7 @@ LL |         set.insert(value);
    |             ^^^^^^^^^^^^^
 
 error: usage of `HashSet::insert` after `HashSet::contains`
-  --> tests/ui/set_contains_or_insert.rs:37:15
+  --> tests/ui/set_contains_or_insert.rs:31:15
    |
 LL |     if (&set).contains(&value) {
    |               ^^^^^^^^^^^^^^^^
@@ -42,7 +42,7 @@ LL |         set.insert(value);
    |             ^^^^^^^^^^^^^
 
 error: usage of `HashSet::insert` after `HashSet::contains`
-  --> tests/ui/set_contains_or_insert.rs:42:13
+  --> tests/ui/set_contains_or_insert.rs:36:13
    |
 LL |     if !set.contains(borrow_value) {
    |             ^^^^^^^^^^^^^^^^^^^^^^
@@ -50,12 +50,68 @@ LL |         set.insert(*borrow_value);
    |             ^^^^^^^^^^^^^^^^^^^^^
 
 error: usage of `HashSet::insert` after `HashSet::contains`
-  --> tests/ui/set_contains_or_insert.rs:47:20
+  --> tests/ui/set_contains_or_insert.rs:41:20
    |
 LL |     if !borrow_set.contains(&value) {
    |                    ^^^^^^^^^^^^^^^^
 LL |         borrow_set.insert(value);
    |                    ^^^^^^^^^^^^^
 
-error: aborting due to 7 previous errors
+error: usage of `BTreeSet::insert` after `BTreeSet::contains`
+  --> tests/ui/set_contains_or_insert.rs:79:13
+   |
+LL |     if !set.contains(&value) {
+   |             ^^^^^^^^^^^^^^^^
+LL |         set.insert(value);
+   |             ^^^^^^^^^^^^^
+
+error: usage of `BTreeSet::insert` after `BTreeSet::contains`
+  --> tests/ui/set_contains_or_insert.rs:84:12
+   |
+LL |     if set.contains(&value) {
+   |            ^^^^^^^^^^^^^^^^
+LL |         set.insert(value);
+   |             ^^^^^^^^^^^^^
+
+error: usage of `BTreeSet::insert` after `BTreeSet::contains`
+  --> tests/ui/set_contains_or_insert.rs:89:13
+   |
+LL |     if !set.contains(&value) {
+   |             ^^^^^^^^^^^^^^^^
+LL |         set.insert(value);
+   |             ^^^^^^^^^^^^^
+
+error: usage of `BTreeSet::insert` after `BTreeSet::contains`
+  --> tests/ui/set_contains_or_insert.rs:93:14
+   |
+LL |     if !!set.contains(&value) {
+   |              ^^^^^^^^^^^^^^^^
+LL |         set.insert(value);
+   |             ^^^^^^^^^^^^^
+
+error: usage of `BTreeSet::insert` after `BTreeSet::contains`
+  --> tests/ui/set_contains_or_insert.rs:98:15
+   |
+LL |     if (&set).contains(&value) {
+   |               ^^^^^^^^^^^^^^^^
+LL |         set.insert(value);
+   |             ^^^^^^^^^^^^^
+
+error: usage of `BTreeSet::insert` after `BTreeSet::contains`
+  --> tests/ui/set_contains_or_insert.rs:103:13
+   |
+LL |     if !set.contains(borrow_value) {
+   |             ^^^^^^^^^^^^^^^^^^^^^^
+LL |         set.insert(*borrow_value);
+   |             ^^^^^^^^^^^^^^^^^^^^^
+
+error: usage of `BTreeSet::insert` after `BTreeSet::contains`
+  --> tests/ui/set_contains_or_insert.rs:108:20
+   |
+LL |     if !borrow_set.contains(&value) {
+   |                    ^^^^^^^^^^^^^^^^
+LL |         borrow_set.insert(value);
+   |                    ^^^^^^^^^^^^^
+
+error: aborting due to 14 previous errors
 
diff --git a/tests/ui/single_match.fixed b/tests/ui/single_match.fixed
index acd70416d8bf0..5249c4408899a 100644
--- a/tests/ui/single_match.fixed
+++ b/tests/ui/single_match.fixed
@@ -253,3 +253,46 @@ mod issue8634 {
         }
     }
 }
+
+fn issue11365() {
+    enum Foo {
+        A,
+        B,
+        C,
+    }
+    use Foo::{A, B, C};
+
+    match Some(A) {
+        Some(A | B | C) => println!(),
+        None => {},
+    }
+
+    match Some(A) {
+        Some(A | B) => println!(),
+        Some { 0: C } | None => {},
+    }
+
+    match [A, A] {
+        [A, _] => println!(),
+        [_, A | B | C] => {},
+    }
+
+    match Ok::<_, u32>(Some(A)) {
+        Ok(Some(A)) => println!(),
+        Err(_) | Ok(None | Some(B | C)) => {},
+    }
+
+    if let Ok(Some(A)) = Ok::<_, u32>(Some(A)) { println!() }
+
+    match &Some(A) {
+        Some(A | B | C) => println!(),
+        None => {},
+    }
+
+    match &Some(A) {
+        &Some(A | B | C) => println!(),
+        None => {},
+    }
+
+    if let Some(A | B) = &Some(A) { println!() }
+}
diff --git a/tests/ui/single_match.rs b/tests/ui/single_match.rs
index bde7819981089..882098a56e780 100644
--- a/tests/ui/single_match.rs
+++ b/tests/ui/single_match.rs
@@ -311,3 +311,52 @@ mod issue8634 {
         }
     }
 }
+
+fn issue11365() {
+    enum Foo {
+        A,
+        B,
+        C,
+    }
+    use Foo::{A, B, C};
+
+    match Some(A) {
+        Some(A | B | C) => println!(),
+        None => {},
+    }
+
+    match Some(A) {
+        Some(A | B) => println!(),
+        Some { 0: C } | None => {},
+    }
+
+    match [A, A] {
+        [A, _] => println!(),
+        [_, A | B | C] => {},
+    }
+
+    match Ok::<_, u32>(Some(A)) {
+        Ok(Some(A)) => println!(),
+        Err(_) | Ok(None | Some(B | C)) => {},
+    }
+
+    match Ok::<_, u32>(Some(A)) {
+        Ok(Some(A)) => println!(),
+        Err(_) | Ok(None | Some(_)) => {},
+    }
+
+    match &Some(A) {
+        Some(A | B | C) => println!(),
+        None => {},
+    }
+
+    match &Some(A) {
+        &Some(A | B | C) => println!(),
+        None => {},
+    }
+
+    match &Some(A) {
+        Some(A | B) => println!(),
+        None | Some(_) => {},
+    }
+}
diff --git a/tests/ui/single_match.stderr b/tests/ui/single_match.stderr
index a249c120ee4a4..ceb2a193bf7b1 100644
--- a/tests/ui/single_match.stderr
+++ b/tests/ui/single_match.stderr
@@ -198,5 +198,23 @@ LL +         }
 LL +     }
    |
 
-error: aborting due to 18 previous errors
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+  --> tests/ui/single_match.rs:343:5
+   |
+LL | /     match Ok::<_, u32>(Some(A)) {
+LL | |         Ok(Some(A)) => println!(),
+LL | |         Err(_) | Ok(None | Some(_)) => {},
+LL | |     }
+   | |_____^ help: try: `if let Ok(Some(A)) = Ok::<_, u32>(Some(A)) { println!() }`
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+  --> tests/ui/single_match.rs:358:5
+   |
+LL | /     match &Some(A) {
+LL | |         Some(A | B) => println!(),
+LL | |         None | Some(_) => {},
+LL | |     }
+   | |_____^ help: try: `if let Some(A | B) = &Some(A) { println!() }`
+
+error: aborting due to 20 previous errors
 
diff --git a/tests/ui/std_instead_of_core.fixed b/tests/ui/std_instead_of_core.fixed
index 6ede7bfcd9f66..227b98c683e97 100644
--- a/tests/ui/std_instead_of_core.fixed
+++ b/tests/ui/std_instead_of_core.fixed
@@ -75,8 +75,17 @@ mod std_in_proc_macro_derive {
     struct B {}
 }
 
-fn main() {
-    std_instead_of_core();
-    std_instead_of_alloc();
-    alloc_instead_of_core();
+// Some intrinsics are usable on stable but live in an unstable module, but should still suggest
+// replacing std -> core
+fn intrinsic(a: *mut u8, b: *mut u8) {
+    unsafe {
+        core::intrinsics::copy(a, b, 1);
+        //~^ std_instead_of_core
+    }
 }
+
+#[clippy::msrv = "1.76"]
+fn msrv_1_76(_: std::net::IpAddr) {}
+
+#[clippy::msrv = "1.77"]
+fn msrv_1_77(_: core::net::IpAddr) {}
diff --git a/tests/ui/std_instead_of_core.rs b/tests/ui/std_instead_of_core.rs
index e22b4f61f3ecc..01bb78dd3bf1d 100644
--- a/tests/ui/std_instead_of_core.rs
+++ b/tests/ui/std_instead_of_core.rs
@@ -75,8 +75,17 @@ mod std_in_proc_macro_derive {
     struct B {}
 }
 
-fn main() {
-    std_instead_of_core();
-    std_instead_of_alloc();
-    alloc_instead_of_core();
+// Some intrinsics are usable on stable but live in an unstable module, but should still suggest
+// replacing std -> core
+fn intrinsic(a: *mut u8, b: *mut u8) {
+    unsafe {
+        std::intrinsics::copy(a, b, 1);
+        //~^ std_instead_of_core
+    }
 }
+
+#[clippy::msrv = "1.76"]
+fn msrv_1_76(_: std::net::IpAddr) {}
+
+#[clippy::msrv = "1.77"]
+fn msrv_1_77(_: std::net::IpAddr) {}
diff --git a/tests/ui/std_instead_of_core.stderr b/tests/ui/std_instead_of_core.stderr
index 22cb9db7050b8..45d60d235ceb3 100644
--- a/tests/ui/std_instead_of_core.stderr
+++ b/tests/ui/std_instead_of_core.stderr
@@ -85,5 +85,17 @@ LL |     use alloc::slice::from_ref;
    = note: `-D clippy::alloc-instead-of-core` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::alloc_instead_of_core)]`
 
-error: aborting due to 13 previous errors
+error: used import from `std` instead of `core`
+  --> tests/ui/std_instead_of_core.rs:82:9
+   |
+LL |         std::intrinsics::copy(a, b, 1);
+   |         ^^^ help: consider importing the item from `core`: `core`
+
+error: used import from `std` instead of `core`
+  --> tests/ui/std_instead_of_core.rs:91:17
+   |
+LL | fn msrv_1_77(_: std::net::IpAddr) {}
+   |                 ^^^ help: consider importing the item from `core`: `core`
+
+error: aborting due to 15 previous errors
 
diff --git a/tests/ui/suspicious_xor_used_as_pow.stderr b/tests/ui/suspicious_xor_used_as_pow.stderr
index 4faf0237c17ad..43b03676b1db5 100644
--- a/tests/ui/suspicious_xor_used_as_pow.stderr
+++ b/tests/ui/suspicious_xor_used_as_pow.stderr
@@ -2,51 +2,84 @@ error: `^` is not the exponentiation operator
   --> tests/ui/suspicious_xor_used_as_pow.rs:19:13
    |
 LL |     let _ = 2 ^ 5;
-   |             ^^^^^ help: did you mean to write: `2.pow(5)`
+   |             ^^^^^
    |
    = note: `-D clippy::suspicious-xor-used-as-pow` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::suspicious_xor_used_as_pow)]`
+help: did you mean to write
+   |
+LL |     let _ = 2.pow(5);
+   |             ~~~~~~~~
 
 error: `^` is not the exponentiation operator
   --> tests/ui/suspicious_xor_used_as_pow.rs:22:13
    |
 LL |     let _ = 2i32 ^ 9i32;
-   |             ^^^^^^^^^^^ help: did you mean to write: `2i32.pow(9i32)`
+   |             ^^^^^^^^^^^
+   |
+help: did you mean to write
+   |
+LL |     let _ = 2i32.pow(9i32);
+   |             ~~~~~~~~~~~~~~
 
 error: `^` is not the exponentiation operator
   --> tests/ui/suspicious_xor_used_as_pow.rs:24:13
    |
 LL |     let _ = 2i32 ^ 2i32;
-   |             ^^^^^^^^^^^ help: did you mean to write: `2i32.pow(2i32)`
+   |             ^^^^^^^^^^^
+   |
+help: did you mean to write
+   |
+LL |     let _ = 2i32.pow(2i32);
+   |             ~~~~~~~~~~~~~~
 
 error: `^` is not the exponentiation operator
   --> tests/ui/suspicious_xor_used_as_pow.rs:26:13
    |
 LL |     let _ = 50i32 ^ 3i32;
-   |             ^^^^^^^^^^^^ help: did you mean to write: `50i32.pow(3i32)`
+   |             ^^^^^^^^^^^^
+   |
+help: did you mean to write
+   |
+LL |     let _ = 50i32.pow(3i32);
+   |             ~~~~~~~~~~~~~~~
 
 error: `^` is not the exponentiation operator
   --> tests/ui/suspicious_xor_used_as_pow.rs:28:13
    |
 LL |     let _ = 5i32 ^ 8i32;
-   |             ^^^^^^^^^^^ help: did you mean to write: `5i32.pow(8i32)`
+   |             ^^^^^^^^^^^
+   |
+help: did you mean to write
+   |
+LL |     let _ = 5i32.pow(8i32);
+   |             ~~~~~~~~~~~~~~
 
 error: `^` is not the exponentiation operator
   --> tests/ui/suspicious_xor_used_as_pow.rs:30:13
    |
 LL |     let _ = 2i32 ^ 32i32;
-   |             ^^^^^^^^^^^^ help: did you mean to write: `2i32.pow(32i32)`
+   |             ^^^^^^^^^^^^
+   |
+help: did you mean to write
+   |
+LL |     let _ = 2i32.pow(32i32);
+   |             ~~~~~~~~~~~~~~~
 
 error: `^` is not the exponentiation operator
   --> tests/ui/suspicious_xor_used_as_pow.rs:13:9
    |
 LL |         1 ^ 2 // should warn even if inside macro
-   |         ^^^^^ help: did you mean to write: `1.pow(2)`
+   |         ^^^^^
 ...
 LL |     macro_test_inside!();
    |     -------------------- in this macro invocation
    |
    = note: this error originates in the macro `macro_test_inside` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: did you mean to write
+   |
+LL |         1.pow(2) // should warn even if inside macro
+   |         ~~~~~~~~
 
 error: aborting due to 7 previous errors
 
diff --git a/tests/ui/try_err.fixed b/tests/ui/try_err.fixed
index aae4f8ac47f82..f149bfb77740c 100644
--- a/tests/ui/try_err.fixed
+++ b/tests/ui/try_err.fixed
@@ -1,5 +1,5 @@
 //@aux-build:proc_macros.rs
-
+#![feature(try_blocks)]
 #![deny(clippy::try_err)]
 #![allow(
     clippy::unnecessary_wraps,
@@ -152,3 +152,11 @@ pub fn try_return(x: bool) -> Result<i32, i32> {
     }
     Ok(0)
 }
+
+// Test that the lint is suppressed in try block.
+pub fn try_block() -> Result<(), i32> {
+    let _: Result<_, i32> = try {
+        Err(1)?;
+    };
+    Ok(())
+}
diff --git a/tests/ui/try_err.rs b/tests/ui/try_err.rs
index 927eccf2d54c5..841ec6b5d5c77 100644
--- a/tests/ui/try_err.rs
+++ b/tests/ui/try_err.rs
@@ -1,5 +1,5 @@
 //@aux-build:proc_macros.rs
-
+#![feature(try_blocks)]
 #![deny(clippy::try_err)]
 #![allow(
     clippy::unnecessary_wraps,
@@ -152,3 +152,11 @@ pub fn try_return(x: bool) -> Result<i32, i32> {
     }
     Ok(0)
 }
+
+// Test that the lint is suppressed in try block.
+pub fn try_block() -> Result<(), i32> {
+    let _: Result<_, i32> = try {
+        Err(1)?;
+    };
+    Ok(())
+}
diff --git a/tests/ui/uninit_vec.rs b/tests/ui/uninit_vec.rs
index 58729a97d57bb..0cc77a8775d57 100644
--- a/tests/ui/uninit_vec.rs
+++ b/tests/ui/uninit_vec.rs
@@ -1,7 +1,7 @@
 #![warn(clippy::uninit_vec)]
 
-use std::mem::MaybeUninit;
 use std::cell::UnsafeCell;
+use std::mem::MaybeUninit;
 
 #[derive(Default)]
 struct MyVec {
diff --git a/tests/ui/unneeded_field_pattern.rs b/tests/ui/unneeded_field_pattern.rs
index 0dc21f4ce945d..1d42f81711bd6 100644
--- a/tests/ui/unneeded_field_pattern.rs
+++ b/tests/ui/unneeded_field_pattern.rs
@@ -1,6 +1,6 @@
 //@aux-build:proc_macros.rs
 #![warn(clippy::unneeded_field_pattern)]
-#![allow(dead_code, unused)]
+#![allow(dead_code, unused, clippy::single_match)]
 
 #[macro_use]
 extern crate proc_macros;
diff --git a/tests/ui/unused_result_ok.fixed b/tests/ui/unused_result_ok.fixed
new file mode 100644
index 0000000000000..e78fde5c9e3cc
--- /dev/null
+++ b/tests/ui/unused_result_ok.fixed
@@ -0,0 +1,40 @@
+//@aux-build:proc_macros.rs
+#![warn(clippy::unused_result_ok)]
+#![allow(dead_code)]
+
+#[macro_use]
+extern crate proc_macros;
+
+fn bad_style(x: &str) {
+    let _ = x.parse::<u32>();
+}
+
+fn good_style(x: &str) -> Option<u32> {
+    x.parse::<u32>().ok()
+}
+
+#[rustfmt::skip]
+fn strange_parse(x: &str) {
+    let _ = x   .   parse::<i32>();
+}
+
+macro_rules! v {
+    () => {
+        Ok::<(), ()>(())
+    };
+}
+
+macro_rules! w {
+    () => {
+        let _ = Ok::<(), ()>(());
+    };
+}
+
+fn main() {
+    let _ = v!();
+    w!();
+
+    external! {
+        Ok::<(),()>(()).ok();
+    };
+}
diff --git a/tests/ui/unused_result_ok.rs b/tests/ui/unused_result_ok.rs
new file mode 100644
index 0000000000000..117d64c4cec60
--- /dev/null
+++ b/tests/ui/unused_result_ok.rs
@@ -0,0 +1,40 @@
+//@aux-build:proc_macros.rs
+#![warn(clippy::unused_result_ok)]
+#![allow(dead_code)]
+
+#[macro_use]
+extern crate proc_macros;
+
+fn bad_style(x: &str) {
+    x.parse::<u32>().ok();
+}
+
+fn good_style(x: &str) -> Option<u32> {
+    x.parse::<u32>().ok()
+}
+
+#[rustfmt::skip]
+fn strange_parse(x: &str) {
+    x   .   parse::<i32>()   .   ok   ();
+}
+
+macro_rules! v {
+    () => {
+        Ok::<(), ()>(())
+    };
+}
+
+macro_rules! w {
+    () => {
+        Ok::<(), ()>(()).ok();
+    };
+}
+
+fn main() {
+    v!().ok();
+    w!();
+
+    external! {
+        Ok::<(),()>(()).ok();
+    };
+}
diff --git a/tests/ui/unused_result_ok.stderr b/tests/ui/unused_result_ok.stderr
new file mode 100644
index 0000000000000..241e0c71261e3
--- /dev/null
+++ b/tests/ui/unused_result_ok.stderr
@@ -0,0 +1,52 @@
+error: ignoring a result with `.ok()` is misleading
+  --> tests/ui/unused_result_ok.rs:9:5
+   |
+LL |     x.parse::<u32>().ok();
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::unused-result-ok` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::unused_result_ok)]`
+help: consider using `let _ =` and removing the call to `.ok()` instead
+   |
+LL |     let _ = x.parse::<u32>();
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: ignoring a result with `.ok()` is misleading
+  --> tests/ui/unused_result_ok.rs:18:5
+   |
+LL |     x   .   parse::<i32>()   .   ok   ();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: consider using `let _ =` and removing the call to `.ok()` instead
+   |
+LL |     let _ = x   .   parse::<i32>();
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: ignoring a result with `.ok()` is misleading
+  --> tests/ui/unused_result_ok.rs:34:5
+   |
+LL |     v!().ok();
+   |     ^^^^^^^^^
+   |
+help: consider using `let _ =` and removing the call to `.ok()` instead
+   |
+LL |     let _ = v!();
+   |     ~~~~~~~~~~~~
+
+error: ignoring a result with `.ok()` is misleading
+  --> tests/ui/unused_result_ok.rs:29:9
+   |
+LL |         Ok::<(), ()>(()).ok();
+   |         ^^^^^^^^^^^^^^^^^^^^^
+...
+LL |     w!();
+   |     ---- in this macro invocation
+   |
+   = note: this error originates in the macro `w` (in Nightly builds, run with -Z macro-backtrace for more info)
+help: consider using `let _ =` and removing the call to `.ok()` instead
+   |
+LL |         let _ = Ok::<(), ()>(());
+   |         ~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 4 previous errors
+
diff --git a/tests/ui/unwrap_expect_used.rs b/tests/ui/unwrap_expect_used.rs
index 96368a0705361..a56bd0a8d0790 100644
--- a/tests/ui/unwrap_expect_used.rs
+++ b/tests/ui/unwrap_expect_used.rs
@@ -31,7 +31,7 @@ fn main() {
 
     // Don't trigger on unwrap_err on an option
     Some(3).unwrap_err();
-    Some(3).expect_err("Hellow none!");
+    Some(3).expect_err("Hello none!");
 
     // Issue #11245: The `Err` variant can never be constructed so do not lint this.
     let x: Result<(), !> = Ok(());
diff --git a/tests/ui/while_let_on_iterator.fixed b/tests/ui/while_let_on_iterator.fixed
index 59b5c858d0460..b8087c6e000f5 100644
--- a/tests/ui/while_let_on_iterator.fixed
+++ b/tests/ui/while_let_on_iterator.fixed
@@ -456,6 +456,15 @@ fn fn_once_closure() {
     });
 }
 
+fn issue13123() {
+    let mut it = 0..20;
+    'label: for n in it {
+        if n % 25 == 0 {
+            break 'label;
+        }
+    }
+}
+
 fn main() {
     let mut it = 0..20;
     for _ in it {
diff --git a/tests/ui/while_let_on_iterator.rs b/tests/ui/while_let_on_iterator.rs
index 559513d56946d..8e02f59b51265 100644
--- a/tests/ui/while_let_on_iterator.rs
+++ b/tests/ui/while_let_on_iterator.rs
@@ -456,6 +456,15 @@ fn fn_once_closure() {
     });
 }
 
+fn issue13123() {
+    let mut it = 0..20;
+    'label: while let Some(n) = it.next() {
+        if n % 25 == 0 {
+            break 'label;
+        }
+    }
+}
+
 fn main() {
     let mut it = 0..20;
     while let Some(..) = it.next() {
diff --git a/tests/ui/while_let_on_iterator.stderr b/tests/ui/while_let_on_iterator.stderr
index 8ff1f23644b16..d96b26acf345d 100644
--- a/tests/ui/while_let_on_iterator.stderr
+++ b/tests/ui/while_let_on_iterator.stderr
@@ -160,8 +160,14 @@ LL |         while let Some(x) = it.next() {
 error: this loop could be written as a `for` loop
   --> tests/ui/while_let_on_iterator.rs:461:5
    |
+LL |     'label: while let Some(n) = it.next() {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `'label: for n in it`
+
+error: this loop could be written as a `for` loop
+  --> tests/ui/while_let_on_iterator.rs:470:5
+   |
 LL |     while let Some(..) = it.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`
 
-error: aborting due to 27 previous errors
+error: aborting due to 28 previous errors
 
diff --git a/tests/versioncheck.rs b/tests/versioncheck.rs
index eba5405e67ed2..6832833393766 100644
--- a/tests/versioncheck.rs
+++ b/tests/versioncheck.rs
@@ -1,4 +1,3 @@
-#![cfg_attr(feature = "deny-warnings", deny(warnings))]
 #![warn(rust_2018_idioms, unused_lifetimes)]
 #![allow(clippy::single_match_else)]
 
diff --git a/tests/workspace_test/path_dep/Cargo.toml b/tests/workspace_test/path_dep/Cargo.toml
index 85a91cd2decd5..98b4fb7aa5042 100644
--- a/tests/workspace_test/path_dep/Cargo.toml
+++ b/tests/workspace_test/path_dep/Cargo.toml
@@ -1,3 +1,6 @@
 [package]
 name = "path_dep"
 version = "0.1.0"
+
+[features]
+primary_package_test = []
diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html
index 0c0f28e4fbd3d..300c9de178f2b 100644
--- a/util/gh-pages/index.html
+++ b/util/gh-pages/index.html
@@ -541,7 +541,7 @@ <h1>Clippy Lints</h1>
                     <div class="col-12 col-md-5 search-control">
                         <div class="input-group">
                             <label class="input-group-addon" id="filter-label" for="search-input">Filter:</label>
-                            <input type="text" class="form-control filter-input" placeholder="Keywords or search string" id="search-input"
+                            <input type="text" class="form-control filter-input" placeholder="Keywords or search string (`S` or `/` to focus)" id="search-input"
                                 ng-model="search" ng-blur="updatePath()" ng-keyup="$event.keyCode == 13 && updatePath()"
                                 ng-model-options="{debounce: 50}" />
                             <span class="input-group-btn">
@@ -605,7 +605,7 @@ <h2 class="panel-title">
                             <a href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+{{lint.id}}">Related Issues</a>
                         </div>
                         <!-- Jump to source -->
-                        <div class="lint-additional-info-item">
+                        <div class="lint-additional-info-item" ng-if="lint.id_span">
                             <a href="https://github.com/rust-lang/rust-clippy/blob/{{docVersion}}/clippy_lints/{{lint.id_span.path}}#L{{lint.id_span.line}}">View Source</a>
                         </div>
                     </div>
diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js
index 661f80a6d3468..ed1e090e1b540 100644
--- a/util/gh-pages/script.js
+++ b/util/gh-pages/script.js
@@ -1,5 +1,5 @@
 (function () {
-    var md = window.markdownit({
+    const md = window.markdownit({
         html: true,
         linkify: true,
         typographer: true,
@@ -17,7 +17,7 @@
     });
 
     function scrollToLint(lintId) {
-        var target = document.getElementById(lintId);
+        const target = document.getElementById(lintId);
         if (!target) {
             return;
         }
@@ -25,21 +25,17 @@
     }
 
     function scrollToLintByURL($scope, $location) {
-        var removeListener = $scope.$on('ngRepeatFinished', function (ngRepeatFinishedEvent) {
+        const removeListener = $scope.$on('ngRepeatFinished', function (ngRepeatFinishedEvent) {
             scrollToLint($location.path().substring(1));
             removeListener();
         });
     }
 
     function selectGroup($scope, selectedGroup) {
-        var groups = $scope.groups;
-        for (var group in groups) {
+        const groups = $scope.groups;
+        for (const group in groups) {
             if (groups.hasOwnProperty(group)) {
-                if (group === selectedGroup) {
-                    groups[group] = true;
-                } else {
-                    groups[group] = false;
-                }
+                groups[group] = group === selectedGroup;
             }
         }
     }
@@ -108,7 +104,7 @@
         })
         .controller("lintList", function ($scope, $http, $location, $timeout) {
             // Level filter
-            var LEVEL_FILTERS_DEFAULT = {allow: true, warn: true, deny: true, none: true};
+            const LEVEL_FILTERS_DEFAULT = {allow: true, warn: true, deny: true, none: true};
             $scope.levels = { ...LEVEL_FILTERS_DEFAULT };
             $scope.byLevels = function (lint) {
                 return $scope.levels[lint.level];
@@ -367,7 +363,7 @@
             }
 
             $scope.clearVersionFilters = function () {
-                for (let filter in $scope.versionFilters) {
+                for (const filter in $scope.versionFilters) {
                     $scope.versionFilters[filter] = { enabled: false, minorVersion: null };
                 }
             }
@@ -378,7 +374,7 @@
 
             $scope.updateVersionFilters = function() {
                 for (const filter in $scope.versionFilters) {
-                    let minorVersion = $scope.versionFilters[filter].minorVersion;
+                    const minorVersion = $scope.versionFilters[filter].minorVersion;
 
                     // 1.29.0 and greater
                     if (minorVersion && minorVersion > 28) {
@@ -391,14 +387,14 @@
             }
 
             $scope.byVersion = function(lint) {
-                let filters = $scope.versionFilters;
+                const filters = $scope.versionFilters;
                 for (const filter in filters) {
                     if (filters[filter].enabled) {
-                        let minorVersion = filters[filter].minorVersion;
+                        const minorVersion = filters[filter].minorVersion;
 
                         // Strip the "pre " prefix for pre 1.29.0 lints
-                        let lintVersion = lint.version.startsWith("pre ") ? lint.version.substring(4, lint.version.length) : lint.version;
-                        let lintMinorVersion = lintVersion.substring(2, 4);
+                        const lintVersion = lint.version.startsWith("pre ") ? lint.version.substring(4, lint.version.length) : lint.version;
+                        const lintMinorVersion = lintVersion.substring(2, 4);
 
                         switch (filter) {
                             // "=" gets the highest priority, since all filters are inclusive
@@ -441,8 +437,8 @@
 
                 // Search the description
                 // The use of `for`-loops instead of `foreach` enables us to return early
-                let terms = searchStr.split(" ");
-                let docsLowerCase = lint.docs.toLowerCase();
+                const terms = searchStr.split(" ");
+                const docsLowerCase = lint.docs.toLowerCase();
                 for (index = 0; index < terms.length; index++) {
                     // This is more likely and will therefore be checked first
                     if (docsLowerCase.indexOf(terms[index]) !== -1) {
@@ -479,7 +475,7 @@
                 const clipboard = document.getElementById("clipboard-" + lint.id);
                 if (clipboard) {
                     let resetClipboardTimeout = null;
-                    let resetClipboardIcon = clipboard.innerHTML;
+                    const resetClipboardIcon = clipboard.innerHTML;
 
                     function resetClipboard() {
                         resetClipboardTimeout = null;
@@ -511,7 +507,7 @@
                     $scope.data = data;
                     $scope.loading = false;
 
-                    var selectedGroup = getQueryVariable("sel");
+                    const selectedGroup = getQueryVariable("sel");
                     if (selectedGroup) {
                         selectGroup($scope, selectedGroup.toLowerCase());
                     }
@@ -519,7 +515,7 @@
                     scrollToLintByURL($scope, $location);
 
                     setTimeout(function () {
-                        var el = document.getElementById('filter-input');
+                        const el = document.getElementById('filter-input');
                         if (el) { el.focus() }
                     }, 0);
                 })
@@ -531,10 +527,10 @@
 })();
 
 function getQueryVariable(variable) {
-    var query = window.location.search.substring(1);
-    var vars = query.split('&');
-    for (var i = 0; i < vars.length; i++) {
-        var pair = vars[i].split('=');
+    const query = window.location.search.substring(1);
+    const vars = query.split('&');
+    for (const entry of vars) {
+        const pair = entry.split('=');
         if (decodeURIComponent(pair[0]) == variable) {
             return decodeURIComponent(pair[1]);
         }
@@ -579,6 +575,32 @@ function setTheme(theme, store) {
     }
 }
 
+function handleShortcut(ev) {
+    if (ev.ctrlKey || ev.altKey || ev.metaKey) {
+        return;
+    }
+
+    if (document.activeElement.tagName === "INPUT") {
+        if (ev.key === "Escape") {
+            document.activeElement.blur();
+        }
+    } else {
+        switch (ev.key) {
+            case "s":
+            case "S":
+            case "/":
+                ev.preventDefault(); // To prevent the key to be put into the input.
+                document.getElementById("search-input").focus();
+                break;
+            default:
+                break;
+        }
+    }
+}
+
+document.addEventListener("keypress", handleShortcut);
+document.addEventListener("keydown", handleShortcut);
+
 // loading the theme after the initial load
 const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
 const theme = localStorage.getItem('clippy-lint-list-theme');

From 9948b423a0e602e514e63ead9dfebdfa9acb0f03 Mon Sep 17 00:00:00 2001
From: Alexey Semenyuk <alexsemenyuk88@gmail.com>
Date: Fri, 9 Aug 2024 01:35:28 +0500
Subject: [PATCH 15/54] Fix confusing message in double_must_use lint

---
 clippy_lints/src/functions/must_use.rs |  4 ++--
 tests/ui/double_must_use.rs            |  8 ++++----
 tests/ui/double_must_use.stderr        | 16 ++++++++--------
 3 files changed, 14 insertions(+), 14 deletions(-)

diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs
index b179d7b52492c..97be38c4a7dd6 100644
--- a/clippy_lints/src/functions/must_use.rs
+++ b/clippy_lints/src/functions/must_use.rs
@@ -129,9 +129,9 @@ fn check_needless_must_use(
             cx,
             DOUBLE_MUST_USE,
             fn_header_span,
-            "this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`",
+            "this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`",
             None,
-            "either add some descriptive text or remove the attribute",
+            "either add some descriptive message or remove the attribute",
         );
     }
 }
diff --git a/tests/ui/double_must_use.rs b/tests/ui/double_must_use.rs
index 615de3e24743c..4460aeb075bf6 100644
--- a/tests/ui/double_must_use.rs
+++ b/tests/ui/double_must_use.rs
@@ -3,19 +3,19 @@
 
 #[must_use]
 pub fn must_use_result() -> Result<(), ()> {
-    //~^ ERROR: this function has an empty `#[must_use]` attribute, but returns a type already
+    //~^ ERROR: this function has a `#[must_use]` attribute with no message, but returns a type already
     unimplemented!();
 }
 
 #[must_use]
 pub fn must_use_tuple() -> (Result<(), ()>, u8) {
-    //~^ ERROR: this function has an empty `#[must_use]` attribute, but returns a type already
+    //~^ ERROR: this function has a `#[must_use]` attribute with no message, but returns a type already
     unimplemented!();
 }
 
 #[must_use]
 pub fn must_use_array() -> [Result<(), ()>; 1] {
-    //~^ ERROR: this function has an empty `#[must_use]` attribute, but returns a type already
+    //~^ ERROR: this function has a `#[must_use]` attribute with no message, but returns a type already
     unimplemented!();
 }
 
@@ -32,7 +32,7 @@ async fn async_must_use() -> usize {
 
 #[must_use]
 async fn async_must_use_result() -> Result<(), ()> {
-    //~^ ERROR: this function has an empty `#[must_use]` attribute, but returns a type already
+    //~^ ERROR: this function has a `#[must_use]` attribute with no message, but returns a type already
     Ok(())
 }
 
diff --git a/tests/ui/double_must_use.stderr b/tests/ui/double_must_use.stderr
index 0f2154ecbcfab..b26d1e48a8b86 100644
--- a/tests/ui/double_must_use.stderr
+++ b/tests/ui/double_must_use.stderr
@@ -1,36 +1,36 @@
-error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
+error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
   --> tests/ui/double_must_use.rs:5:1
    |
 LL | pub fn must_use_result() -> Result<(), ()> {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: either add some descriptive text or remove the attribute
+   = help: either add some descriptive message or remove the attribute
    = note: `-D clippy::double-must-use` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::double_must_use)]`
 
-error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
+error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
   --> tests/ui/double_must_use.rs:11:1
    |
 LL | pub fn must_use_tuple() -> (Result<(), ()>, u8) {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: either add some descriptive text or remove the attribute
+   = help: either add some descriptive message or remove the attribute
 
-error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
+error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
   --> tests/ui/double_must_use.rs:17:1
    |
 LL | pub fn must_use_array() -> [Result<(), ()>; 1] {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: either add some descriptive text or remove the attribute
+   = help: either add some descriptive message or remove the attribute
 
-error: this function has an empty `#[must_use]` attribute, but returns a type already marked as `#[must_use]`
+error: this function has a `#[must_use]` attribute with no message, but returns a type already marked as `#[must_use]`
   --> tests/ui/double_must_use.rs:34:1
    |
 LL | async fn async_must_use_result() -> Result<(), ()> {
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: either add some descriptive text or remove the attribute
+   = help: either add some descriptive message or remove the attribute
 
 error: aborting due to 4 previous errors
 

From e785219238e73f353f392afe30073180c5b2c5d6 Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote <n.nethercote@gmail.com>
Date: Thu, 8 Aug 2024 17:18:20 +1000
Subject: [PATCH 16/54] Shrink `TyKind::FnPtr`.

By splitting the `FnSig` within `TyKind::FnPtr` into `FnSigTys` and
`FnHeader`, which can be packed more efficiently. This reduces the size
of the hot `TyKind` type from 32 bytes to 24 bytes on 64-bit platforms.
This reduces peak memory usage by a few percent on some benchmarks. It
also reduces cache misses and page faults similarly, though this doesn't
translate to clear cycles or wall-time improvements on CI.
---
 clippy_lints/src/casts/fn_to_numeric_cast.rs                 | 2 +-
 clippy_lints/src/casts/fn_to_numeric_cast_any.rs             | 2 +-
 clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs | 2 +-
 clippy_lints/src/default_numeric_fallback.rs                 | 2 +-
 clippy_lints/src/dereference.rs                              | 2 +-
 clippy_lints/src/eta_reduction.rs                            | 2 +-
 clippy_lints/src/methods/map_flatten.rs                      | 2 +-
 clippy_lints/src/mixed_read_write_in_expression.rs           | 2 +-
 clippy_lints/src/multiple_unsafe_ops_per_block.rs            | 2 +-
 clippy_lints/src/mut_reference.rs                            | 2 +-
 clippy_utils/src/ty.rs                                       | 4 ++--
 clippy_utils/src/visitors.rs                                 | 2 +-
 12 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/clippy_lints/src/casts/fn_to_numeric_cast.rs b/clippy_lints/src/casts/fn_to_numeric_cast.rs
index f263bec1576d0..dbe03e4ae8095 100644
--- a/clippy_lints/src/casts/fn_to_numeric_cast.rs
+++ b/clippy_lints/src/casts/fn_to_numeric_cast.rs
@@ -15,7 +15,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
     }
 
     match cast_from.kind() {
-        ty::FnDef(..) | ty::FnPtr(_) => {
+        ty::FnDef(..) | ty::FnPtr(..) => {
             let mut applicability = Applicability::MaybeIncorrect;
             let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
             let to_nbits = utils::int_ty_to_nbits(cast_to, cx.tcx);
diff --git a/clippy_lints/src/casts/fn_to_numeric_cast_any.rs b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs
index 75de53f73ee7a..5dc6df1e907d0 100644
--- a/clippy_lints/src/casts/fn_to_numeric_cast_any.rs
+++ b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs
@@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
         _ => { /* continue to checks */ },
     }
 
-    if let ty::FnDef(..) | ty::FnPtr(_) = cast_from.kind() {
+    if let ty::FnDef(..) | ty::FnPtr(..) = cast_from.kind() {
         let mut applicability = Applicability::MaybeIncorrect;
         let from_snippet = snippet_with_applicability(cx, cast_expr.span, "..", &mut applicability);
 
diff --git a/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs b/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs
index 0e11bcfb8ecdb..dfbae1618ac62 100644
--- a/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs
+++ b/clippy_lints/src/casts/fn_to_numeric_cast_with_truncation.rs
@@ -14,7 +14,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
         _ => return,
     }
     match cast_from.kind() {
-        ty::FnDef(..) | ty::FnPtr(_) => {
+        ty::FnDef(..) | ty::FnPtr(..) => {
             let mut applicability = Applicability::MaybeIncorrect;
             let from_snippet = snippet_with_applicability(cx, cast_expr.span, "x", &mut applicability);
 
diff --git a/clippy_lints/src/default_numeric_fallback.rs b/clippy_lints/src/default_numeric_fallback.rs
index a74b3a8c8362c..05c3cd3c81407 100644
--- a/clippy_lints/src/default_numeric_fallback.rs
+++ b/clippy_lints/src/default_numeric_fallback.rs
@@ -236,7 +236,7 @@ fn fn_sig_opt<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<PolyFnSig<'
     // We can't use `Ty::fn_sig` because it automatically performs args, this may result in FNs.
     match node_ty.kind() {
         ty::FnDef(def_id, _) => Some(cx.tcx.fn_sig(*def_id).instantiate_identity()),
-        ty::FnPtr(fn_sig) => Some(*fn_sig),
+        ty::FnPtr(sig_tys, hdr) => Some(sig_tys.with(*hdr)),
         _ => None,
     }
 }
diff --git a/clippy_lints/src/dereference.rs b/clippy_lints/src/dereference.rs
index d0cb24884686a..0e55d3db469ae 100644
--- a/clippy_lints/src/dereference.rs
+++ b/clippy_lints/src/dereference.rs
@@ -872,7 +872,7 @@ impl TyCoercionStability {
                 | ty::Pat(..)
                 | ty::Float(_)
                 | ty::RawPtr(..)
-                | ty::FnPtr(_)
+                | ty::FnPtr(..)
                 | ty::Str
                 | ty::Slice(..)
                 | ty::Adt(..)
diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs
index 5a7226d590c4d..a7e831fdc42a8 100644
--- a/clippy_lints/src/eta_reduction.rs
+++ b/clippy_lints/src/eta_reduction.rs
@@ -158,7 +158,7 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc
 
                     cx.tcx.fn_sig(def).skip_binder().skip_binder()
                 },
-                ty::FnPtr(sig) => sig.skip_binder(),
+                ty::FnPtr(sig_tys, hdr) => sig_tys.with(*hdr).skip_binder(),
                 ty::Closure(_, subs) => cx
                     .tcx
                     .signature_unclosure(subs.as_closure().sig(), Safety::Safe)
diff --git a/clippy_lints/src/methods/map_flatten.rs b/clippy_lints/src/methods/map_flatten.rs
index def8be2ef73dc..22a03825194e0 100644
--- a/clippy_lints/src/methods/map_flatten.rs
+++ b/clippy_lints/src/methods/map_flatten.rs
@@ -58,7 +58,7 @@ fn try_get_caller_ty_name_and_method_name(
 fn is_map_to_option(cx: &LateContext<'_>, map_arg: &Expr<'_>) -> bool {
     let map_closure_ty = cx.typeck_results().expr_ty(map_arg);
     match map_closure_ty.kind() {
-        ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(_) => {
+        ty::Closure(_, _) | ty::FnDef(_, _) | ty::FnPtr(..) => {
             let map_closure_sig = match map_closure_ty.kind() {
                 ty::Closure(_, args) => args.as_closure().sig(),
                 _ => map_closure_ty.fn_sig(cx.tcx),
diff --git a/clippy_lints/src/mixed_read_write_in_expression.rs b/clippy_lints/src/mixed_read_write_in_expression.rs
index 6964d8c8dbb33..0b3769ecb7cc4 100644
--- a/clippy_lints/src/mixed_read_write_in_expression.rs
+++ b/clippy_lints/src/mixed_read_write_in_expression.rs
@@ -166,7 +166,7 @@ impl<'a, 'tcx> Visitor<'tcx> for DivergenceVisitor<'a, 'tcx> {
             ExprKind::Call(func, _) => {
                 let typ = self.cx.typeck_results().expr_ty(func);
                 match typ.kind() {
-                    ty::FnDef(..) | ty::FnPtr(_) => {
+                    ty::FnDef(..) | ty::FnPtr(..) => {
                         let sig = typ.fn_sig(self.cx.tcx);
                         if self.cx.tcx.instantiate_bound_regions_with_erased(sig).output().kind() == &ty::Never {
                             self.report_diverging_sub_expr(e);
diff --git a/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/clippy_lints/src/multiple_unsafe_ops_per_block.rs
index da74a7c7145a6..0bde0da3cd814 100644
--- a/clippy_lints/src/multiple_unsafe_ops_per_block.rs
+++ b/clippy_lints/src/multiple_unsafe_ops_per_block.rs
@@ -130,7 +130,7 @@ fn collect_unsafe_exprs<'tcx>(
             ExprKind::Call(path_expr, _) => {
                 let sig = match *cx.typeck_results().expr_ty(path_expr).kind() {
                     ty::FnDef(id, _) => cx.tcx.fn_sig(id).skip_binder(),
-                    ty::FnPtr(sig) => sig,
+                    ty::FnPtr(sig_tys, hdr) => sig_tys.with(hdr),
                     _ => return Continue(Descend::Yes),
                 };
                 if sig.safety() == Safety::Unsafe {
diff --git a/clippy_lints/src/mut_reference.rs b/clippy_lints/src/mut_reference.rs
index 0a3b769c3e604..3c0f06f66d105 100644
--- a/clippy_lints/src/mut_reference.rs
+++ b/clippy_lints/src/mut_reference.rs
@@ -79,7 +79,7 @@ fn check_arguments<'tcx>(
     fn_kind: &str,
 ) {
     match type_definition.kind() {
-        ty::FnDef(..) | ty::FnPtr(_) => {
+        ty::FnDef(..) | ty::FnPtr(..) => {
             let parameters = type_definition.fn_sig(cx.tcx).skip_binder().inputs();
             for (argument, parameter) in iter::zip(arguments, parameters) {
                 match parameter.kind() {
diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs
index bd48990aea95f..2f6faba073e95 100644
--- a/clippy_utils/src/ty.rs
+++ b/clippy_utils/src/ty.rs
@@ -541,7 +541,7 @@ pub fn peel_mid_ty_refs_is_mutable(ty: Ty<'_>) -> (Ty<'_>, usize, Mutability) {
 /// Returns `true` if the given type is an `unsafe` function.
 pub fn type_is_unsafe_function<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
     match ty.kind() {
-        ty::FnDef(..) | ty::FnPtr(_) => ty.fn_sig(cx.tcx).safety() == Safety::Unsafe,
+        ty::FnDef(..) | ty::FnPtr(..) => ty.fn_sig(cx.tcx).safety() == Safety::Unsafe,
         _ => false,
     }
 }
@@ -721,7 +721,7 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'t
             cx.tcx.item_super_predicates(def_id).iter_instantiated(cx.tcx, args),
             cx.tcx.opt_parent(def_id),
         ),
-        ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig, None)),
+        ty::FnPtr(sig_tys, hdr) => Some(ExprFnSig::Sig(sig_tys.with(hdr), None)),
         ty::Dynamic(bounds, _, _) => {
             let lang_items = cx.tcx.lang_items();
             match bounds.principal() {
diff --git a/clippy_utils/src/visitors.rs b/clippy_utils/src/visitors.rs
index 2a5d3536ff6b7..e5b6d3965e930 100644
--- a/clippy_utils/src/visitors.rs
+++ b/clippy_utils/src/visitors.rs
@@ -441,7 +441,7 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
                     ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().safety() == Safety::Unsafe => {
                         self.is_unsafe = true;
                     },
-                    ty::FnPtr(sig) if sig.safety() == Safety::Unsafe => self.is_unsafe = true,
+                    ty::FnPtr(_, hdr) if hdr.safety == Safety::Unsafe => self.is_unsafe = true,
                     _ => walk_expr(self, e),
                 },
                 ExprKind::Path(ref p)

From d85cf0bacd8ea565557317940d878a535189a6d9 Mon Sep 17 00:00:00 2001
From: rzvxa <rzvxa@protonmail.com>
Date: Fri, 9 Aug 2024 12:18:56 +0330
Subject: [PATCH 17/54] use `fulfill_or_allowed` over `is_lint_allowed`.

---
 .../src/inconsistent_struct_constructor.rs    | 23 ++++++++++---------
 1 file changed, 12 insertions(+), 11 deletions(-)

diff --git a/clippy_lints/src/inconsistent_struct_constructor.rs b/clippy_lints/src/inconsistent_struct_constructor.rs
index da289225509bf..d386bfca6baa1 100644
--- a/clippy_lints/src/inconsistent_struct_constructor.rs
+++ b/clippy_lints/src/inconsistent_struct_constructor.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_lint_allowed;
+use clippy_utils::fulfill_or_allowed;
 use clippy_utils::source::snippet;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_errors::Applicability;
@@ -74,7 +74,6 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
             && adt_def.is_struct()
             && let Some(local_def_id) = adt_def.did().as_local()
             && let ty_hir_id = cx.tcx.local_def_id_to_hir_id(local_def_id)
-            && !is_lint_allowed(cx, INCONSISTENT_STRUCT_CONSTRUCTOR, ty_hir_id)
             && let Some(variant) = adt_def.variants().iter().next()
         {
             let mut def_order_map = FxHashMap::default();
@@ -107,15 +106,17 @@ impl<'tcx> LateLintPass<'tcx> for InconsistentStructConstructor {
                 snippet(cx, qpath.span(), ".."),
             );
 
-            span_lint_and_sugg(
-                cx,
-                INCONSISTENT_STRUCT_CONSTRUCTOR,
-                expr.span,
-                "struct constructor field order is inconsistent with struct definition field order",
-                "try",
-                sugg,
-                Applicability::MachineApplicable,
-            );
+            if !fulfill_or_allowed(cx, INCONSISTENT_STRUCT_CONSTRUCTOR, Some(ty_hir_id)) {
+                span_lint_and_sugg(
+                    cx,
+                    INCONSISTENT_STRUCT_CONSTRUCTOR,
+                    expr.span,
+                    "struct constructor field order is inconsistent with struct definition field order",
+                    "try",
+                    sugg,
+                    Applicability::MachineApplicable,
+                );
+            }
         }
     }
 }

From b32a0176faa3ff2aba07f38b7d7cf72304d6d4e0 Mon Sep 17 00:00:00 2001
From: Alex Macleod <alex@macleod.io>
Date: Fri, 9 Aug 2024 13:00:24 +0000
Subject: [PATCH 18/54] Replace `rustc_semver` with
 `rustc_session::RustcVersion`

---
 clippy_config/Cargo.toml                         |  1 -
 clippy_config/src/lib.rs                         |  1 +
 clippy_config/src/msrvs.rs                       | 16 ++++++++--------
 clippy_lints/Cargo.toml                          |  1 -
 clippy_lints/src/approx_const.rs                 |  3 +--
 clippy_lints/src/incompatible_msrv.rs            | 15 +++++++--------
 clippy_lints/src/manual_retain.rs                |  3 +--
 clippy_lints/src/std_instead_of_core.rs          |  5 +----
 .../internal_lints/lint_without_lint_pass.rs     | 10 +++++++---
 clippy_utils/Cargo.toml                          |  1 -
 clippy_utils/src/qualify_min_const_fn.rs         |  7 +------
 tests/ui/min_rust_version_invalid_attr.rs        |  2 +-
 tests/ui/min_rust_version_invalid_attr.stderr    |  4 ++--
 13 files changed, 30 insertions(+), 39 deletions(-)

diff --git a/clippy_config/Cargo.toml b/clippy_config/Cargo.toml
index d5b28e2532371..5c4e0761dbca7 100644
--- a/clippy_config/Cargo.toml
+++ b/clippy_config/Cargo.toml
@@ -7,7 +7,6 @@ edition = "2021"
 
 [dependencies]
 itertools = "0.12"
-rustc-semver = "1.1"
 serde = { version = "1.0", features = ["derive"] }
 toml = "0.7.3"
 
diff --git a/clippy_config/src/lib.rs b/clippy_config/src/lib.rs
index d2246f12029b3..3afe599c7b6e4 100644
--- a/clippy_config/src/lib.rs
+++ b/clippy_config/src/lib.rs
@@ -14,6 +14,7 @@
 )]
 
 extern crate rustc_ast;
+extern crate rustc_attr;
 extern crate rustc_data_structures;
 #[allow(unused_extern_crates)]
 extern crate rustc_driver;
diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs
index fc56ac5179686..5b9707efd2634 100644
--- a/clippy_config/src/msrvs.rs
+++ b/clippy_config/src/msrvs.rs
@@ -1,6 +1,6 @@
 use rustc_ast::Attribute;
-use rustc_semver::RustcVersion;
-use rustc_session::Session;
+use rustc_attr::parse_version;
+use rustc_session::{RustcVersion, Session};
 use rustc_span::{sym, Symbol};
 use serde::Deserialize;
 use std::fmt;
@@ -10,7 +10,7 @@ macro_rules! msrv_aliases {
         $($name:ident),* $(,)?
     })*) => {
         $($(
-        pub const $name: RustcVersion = RustcVersion::new($major, $minor, $patch);
+        pub const $name: RustcVersion = RustcVersion { major: $major, minor :$minor, patch: $patch };
         )*)*
     };
 }
@@ -81,9 +81,9 @@ impl<'de> Deserialize<'de> for Msrv {
         D: serde::Deserializer<'de>,
     {
         let v = String::deserialize(deserializer)?;
-        RustcVersion::parse(&v)
+        parse_version(Symbol::intern(&v))
             .map(|v| Msrv { stack: vec![v] })
-            .map_err(|_| serde::de::Error::custom("not a valid Rust version"))
+            .ok_or_else(|| serde::de::Error::custom("not a valid Rust version"))
     }
 }
 
@@ -95,7 +95,7 @@ impl Msrv {
     pub fn read_cargo(&mut self, sess: &Session) {
         let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
             .ok()
-            .and_then(|v| RustcVersion::parse(&v).ok());
+            .and_then(|v| parse_version(Symbol::intern(&v)));
 
         match (self.current(), cargo_msrv) {
             (None, Some(cargo_msrv)) => self.stack = vec![cargo_msrv],
@@ -115,7 +115,7 @@ impl Msrv {
     }
 
     pub fn meets(&self, required: RustcVersion) -> bool {
-        self.current().map_or(true, |version| version.meets(required))
+        self.current().map_or(true, |msrv| msrv >= required)
     }
 
     fn parse_attr(sess: &Session, attrs: &[Attribute]) -> Option<RustcVersion> {
@@ -131,7 +131,7 @@ impl Msrv {
             }
 
             if let Some(msrv) = msrv_attr.value_str() {
-                if let Ok(version) = RustcVersion::parse(msrv.as_str()) {
+                if let Some(version) = parse_version(msrv) {
                     return Some(version);
                 }
 
diff --git a/clippy_lints/Cargo.toml b/clippy_lints/Cargo.toml
index 99ed93468a01a..fbd4566da58c7 100644
--- a/clippy_lints/Cargo.toml
+++ b/clippy_lints/Cargo.toml
@@ -25,7 +25,6 @@ regex = { version = "1.5", optional = true }
 unicode-normalization = "0.1"
 unicode-script = { version = "0.5", default-features = false }
 semver = "1.0"
-rustc-semver = "1.1"
 url = "2.2"
 
 [dev-dependencies]
diff --git a/clippy_lints/src/approx_const.rs b/clippy_lints/src/approx_const.rs
index 3b4cc1134802d..56f5e903dc3e2 100644
--- a/clippy_lints/src/approx_const.rs
+++ b/clippy_lints/src/approx_const.rs
@@ -4,8 +4,7 @@ use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_ast::ast::{FloatTy, LitFloatType, LitKind};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
-use rustc_session::impl_lint_pass;
+use rustc_session::{impl_lint_pass, RustcVersion};
 use rustc_span::symbol;
 use std::f64::consts as f64;
 
diff --git a/clippy_lints/src/incompatible_msrv.rs b/clippy_lints/src/incompatible_msrv.rs
index 0ef5b803a89d6..2ad045e12686c 100644
--- a/clippy_lints/src/incompatible_msrv.rs
+++ b/clippy_lints/src/incompatible_msrv.rs
@@ -7,8 +7,7 @@ use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::{Expr, ExprKind, HirId};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::TyCtxt;
-use rustc_semver::RustcVersion;
-use rustc_session::impl_lint_pass;
+use rustc_session::{impl_lint_pass, RustcVersion};
 use rustc_span::def_id::DefId;
 use rustc_span::{ExpnKind, Span};
 
@@ -65,18 +64,18 @@ impl IncompatibleMsrv {
                 StabilityLevel::Stable {
                     since: StableSince::Version(version),
                     ..
-                } => Some(RustcVersion::new(
-                    version.major.into(),
-                    version.minor.into(),
-                    version.patch.into(),
-                )),
+                } => Some(version),
                 _ => None,
             }) {
             version
         } else if let Some(parent_def_id) = tcx.opt_parent(def_id) {
             self.get_def_id_version(tcx, parent_def_id)
         } else {
-            RustcVersion::new(1, 0, 0)
+            RustcVersion {
+                major: 1,
+                minor: 0,
+                patch: 0,
+            }
         };
         self.is_above_msrv.insert(def_id, version);
         version
diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs
index 09f6362b4ddc8..d549470ed47e1 100644
--- a/clippy_lints/src/manual_retain.rs
+++ b/clippy_lints/src/manual_retain.rs
@@ -9,8 +9,7 @@ use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::ExprKind::Assign;
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_semver::RustcVersion;
-use rustc_session::impl_lint_pass;
+use rustc_session::{impl_lint_pass, RustcVersion};
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 
diff --git a/clippy_lints/src/std_instead_of_core.rs b/clippy_lints/src/std_instead_of_core.rs
index 974e21df817a8..44283a49e84b9 100644
--- a/clippy_lints/src/std_instead_of_core.rs
+++ b/clippy_lints/src/std_instead_of_core.rs
@@ -9,7 +9,6 @@ use rustc_hir::def_id::DefId;
 use rustc_hir::{HirId, Path, PathSegment};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
-use rustc_semver::RustcVersion;
 use rustc_session::impl_lint_pass;
 use rustc_span::symbol::kw;
 use rustc_span::{sym, Span};
@@ -185,9 +184,7 @@ fn is_stable(cx: &LateContext<'_>, mut def_id: DefId, msrv: &Msrv) -> bool {
             } = stability.level
         {
             let stable = match since {
-                StableSince::Version(v) => {
-                    msrv.meets(RustcVersion::new(v.major.into(), v.minor.into(), v.patch.into()))
-                },
+                StableSince::Version(v) => msrv.meets(v),
                 StableSince::Current => msrv.current().is_none(),
                 StableSince::Err => false,
             };
diff --git a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
index df342e48d637f..20526113d69e3 100644
--- a/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
+++ b/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
@@ -9,7 +9,6 @@ use rustc_hir::intravisit::Visitor;
 use rustc_hir::{ExprKind, HirId, Item, MutTy, Mutability, Path, TyKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
-use rustc_semver::RustcVersion;
 use rustc_session::impl_lint_pass;
 use rustc_span::source_map::Spanned;
 use rustc_span::symbol::Symbol;
@@ -92,7 +91,12 @@ pub struct LintWithoutLintPass {
     registered_lints: FxHashSet<Symbol>,
 }
 
-impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE]);
+impl_lint_pass!(LintWithoutLintPass => [
+    DEFAULT_LINT,
+    LINT_WITHOUT_LINT_PASS,
+    INVALID_CLIPPY_VERSION_ATTRIBUTE,
+    MISSING_CLIPPY_VERSION_ATTRIBUTE,
+]);
 
 impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
@@ -220,7 +224,7 @@ fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'
             return;
         }
 
-        if RustcVersion::parse(value.as_str()).is_err() {
+        if rustc_attr::parse_version(value).is_none() {
             span_lint_and_help(
                 cx,
                 INVALID_CLIPPY_VERSION_ATTRIBUTE,
diff --git a/clippy_utils/Cargo.toml b/clippy_utils/Cargo.toml
index 9fefd94d339be..629ce9d04d432 100644
--- a/clippy_utils/Cargo.toml
+++ b/clippy_utils/Cargo.toml
@@ -8,7 +8,6 @@ publish = false
 clippy_config = { path = "../clippy_config" }
 arrayvec = { version = "0.7", default-features = false }
 itertools = "0.12"
-rustc-semver = "1.1"
 # FIXME(f16_f128): remove when no longer needed for parsing
 rustc_apfloat = "0.2.0"
 
diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs
index f206b2ceebcb1..92b74355607a7 100644
--- a/clippy_utils/src/qualify_min_const_fn.rs
+++ b/clippy_utils/src/qualify_min_const_fn.rs
@@ -18,7 +18,6 @@ use rustc_middle::mir::{
 use rustc_middle::traits::{BuiltinImplSource, ImplSource, ObligationCause};
 use rustc_middle::ty::adjustment::PointerCoercion;
 use rustc_middle::ty::{self, GenericArgKind, TraitRef, Ty, TyCtxt};
-use rustc_semver::RustcVersion;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 use rustc_trait_selection::traits::{ObligationCtxt, SelectionContext};
@@ -391,11 +390,7 @@ fn is_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
                     StableSince::Err => return false,
                 };
 
-                msrv.meets(RustcVersion::new(
-                    u32::from(const_stab_rust_version.major),
-                    u32::from(const_stab_rust_version.minor),
-                    u32::from(const_stab_rust_version.patch),
-                ))
+                msrv.meets(const_stab_rust_version)
             } else {
                 // Unstable const fn with the feature enabled.
                 msrv.current().is_none()
diff --git a/tests/ui/min_rust_version_invalid_attr.rs b/tests/ui/min_rust_version_invalid_attr.rs
index 2dccadd9fceec..c8409d78ed77c 100644
--- a/tests/ui/min_rust_version_invalid_attr.rs
+++ b/tests/ui/min_rust_version_invalid_attr.rs
@@ -17,7 +17,7 @@ mod multiple {
     //~^ ERROR: `clippy::msrv` is defined multiple times
 
     mod foo {
-        #![clippy::msrv = "1"]
+        #![clippy::msrv = "1.0"]
         #![clippy::msrv = "1.0.0"]
         //~^ ERROR: `clippy::msrv` is defined multiple times
     }
diff --git a/tests/ui/min_rust_version_invalid_attr.stderr b/tests/ui/min_rust_version_invalid_attr.stderr
index b4cb1b5713f90..dbc276ed89df3 100644
--- a/tests/ui/min_rust_version_invalid_attr.stderr
+++ b/tests/ui/min_rust_version_invalid_attr.stderr
@@ -31,8 +31,8 @@ LL |         #![clippy::msrv = "1.0.0"]
 note: first definition found here
   --> tests/ui/min_rust_version_invalid_attr.rs:20:9
    |
-LL |         #![clippy::msrv = "1"]
-   |         ^^^^^^^^^^^^^^^^^^^^^^
+LL |         #![clippy::msrv = "1.0"]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 4 previous errors
 

From 9afab36ca9aac3ac8a685ead1982b8ad6e782f70 Mon Sep 17 00:00:00 2001
From: Jason Newcomb <jsnewcomb@pm.me>
Date: Fri, 9 Aug 2024 09:01:41 -0400
Subject: [PATCH 19/54] Rename `get_source_text` to `get_source_range`. Add new
 `get_source_text` which returns a displayable string-like type.

---
 clippy_lints/src/matches/single_match.rs |  2 +-
 clippy_utils/src/consts.rs               |  2 +-
 clippy_utils/src/hir_utils.rs            |  4 +-
 clippy_utils/src/source.rs               | 71 ++++++++++++++++--------
 4 files changed, 53 insertions(+), 26 deletions(-)

diff --git a/clippy_lints/src/matches/single_match.rs b/clippy_lints/src/matches/single_match.rs
index 24dea03601c5f..b6930f7b9d10f 100644
--- a/clippy_lints/src/matches/single_match.rs
+++ b/clippy_lints/src/matches/single_match.rs
@@ -22,7 +22,7 @@ use super::{MATCH_BOOL, SINGLE_MATCH, SINGLE_MATCH_ELSE};
 /// span, e.g. a string literal `"//"`, but we know that this isn't the case for empty
 /// match arms.
 fn empty_arm_has_comment(cx: &LateContext<'_>, span: Span) -> bool {
-    if let Some(ff) = span.get_source_text(cx)
+    if let Some(ff) = span.get_source_range(cx)
         && let Some(text) = ff.as_str()
     {
         text.as_bytes().windows(2).any(|w| w == b"//" || w == b"/*")
diff --git a/clippy_utils/src/consts.rs b/clippy_utils/src/consts.rs
index e907e4058e5a2..760d5bc95f7fb 100644
--- a/clippy_utils/src/consts.rs
+++ b/clippy_utils/src/consts.rs
@@ -687,7 +687,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
                 if let Some(expr_span) = walk_span_to_context(expr.span, span.ctxt)
                     && let expr_lo = expr_span.lo()
                     && expr_lo >= span.lo
-                    && let Some(src) = (span.lo..expr_lo).get_source_text(&self.tcx)
+                    && let Some(src) = (span.lo..expr_lo).get_source_range(&self.tcx)
                     && let Some(src) = src.as_str()
                 {
                     use rustc_lexer::TokenKind::{BlockComment, LineComment, OpenBrace, Semi, Whitespace};
diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs
index f325e4eaf1544..b0a668329e920 100644
--- a/clippy_utils/src/hir_utils.rs
+++ b/clippy_utils/src/hir_utils.rs
@@ -1191,9 +1191,9 @@ fn eq_span_tokens(
     pred: impl Fn(TokenKind) -> bool,
 ) -> bool {
     fn f(cx: &LateContext<'_>, left: Range<BytePos>, right: Range<BytePos>, pred: impl Fn(TokenKind) -> bool) -> bool {
-        if let Some(lsrc) = left.get_source_text(cx)
+        if let Some(lsrc) = left.get_source_range(cx)
             && let Some(lsrc) = lsrc.as_str()
-            && let Some(rsrc) = right.get_source_text(cx)
+            && let Some(rsrc) = right.get_source_range(cx)
             && let Some(rsrc) = rsrc.as_str()
         {
             let pred = |t: &(_, _)| pred(t.0);
diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs
index 96dd3c55d37ae..1be1a9b28b9ea 100644
--- a/clippy_utils/src/source.rs
+++ b/clippy_utils/src/source.rs
@@ -16,7 +16,7 @@ use rustc_span::{
 };
 use std::borrow::Cow;
 use std::fmt;
-use std::ops::Range;
+use std::ops::{Deref, Range};
 
 pub trait HasSession {
     fn sess(&self) -> &Session;
@@ -94,10 +94,16 @@ impl IntoSpan for Range<BytePos> {
 }
 
 pub trait SpanRangeExt: SpanRange {
+    /// Attempts to get a handle to the source text. Returns `None` if either the span is malformed,
+    /// or the source text is not accessible.
+    fn get_source_text(self, cx: &impl HasSession) -> Option<SourceText> {
+        get_source_range(cx.sess().source_map(), self.into_range()).and_then(SourceText::new)
+    }
+
     /// Gets the source file, and range in the file, of the given span. Returns `None` if the span
     /// extends through multiple files, or is malformed.
-    fn get_source_text(self, cx: &impl HasSession) -> Option<SourceFileRange> {
-        get_source_text(cx.sess().source_map(), self.into_range())
+    fn get_source_range(self, cx: &impl HasSession) -> Option<SourceFileRange> {
+        get_source_range(cx.sess().source_map(), self.into_range())
     }
 
     /// Calls the given function with the source text referenced and returns the value. Returns
@@ -144,21 +150,49 @@ pub trait SpanRangeExt: SpanRange {
     fn trim_start(self, cx: &impl HasSession) -> Range<BytePos> {
         trim_start(cx.sess().source_map(), self.into_range())
     }
+}
+impl<T: SpanRange> SpanRangeExt for T {}
 
-    /// Writes the referenced source text to the given writer. Will return `Err` if the source text
-    /// could not be retrieved.
-    fn write_source_text_to(self, cx: &impl HasSession, dst: &mut impl fmt::Write) -> fmt::Result {
-        write_source_text_to(cx.sess().source_map(), self.into_range(), dst)
+/// Handle to a range of text in a source file.
+pub struct SourceText(SourceFileRange);
+impl SourceText {
+    /// Takes ownership of the source file handle if the source text is accessible.
+    pub fn new(text: SourceFileRange) -> Option<Self> {
+        if text.as_str().is_some() {
+            Some(Self(text))
+        } else {
+            None
+        }
+    }
+
+    /// Gets the source text.
+    pub fn as_str(&self) -> &str {
+        self.0.as_str().unwrap()
     }
 
-    /// Extracts the referenced source text as an owned string.
-    fn source_text_to_string(self, cx: &impl HasSession) -> Option<String> {
-        self.with_source_text(cx, ToOwned::to_owned)
+    /// Converts this into an owned string.
+    pub fn to_owned(&self) -> String {
+        self.as_str().to_owned()
+    }
+}
+impl Deref for SourceText {
+    type Target = str;
+    fn deref(&self) -> &Self::Target {
+        self.as_str()
+    }
+}
+impl AsRef<str> for SourceText {
+    fn as_ref(&self) -> &str {
+        self.as_str()
+    }
+}
+impl fmt::Display for SourceText {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.as_str().fmt(f)
     }
 }
-impl<T: SpanRange> SpanRangeExt for T {}
 
-fn get_source_text(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange> {
+fn get_source_range(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange> {
     let start = sm.lookup_byte_offset(sp.start);
     let end = sm.lookup_byte_offset(sp.end);
     if !Lrc::ptr_eq(&start.sf, &end.sf) || start.pos > end.pos {
@@ -169,7 +203,7 @@ fn get_source_text(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRange
 }
 
 fn with_source_text<T>(sm: &SourceMap, sp: Range<BytePos>, f: impl for<'a> FnOnce(&'a str) -> T) -> Option<T> {
-    if let Some(src) = get_source_text(sm, sp)
+    if let Some(src) = get_source_range(sm, sp)
         && let Some(src) = src.as_str()
     {
         Some(f(src))
@@ -183,7 +217,7 @@ fn with_source_text_and_range<T>(
     sp: Range<BytePos>,
     f: impl for<'a> FnOnce(&'a str, Range<usize>) -> T,
 ) -> Option<T> {
-    if let Some(src) = get_source_text(sm, sp)
+    if let Some(src) = get_source_range(sm, sp)
         && let Some(text) = &src.sf.src
     {
         Some(f(text, src.range))
@@ -198,7 +232,7 @@ fn map_range(
     sp: Range<BytePos>,
     f: impl for<'a> FnOnce(&'a str, Range<usize>) -> Option<Range<usize>>,
 ) -> Option<Range<BytePos>> {
-    if let Some(src) = get_source_text(sm, sp.clone())
+    if let Some(src) = get_source_range(sm, sp.clone())
         && let Some(text) = &src.sf.src
         && let Some(range) = f(text, src.range.clone())
     {
@@ -232,13 +266,6 @@ fn trim_start(sm: &SourceMap, sp: Range<BytePos>) -> Range<BytePos> {
     .unwrap_or(sp)
 }
 
-fn write_source_text_to(sm: &SourceMap, sp: Range<BytePos>, dst: &mut impl fmt::Write) -> fmt::Result {
-    match with_source_text(sm, sp, |src| dst.write_str(src)) {
-        Some(x) => x,
-        None => Err(fmt::Error),
-    }
-}
-
 pub struct SourceFileRange {
     pub sf: Lrc<SourceFile>,
     pub range: Range<usize>,

From dda9eb9fa8c87dc3d807189491a7730810d005a4 Mon Sep 17 00:00:00 2001
From: Ben Kimock <kimockb@gmail.com>
Date: Thu, 11 Jul 2024 19:41:48 -0400
Subject: [PATCH 20/54] Paper over the clippy ICE

---
 tests/ui/borrow_interior_mutable_const/others.rs     | 3 +--
 tests/ui/borrow_interior_mutable_const/others.stderr | 6 +++---
 2 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/tests/ui/borrow_interior_mutable_const/others.rs b/tests/ui/borrow_interior_mutable_const/others.rs
index 0ea93dd84625c..452d1b198133e 100644
--- a/tests/ui/borrow_interior_mutable_const/others.rs
+++ b/tests/ui/borrow_interior_mutable_const/others.rs
@@ -10,7 +10,7 @@ use std::sync::Once;
 
 const ATOMIC: AtomicUsize = AtomicUsize::new(5);
 const CELL: Cell<usize> = Cell::new(6);
-const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
+const ATOMIC_TUPLE: ([AtomicUsize; 1], Option<Box<AtomicUsize>>, u8) = ([ATOMIC], None, 7);
 const INTEGER: u8 = 8;
 const STRING: String = String::new();
 const STR: &str = "012345";
@@ -74,7 +74,6 @@ fn main() {
     let _ = &(&&&&ATOMIC_TUPLE).0; //~ ERROR: interior mutability
     let _ = &ATOMIC_TUPLE.0[0]; //~ ERROR: interior mutability
     let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst); //~ ERROR: interior mutability
-    let _ = &*ATOMIC_TUPLE.1;
     let _ = &ATOMIC_TUPLE.2;
     let _ = (&&&&ATOMIC_TUPLE).0;
     let _ = (&&&&ATOMIC_TUPLE).2;
diff --git a/tests/ui/borrow_interior_mutable_const/others.stderr b/tests/ui/borrow_interior_mutable_const/others.stderr
index 33c774667f942..9a9028c864986 100644
--- a/tests/ui/borrow_interior_mutable_const/others.stderr
+++ b/tests/ui/borrow_interior_mutable_const/others.stderr
@@ -92,7 +92,7 @@ LL |     let _ = ATOMIC_TUPLE.0[0].load(Ordering::SeqCst);
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/others.rs:82:13
+  --> tests/ui/borrow_interior_mutable_const/others.rs:81:13
    |
 LL |     let _ = ATOMIC_TUPLE.0[0];
    |             ^^^^^^^^^^^^
@@ -100,7 +100,7 @@ LL |     let _ = ATOMIC_TUPLE.0[0];
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/others.rs:87:5
+  --> tests/ui/borrow_interior_mutable_const/others.rs:86:5
    |
 LL |     CELL.set(2);
    |     ^^^^
@@ -108,7 +108,7 @@ LL |     CELL.set(2);
    = help: assign this const to a local or static variable, and use the variable here
 
 error: a `const` item with interior mutability should not be borrowed
-  --> tests/ui/borrow_interior_mutable_const/others.rs:88:16
+  --> tests/ui/borrow_interior_mutable_const/others.rs:87:16
    |
 LL |     assert_eq!(CELL.get(), 6);
    |                ^^^^

From f9509d3574a998bbfb664c5768d183f7d2af39a9 Mon Sep 17 00:00:00 2001
From: Jason Newcomb <jsnewcomb@pm.me>
Date: Thu, 4 Jul 2024 19:00:17 -0400
Subject: [PATCH 21/54] Refactor `absolute_paths`: * Check the path length
 first * Use `is_from_proc_macro` * Use symbols instead of strings when
 checking crate names

---
 clippy_lints/src/absolute_paths.rs            |  95 +++++-----
 clippy_utils/src/check_proc_macro.rs          |  12 +-
 .../absolute_paths.allow_crates.stderr        |  45 +++--
 .../absolute_paths.allow_long.stderr          |  14 ++
 .../absolute_paths.default.stderr             |  80 ++++++++
 .../absolute_paths.disallow_crates.stderr     |  71 -------
 .../absolute_paths.no_short.stderr            |  98 ++++++++++
 .../ui-toml/absolute_paths/absolute_paths.rs  | 178 ++++++++++--------
 .../absolute_paths_2015.default.stderr        |  14 ++
 .../absolute_paths/absolute_paths_2015.rs     |  16 ++
 .../absolute_paths/allow_crates/clippy.toml   |   3 +-
 .../absolute_paths/allow_long/clippy.toml     |   1 +
 .../absolute_paths/auxiliary/helper.rs        |  11 --
 .../absolute_paths/default/clippy.toml        |   0
 .../disallow_crates/clippy.toml               |   1 -
 .../absolute_paths/no_short/clippy.toml       |   1 +
 16 files changed, 415 insertions(+), 225 deletions(-)
 create mode 100644 tests/ui-toml/absolute_paths/absolute_paths.allow_long.stderr
 create mode 100644 tests/ui-toml/absolute_paths/absolute_paths.default.stderr
 delete mode 100644 tests/ui-toml/absolute_paths/absolute_paths.disallow_crates.stderr
 create mode 100644 tests/ui-toml/absolute_paths/absolute_paths.no_short.stderr
 create mode 100644 tests/ui-toml/absolute_paths/absolute_paths_2015.default.stderr
 create mode 100644 tests/ui-toml/absolute_paths/absolute_paths_2015.rs
 create mode 100644 tests/ui-toml/absolute_paths/allow_long/clippy.toml
 delete mode 100644 tests/ui-toml/absolute_paths/auxiliary/helper.rs
 create mode 100644 tests/ui-toml/absolute_paths/default/clippy.toml
 delete mode 100644 tests/ui-toml/absolute_paths/disallow_crates/clippy.toml
 create mode 100644 tests/ui-toml/absolute_paths/no_short/clippy.toml

diff --git a/clippy_lints/src/absolute_paths.rs b/clippy_lints/src/absolute_paths.rs
index c0a9d888e0b0b..c72b3f1318c86 100644
--- a/clippy_lints/src/absolute_paths.rs
+++ b/clippy_lints/src/absolute_paths.rs
@@ -1,6 +1,6 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::is_from_proc_macro;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{DefId, CRATE_DEF_INDEX};
@@ -8,6 +8,7 @@ use rustc_hir::{HirId, ItemKind, Node, Path};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
 use rustc_span::symbol::kw;
+use rustc_span::Symbol;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -24,6 +25,13 @@ declare_clippy_lint! {
     /// Note: One exception to this is code from macro expansion - this does not lint such cases, as
     /// using absolute paths is the proper way of referencing items in one.
     ///
+    /// ### Known issues
+    ///
+    /// There are currently a few cases which are not caught by this lint:
+    /// * Macro calls. e.g. `path::to::macro!()`
+    /// * Derive macros. e.g. `#[derive(path::to::macro)]`
+    /// * Attribute macros. e.g. `#[path::to::macro]`
+    ///
     /// ### Example
     /// ```no_run
     /// let x = std::f64::consts::PI;
@@ -48,63 +56,66 @@ impl_lint_pass!(AbsolutePaths => [ABSOLUTE_PATHS]);
 
 pub struct AbsolutePaths {
     pub absolute_paths_max_segments: u64,
-    pub absolute_paths_allowed_crates: &'static FxHashSet<String>,
+    pub absolute_paths_allowed_crates: FxHashSet<Symbol>,
 }
 
 impl AbsolutePaths {
     pub fn new(conf: &'static Conf) -> Self {
         Self {
             absolute_paths_max_segments: conf.absolute_paths_max_segments,
-            absolute_paths_allowed_crates: &conf.absolute_paths_allowed_crates,
+            absolute_paths_allowed_crates: conf
+                .absolute_paths_allowed_crates
+                .iter()
+                .map(|x| Symbol::intern(x))
+                .collect(),
         }
     }
 }
 
-impl LateLintPass<'_> for AbsolutePaths {
+impl<'tcx> LateLintPass<'tcx> for AbsolutePaths {
     // We should only lint `QPath::Resolved`s, but since `Path` is only used in `Resolved` and `UsePath`
     // we don't need to use a visitor or anything as we can just check if the `Node` for `hir_id` isn't
     // a `Use`
-    #[expect(clippy::cast_possible_truncation)]
-    fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, hir_id: HirId) {
-        let Self {
-            absolute_paths_max_segments,
-            absolute_paths_allowed_crates,
-        } = self;
-
-        if !path.span.from_expansion()
-            && let node = cx.tcx.hir_node(hir_id)
-            && !matches!(node, Node::Item(item) if matches!(item.kind, ItemKind::Use(_, _)))
-            && let [first, rest @ ..] = path.segments
-            // Handle `::std`
-            && let (segment, len) = if first.ident.name == kw::PathRoot {
-                // Indexing is fine as `PathRoot` must be followed by another segment. `len() - 1`
-                // is fine here for the same reason
-                (&rest[0], path.segments.len() - 1)
-            } else {
-                (first, path.segments.len())
-            }
-            && len > *absolute_paths_max_segments as usize
-            && let Some(segment_snippet) = snippet_opt(cx, segment.ident.span)
-            && segment_snippet == segment.ident.as_str()
-        {
-            let is_abs_external =
-                matches!(segment.res, Res::Def(DefKind::Mod, DefId { index, .. }) if index == CRATE_DEF_INDEX);
-            let is_abs_crate = segment.ident.name == kw::Crate;
-
-            if is_abs_external && absolute_paths_allowed_crates.contains(segment.ident.name.as_str())
-                || is_abs_crate && absolute_paths_allowed_crates.contains("crate")
+    fn check_path(&mut self, cx: &LateContext<'tcx>, path: &Path<'tcx>, hir_id: HirId) {
+        let segments = match path.segments {
+            [] | [_] => return,
+            // Don't count enum variants and trait items as part of the length.
+            [rest @ .., _]
+                if let [.., s] = rest
+                    && matches!(s.res, Res::Def(DefKind::Enum | DefKind::Trait | DefKind::TraitAlias, _)) =>
+            {
+                rest
+            },
+            path => path,
+        };
+        if let [s1, s2, ..] = segments
+            && let has_root = s1.ident.name == kw::PathRoot
+            && let first = if has_root { s2 } else { s1 }
+            && let len = segments.len() - usize::from(has_root)
+            && len as u64 > self.absolute_paths_max_segments
+            && let crate_name = if let Res::Def(DefKind::Mod, DefId { index, .. }) = first.res
+                && index == CRATE_DEF_INDEX
             {
+                // `other_crate::foo` or `::other_crate::foo`
+                first.ident.name
+            } else if first.ident.name == kw::Crate || has_root {
+                // `::foo` or `crate::foo`
+                kw::Crate
+            } else {
                 return;
             }
-
-            if is_abs_external || is_abs_crate {
-                span_lint(
-                    cx,
-                    ABSOLUTE_PATHS,
-                    path.span,
-                    "consider bringing this path into scope with the `use` keyword",
-                );
-            }
+            && !path.span.from_expansion()
+            && let node = cx.tcx.hir_node(hir_id)
+            && !matches!(node, Node::Item(item) if matches!(item.kind, ItemKind::Use(..)))
+            && !self.absolute_paths_allowed_crates.contains(&crate_name)
+            && !is_from_proc_macro(cx, path)
+        {
+            span_lint(
+                cx,
+                ABSOLUTE_PATHS,
+                path.span,
+                "consider bringing this path into scope with the `use` keyword",
+            );
         }
     }
 }
diff --git a/clippy_utils/src/check_proc_macro.rs b/clippy_utils/src/check_proc_macro.rs
index 7f2234b310b42..2bd6837d973bb 100644
--- a/clippy_utils/src/check_proc_macro.rs
+++ b/clippy_utils/src/check_proc_macro.rs
@@ -123,16 +123,14 @@ fn qpath_search_pat(path: &QPath<'_>) -> (Pat, Pat) {
 
 fn path_search_pat(path: &Path<'_>) -> (Pat, Pat) {
     let (head, tail) = match path.segments {
-        [head, .., tail] => (head, tail),
-        [p] => (p, p),
         [] => return (Pat::Str(""), Pat::Str("")),
+        [p] => (Pat::Sym(p.ident.name), p),
+        // QPath::Resolved can have a path that looks like `<Foo as Bar>::baz` where
+        // the path (`Bar::baz`) has it's span covering the whole QPath.
+        [.., tail] => (Pat::Str(""), tail),
     };
     (
-        if head.ident.name == kw::PathRoot {
-            Pat::Str("::")
-        } else {
-            Pat::Sym(head.ident.name)
-        },
+        head,
         if tail.args.is_some() {
             Pat::Str(">")
         } else {
diff --git a/tests/ui-toml/absolute_paths/absolute_paths.allow_crates.stderr b/tests/ui-toml/absolute_paths/absolute_paths.allow_crates.stderr
index 1cc1034cd89b3..d24b0cd616294 100644
--- a/tests/ui-toml/absolute_paths/absolute_paths.allow_crates.stderr
+++ b/tests/ui-toml/absolute_paths/absolute_paths.allow_crates.stderr
@@ -1,29 +1,44 @@
 error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:40:5
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:14:13
    |
-LL |     std::f32::MAX;
-   |     ^^^^^^^^^^^^^
+LL |     let _ = std::path::is_separator(' ');
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = note: `-D clippy::absolute-paths` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::absolute_paths)]`
+note: the lint level is defined here
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:7:9
+   |
+LL | #![deny(clippy::absolute_paths)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:20:13
+   |
+LL |     let _ = ::std::path::MAIN_SEPARATOR;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:25:13
+   |
+LL |     let _ = std::collections::hash_map::HashMap::<i32, i32>::new();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:41:5
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:28:31
    |
-LL |     core::f32::MAX;
-   |     ^^^^^^^^^^^^^^
+LL |     let _: &std::path::Path = std::path::Path::new("");
+   |                               ^^^^^^^^^^^^^^^
 
 error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:42:5
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:28:13
    |
-LL |     ::core::f32::MAX;
-   |     ^^^^^^^^^^^^^^^^
+LL |     let _: &std::path::Path = std::path::Path::new("");
+   |             ^^^^^^^^^^^^^^^
 
 error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:58:5
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:43:13
    |
-LL |     ::std::f32::MAX;
-   |     ^^^^^^^^^^^^^^^
+LL |     let _ = std::option::Option::None::<i32>;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 4 previous errors
+error: aborting due to 6 previous errors
 
diff --git a/tests/ui-toml/absolute_paths/absolute_paths.allow_long.stderr b/tests/ui-toml/absolute_paths/absolute_paths.allow_long.stderr
new file mode 100644
index 0000000000000..0cc6566af3a96
--- /dev/null
+++ b/tests/ui-toml/absolute_paths/absolute_paths.allow_long.stderr
@@ -0,0 +1,14 @@
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:25:13
+   |
+LL |     let _ = std::collections::hash_map::HashMap::<i32, i32>::new();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:7:9
+   |
+LL | #![deny(clippy::absolute_paths)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui-toml/absolute_paths/absolute_paths.default.stderr b/tests/ui-toml/absolute_paths/absolute_paths.default.stderr
new file mode 100644
index 0000000000000..53aa9030e0dd5
--- /dev/null
+++ b/tests/ui-toml/absolute_paths/absolute_paths.default.stderr
@@ -0,0 +1,80 @@
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:14:13
+   |
+LL |     let _ = std::path::is_separator(' ');
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:7:9
+   |
+LL | #![deny(clippy::absolute_paths)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:20:13
+   |
+LL |     let _ = ::std::path::MAIN_SEPARATOR;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:25:13
+   |
+LL |     let _ = std::collections::hash_map::HashMap::<i32, i32>::new();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:28:31
+   |
+LL |     let _: &std::path::Path = std::path::Path::new("");
+   |                               ^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:28:13
+   |
+LL |     let _: &std::path::Path = std::path::Path::new("");
+   |             ^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:37:13
+   |
+LL |     let _ = ::core::clone::Clone::clone(&0i32);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:40:13
+   |
+LL |     let _ = <i32 as core::clone::Clone>::clone(&0i32);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:43:13
+   |
+LL |     let _ = std::option::Option::None::<i32>;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:65:17
+   |
+LL |         impl<T: core::cmp::Eq> core::fmt::Display for X<T>
+   |                 ^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:70:18
+   |
+LL |         where T: core::clone::Clone
+   |                  ^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:65:32
+   |
+LL |         impl<T: core::cmp::Eq> core::fmt::Display for X<T>
+   |                                ^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:116:5
+   |
+LL |     crate::m1::S
+   |     ^^^^^^^^^^^^
+
+error: aborting due to 12 previous errors
+
diff --git a/tests/ui-toml/absolute_paths/absolute_paths.disallow_crates.stderr b/tests/ui-toml/absolute_paths/absolute_paths.disallow_crates.stderr
deleted file mode 100644
index f342ebf6632e8..0000000000000
--- a/tests/ui-toml/absolute_paths/absolute_paths.disallow_crates.stderr
+++ /dev/null
@@ -1,71 +0,0 @@
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:40:5
-   |
-LL |     std::f32::MAX;
-   |     ^^^^^^^^^^^^^
-   |
-   = note: `-D clippy::absolute-paths` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::absolute_paths)]`
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:41:5
-   |
-LL |     core::f32::MAX;
-   |     ^^^^^^^^^^^^^^
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:42:5
-   |
-LL |     ::core::f32::MAX;
-   |     ^^^^^^^^^^^^^^^^
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:43:5
-   |
-LL |     crate::a::b::c::C;
-   |     ^^^^^^^^^^^^^^^^^
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:44:5
-   |
-LL |     crate::a::b::c::d::e::f::F;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:45:5
-   |
-LL |     crate::a::A;
-   |     ^^^^^^^^^^^
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:46:5
-   |
-LL |     crate::a::b::B;
-   |     ^^^^^^^^^^^^^^
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:47:5
-   |
-LL |     crate::a::b::c::C::ZERO;
-   |     ^^^^^^^^^^^^^^^^^
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:48:5
-   |
-LL |     helper::b::c::d::e::f();
-   |     ^^^^^^^^^^^^^^^^^^^^^
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:49:5
-   |
-LL |     ::helper::b::c::d::e::f();
-   |     ^^^^^^^^^^^^^^^^^^^^^^^
-
-error: consider bringing this path into scope with the `use` keyword
-  --> tests/ui-toml/absolute_paths/absolute_paths.rs:58:5
-   |
-LL |     ::std::f32::MAX;
-   |     ^^^^^^^^^^^^^^^
-
-error: aborting due to 11 previous errors
-
diff --git a/tests/ui-toml/absolute_paths/absolute_paths.no_short.stderr b/tests/ui-toml/absolute_paths/absolute_paths.no_short.stderr
new file mode 100644
index 0000000000000..70d71f6c4ea16
--- /dev/null
+++ b/tests/ui-toml/absolute_paths/absolute_paths.no_short.stderr
@@ -0,0 +1,98 @@
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:14:13
+   |
+LL |     let _ = std::path::is_separator(' ');
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:7:9
+   |
+LL | #![deny(clippy::absolute_paths)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:20:13
+   |
+LL |     let _ = ::std::path::MAIN_SEPARATOR;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:25:13
+   |
+LL |     let _ = std::collections::hash_map::HashMap::<i32, i32>::new();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:28:31
+   |
+LL |     let _: &std::path::Path = std::path::Path::new("");
+   |                               ^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:28:13
+   |
+LL |     let _: &std::path::Path = std::path::Path::new("");
+   |             ^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:37:13
+   |
+LL |     let _ = ::core::clone::Clone::clone(&0i32);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:40:13
+   |
+LL |     let _ = <i32 as core::clone::Clone>::clone(&0i32);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:43:13
+   |
+LL |     let _ = std::option::Option::None::<i32>;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:65:17
+   |
+LL |         impl<T: core::cmp::Eq> core::fmt::Display for X<T>
+   |                 ^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:70:18
+   |
+LL |         where T: core::clone::Clone
+   |                  ^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:65:32
+   |
+LL |         impl<T: core::cmp::Eq> core::fmt::Display for X<T>
+   |                                ^^^^^^^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:113:14
+   |
+LL | pub const _: crate::S = {
+   |              ^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:114:9
+   |
+LL |     let crate::S = m1::S;
+   |         ^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:116:5
+   |
+LL |     crate::m1::S
+   |     ^^^^^^^^^^^^
+
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths.rs:122:14
+   |
+LL |     let _ = <crate::S as Clone>::clone(&m1::S);
+   |              ^^^^^^^^
+
+error: aborting due to 15 previous errors
+
diff --git a/tests/ui-toml/absolute_paths/absolute_paths.rs b/tests/ui-toml/absolute_paths/absolute_paths.rs
index a828701bcee9f..c024f2f513ced 100644
--- a/tests/ui-toml/absolute_paths/absolute_paths.rs
+++ b/tests/ui-toml/absolute_paths/absolute_paths.rs
@@ -1,97 +1,123 @@
 //@aux-build:../../ui/auxiliary/proc_macros.rs
-//@aux-build:helper.rs
-//@revisions: allow_crates disallow_crates
+//@revisions: default allow_crates allow_long no_short
+//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/default
 //@[allow_crates] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/allow_crates
-//@[disallow_crates] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/disallow_crates
-#![allow(clippy::no_effect, clippy::legacy_numeric_constants, unused)]
-#![warn(clippy::absolute_paths)]
-#![feature(decl_macro)]
+//@[allow_long] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/allow_long
+//@[no_short] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/no_short
+#![deny(clippy::absolute_paths)]
 
-extern crate helper;
-#[macro_use]
 extern crate proc_macros;
+use proc_macros::{external, inline_macros, with_span};
 
-pub mod a {
-    pub mod b {
-        pub mod c {
-            pub struct C;
+#[inline_macros]
+fn main() {
+    let _ = std::path::is_separator(' ');
+    //~[default]^ absolute_paths
+    //~[allow_crates]| absolute_paths
+    //~[no_short]| absolute_paths
 
-            impl C {
-                pub const ZERO: u32 = 0;
-            }
+    // Make sure this is treated as having three path segments, not four.
+    let _ = ::std::path::MAIN_SEPARATOR;
+    //~[default]^ absolute_paths
+    //~[allow_crates]| absolute_paths
+    //~[no_short]| absolute_paths
 
-            pub mod d {
-                pub mod e {
-                    pub mod f {
-                        pub struct F;
-                    }
-                }
-            }
-        }
+    let _ = std::collections::hash_map::HashMap::<i32, i32>::new(); //~ absolute_paths
 
-        pub struct B;
-    }
+    // Note `std::path::Path::new` is treated as having three parts
+    let _: &std::path::Path = std::path::Path::new("");
+    //~[default]^ absolute_paths
+    //~[default]| absolute_paths
+    //~[allow_crates]| absolute_paths
+    //~[allow_crates]| absolute_paths
+    //~[no_short]| absolute_paths
+    //~[no_short]| absolute_paths
 
-    pub struct A;
-}
+    // Treated as having three parts.
+    let _ = ::core::clone::Clone::clone(&0i32);
+    //~[default]^ absolute_paths
+    //~[no_short]| absolute_paths
+    let _ = <i32 as core::clone::Clone>::clone(&0i32);
+    //~[default]^ absolute_paths
+    //~[no_short]| absolute_paths
+    let _ = std::option::Option::None::<i32>;
+    //~[default]^ absolute_paths
+    //~[allow_crates]| absolute_paths
+    //~[no_short]| absolute_paths
 
-fn main() {
-    f32::max(1.0, 2.0);
-    std::f32::MAX;
-    core::f32::MAX;
-    ::core::f32::MAX;
-    crate::a::b::c::C;
-    crate::a::b::c::d::e::f::F;
-    crate::a::A;
-    crate::a::b::B;
-    crate::a::b::c::C::ZERO;
-    helper::b::c::d::e::f();
-    ::helper::b::c::d::e::f();
-    fn b() -> a::b::B {
-        todo!()
+    {
+        // FIXME: macro calls should be checked.
+        let x = 1i32;
+        let _ = core::ptr::addr_of!(x);
     }
-    std::println!("a");
-    let x = 1;
-    std::ptr::addr_of!(x);
-    // Test we handle max segments with `PathRoot` properly; this has 4 segments but we should say it
-    // has 3
-    ::std::f32::MAX;
-    // Do not lint due to the above
-    ::helper::a();
-    // Do not lint
-    helper::a();
-    use crate::a::b::c::C;
-    use a::b;
-    use std::f32::MAX;
-    a::b::c::d::e::f::F;
-    b::c::C;
-    fn a() -> a::A {
-        todo!()
-    }
-    use a::b::c;
 
-    fn c() -> c::C {
-        todo!()
+    {
+        // FIXME: derive macro paths should be checked.
+        #[derive(core::clone::Clone)]
+        struct S;
     }
-    fn d() -> Result<(), ()> {
-        todo!()
+
+    {
+        use core::fmt;
+        use core::marker::PhantomData;
+
+        struct X<T>(PhantomData<T>);
+        impl<T: core::cmp::Eq> core::fmt::Display for X<T>
+        //~[default]^ absolute_paths
+        //~[default]| absolute_paths
+        //~[no_short]| absolute_paths
+        //~[no_short]| absolute_paths
+        where T: core::clone::Clone
+        //~[no_short]^ absolute_paths
+        //~[default]| absolute_paths
+        {
+            fn fmt(&self, _: &mut fmt::Formatter) -> fmt::Result {
+                Ok(())
+            }
+        }
     }
-    external! {
-        crate::a::b::c::C::ZERO;
+
+    {
+        mod m1 {
+            pub(crate) mod m2 {
+                pub(crate) const FOO: i32 = 0;
+            }
+        }
+        let _ = m1::m2::FOO;
     }
-    // For some reason, `path.span.from_expansion()` takes care of this for us
+
     with_span! {
         span
-        crate::a::b::c::C::ZERO;
+        let _ = std::path::is_separator(' ');
     }
-    macro_rules! local_crate {
-        () => {
-            crate::a::b::c::C::ZERO;
-        };
+
+    external! {
+        let _ = std::path::is_separator(' ');
     }
-    macro local_crate_2_0() {
-        crate::a::b::c::C::ZERO;
+
+    inline! {
+        let _ = std::path::is_separator(' ');
     }
-    local_crate!();
-    local_crate_2_0!();
+}
+
+pub use core::cmp::Ordering;
+pub use std::fs::File;
+
+#[derive(Clone)]
+pub struct S;
+mod m1 {
+    pub use crate::S;
+}
+
+//~[no_short]v absolute_paths
+pub const _: crate::S = {
+    let crate::S = m1::S; //~[no_short] absolute_paths
+
+    crate::m1::S
+    //~[default]^ absolute_paths
+    //~[no_short]| absolute_paths
+};
+
+pub fn f() {
+    let _ = <crate::S as Clone>::clone(&m1::S); //~[no_short] absolute_paths
 }
diff --git a/tests/ui-toml/absolute_paths/absolute_paths_2015.default.stderr b/tests/ui-toml/absolute_paths/absolute_paths_2015.default.stderr
new file mode 100644
index 0000000000000..6fc495f018080
--- /dev/null
+++ b/tests/ui-toml/absolute_paths/absolute_paths_2015.default.stderr
@@ -0,0 +1,14 @@
+error: consider bringing this path into scope with the `use` keyword
+  --> tests/ui-toml/absolute_paths/absolute_paths_2015.rs:15:13
+   |
+LL |     let _ = ::m1::m2::X;
+   |             ^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> tests/ui-toml/absolute_paths/absolute_paths_2015.rs:6:9
+   |
+LL | #![deny(clippy::absolute_paths)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui-toml/absolute_paths/absolute_paths_2015.rs b/tests/ui-toml/absolute_paths/absolute_paths_2015.rs
new file mode 100644
index 0000000000000..033c47809191c
--- /dev/null
+++ b/tests/ui-toml/absolute_paths/absolute_paths_2015.rs
@@ -0,0 +1,16 @@
+//@revisions: default allow_crates
+//@[default]rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/default
+//@[allow_crates]rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/absolute_paths/allow_crates
+//@edition:2015
+
+#![deny(clippy::absolute_paths)]
+
+mod m1 {
+    pub mod m2 {
+        pub struct X;
+    }
+}
+
+fn main() {
+    let _ = ::m1::m2::X; //~[default] absolute_paths
+}
diff --git a/tests/ui-toml/absolute_paths/allow_crates/clippy.toml b/tests/ui-toml/absolute_paths/allow_crates/clippy.toml
index 59a621e9d1dba..9da49abcd31f6 100644
--- a/tests/ui-toml/absolute_paths/allow_crates/clippy.toml
+++ b/tests/ui-toml/absolute_paths/allow_crates/clippy.toml
@@ -1,2 +1 @@
-absolute-paths-max-segments = 2
-absolute-paths-allowed-crates = ["crate", "helper"]
+absolute-paths-allowed-crates = ["core", "crate"]
diff --git a/tests/ui-toml/absolute_paths/allow_long/clippy.toml b/tests/ui-toml/absolute_paths/allow_long/clippy.toml
new file mode 100644
index 0000000000000..5992fd1ed8219
--- /dev/null
+++ b/tests/ui-toml/absolute_paths/allow_long/clippy.toml
@@ -0,0 +1 @@
+absolute-paths-max-segments = 3
diff --git a/tests/ui-toml/absolute_paths/auxiliary/helper.rs b/tests/ui-toml/absolute_paths/auxiliary/helper.rs
deleted file mode 100644
index 8e2678f5fe697..0000000000000
--- a/tests/ui-toml/absolute_paths/auxiliary/helper.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-pub fn a() {}
-
-pub mod b {
-    pub mod c {
-        pub mod d {
-            pub mod e {
-                pub fn f() {}
-            }
-        }
-    }
-}
diff --git a/tests/ui-toml/absolute_paths/default/clippy.toml b/tests/ui-toml/absolute_paths/default/clippy.toml
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/tests/ui-toml/absolute_paths/disallow_crates/clippy.toml b/tests/ui-toml/absolute_paths/disallow_crates/clippy.toml
deleted file mode 100644
index d44d648c64118..0000000000000
--- a/tests/ui-toml/absolute_paths/disallow_crates/clippy.toml
+++ /dev/null
@@ -1 +0,0 @@
-absolute-paths-max-segments = 2
diff --git a/tests/ui-toml/absolute_paths/no_short/clippy.toml b/tests/ui-toml/absolute_paths/no_short/clippy.toml
new file mode 100644
index 0000000000000..357524420c5a6
--- /dev/null
+++ b/tests/ui-toml/absolute_paths/no_short/clippy.toml
@@ -0,0 +1 @@
+absolute-paths-max-segments = 0

From 917ad034cc89ecdde85771238d0d8f89813290fb Mon Sep 17 00:00:00 2001
From: Nadrieril <nadrieril+git@gmail.com>
Date: Wed, 20 Mar 2024 22:51:29 +0100
Subject: [PATCH 22/54] Fixes in various places

---
 clippy_utils/src/lib.rs           | 1 +
 tests/ui/single_match_else.fixed  | 2 +-
 tests/ui/single_match_else.rs     | 2 +-
 tests/ui/single_match_else.stderr | 4 ++--
 4 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/clippy_utils/src/lib.rs b/clippy_utils/src/lib.rs
index af74e4b67c162..28755ae0710ca 100644
--- a/clippy_utils/src/lib.rs
+++ b/clippy_utils/src/lib.rs
@@ -2931,6 +2931,7 @@ pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> ExprU
             moved_before_use,
             same_ctxt,
         },
+        #[allow(unreachable_patterns)]
         Some(ControlFlow::Break(_)) => unreachable!("type of node is ControlFlow<!>"),
         None => ExprUseCtxt {
             node: Node::Crate(cx.tcx.hir().root_module()),
diff --git a/tests/ui/single_match_else.fixed b/tests/ui/single_match_else.fixed
index e840adf0fa34b..163be16ad8be7 100644
--- a/tests/ui/single_match_else.fixed
+++ b/tests/ui/single_match_else.fixed
@@ -89,7 +89,7 @@ fn main() {
 
     // lint here
     use std::convert::Infallible;
-    if let Ok(a) = Result::<i32, Infallible>::Ok(1) { println!("${:?}", a) } else {
+    if let Ok(a) = Result::<i32, &Infallible>::Ok(1) { println!("${:?}", a) } else {
         println!("else block");
         return;
     }
diff --git a/tests/ui/single_match_else.rs b/tests/ui/single_match_else.rs
index 430c4da20f12a..3f1fd2b31832f 100644
--- a/tests/ui/single_match_else.rs
+++ b/tests/ui/single_match_else.rs
@@ -98,7 +98,7 @@ fn main() {
 
     // lint here
     use std::convert::Infallible;
-    match Result::<i32, Infallible>::Ok(1) {
+    match Result::<i32, &Infallible>::Ok(1) {
         Ok(a) => println!("${:?}", a),
         Err(_) => {
             println!("else block");
diff --git a/tests/ui/single_match_else.stderr b/tests/ui/single_match_else.stderr
index f8f88379d6d12..61c348260d05e 100644
--- a/tests/ui/single_match_else.stderr
+++ b/tests/ui/single_match_else.stderr
@@ -64,7 +64,7 @@ LL +     }
 error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
   --> tests/ui/single_match_else.rs:101:5
    |
-LL | /     match Result::<i32, Infallible>::Ok(1) {
+LL | /     match Result::<i32, &Infallible>::Ok(1) {
 LL | |         Ok(a) => println!("${:?}", a),
 LL | |         Err(_) => {
 LL | |             println!("else block");
@@ -75,7 +75,7 @@ LL | |     }
    |
 help: try
    |
-LL ~     if let Ok(a) = Result::<i32, Infallible>::Ok(1) { println!("${:?}", a) } else {
+LL ~     if let Ok(a) = Result::<i32, &Infallible>::Ok(1) { println!("${:?}", a) } else {
 LL +         println!("else block");
 LL +         return;
 LL +     }

From 4231e93bf7933aa1f2d923f9388090f63e649c2f Mon Sep 17 00:00:00 2001
From: Alex Macleod <alex@macleod.io>
Date: Sat, 10 Aug 2024 10:50:47 +0000
Subject: [PATCH 23/54] Remove `find_format_arg_expr` AST fallback

---
 clippy_lints/src/format.rs      |  2 +-
 clippy_lints/src/format_args.rs | 29 ++++++-----------------------
 clippy_lints/src/format_impl.rs |  2 +-
 clippy_utils/src/macros.rs      |  9 ++-------
 4 files changed, 10 insertions(+), 32 deletions(-)

diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs
index 0b248f784b762..c14bd38a1f21c 100644
--- a/clippy_lints/src/format.rs
+++ b/clippy_lints/src/format.rs
@@ -73,7 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
                     span_useless_format(cx, call_site, sugg, applicability);
                 },
                 ([arg], [piece]) => {
-                    if let Ok(value) = find_format_arg_expr(expr, arg)
+                    if let Some(value) = find_format_arg_expr(expr, arg)
                         && let FormatArgsPiece::Placeholder(placeholder) = piece
                         && placeholder.format_trait == FormatTrait::Display
                         && placeholder.format_options == FormatOptions::default()
diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs
index a31d5cb6ec77f..823992767e508 100644
--- a/clippy_lints/src/format_args.rs
+++ b/clippy_lints/src/format_args.rs
@@ -224,13 +224,11 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
             if let FormatArgsPiece::Placeholder(placeholder) = piece
                 && let Ok(index) = placeholder.argument.index
                 && let Some(arg) = self.format_args.arguments.all_args().get(index)
+                && let Some(arg_expr) = find_format_arg_expr(self.expr, arg)
             {
-                let arg_expr = find_format_arg_expr(self.expr, arg);
-
                 self.check_unused_format_specifier(placeholder, arg_expr);
 
-                if let Ok(arg_expr) = arg_expr
-                    && placeholder.format_trait == FormatTrait::Display
+                if placeholder.format_trait == FormatTrait::Display
                     && placeholder.format_options == FormatOptions::default()
                     && !self.is_aliased(index)
                 {
@@ -242,28 +240,13 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
         }
     }
 
-    fn check_unused_format_specifier(
-        &self,
-        placeholder: &FormatPlaceholder,
-        arg_expr: Result<&Expr<'_>, &rustc_ast::Expr>,
-    ) {
-        let ty_or_ast_expr = arg_expr.map(|expr| self.cx.typeck_results().expr_ty(expr).peel_refs());
-
-        let is_format_args = match ty_or_ast_expr {
-            Ok(ty) => is_type_lang_item(self.cx, ty, LangItem::FormatArguments),
-            Err(expr) => matches!(expr.peel_parens_and_refs().kind, rustc_ast::ExprKind::FormatArgs(_)),
-        };
-
+    fn check_unused_format_specifier(&self, placeholder: &FormatPlaceholder, arg: &Expr<'_>) {
         let options = &placeholder.format_options;
 
-        let arg_span = match arg_expr {
-            Ok(expr) => expr.span,
-            Err(expr) => expr.span,
-        };
-
         if let Some(placeholder_span) = placeholder.span
-            && is_format_args
             && *options != FormatOptions::default()
+            && let ty = self.cx.typeck_results().expr_ty(arg).peel_refs()
+            && is_type_lang_item(self.cx, ty, LangItem::FormatArguments)
         {
             span_lint_and_then(
                 self.cx,
@@ -274,7 +257,7 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
                     let mut suggest_format = |spec| {
                         let message = format!("for the {spec} to apply consider using `format!()`");
 
-                        if let Some(mac_call) = matching_root_macro_call(self.cx, arg_span, sym::format_args_macro) {
+                        if let Some(mac_call) = matching_root_macro_call(self.cx, arg.span, sym::format_args_macro) {
                             diag.span_suggestion(
                                 self.cx.sess().source_map().span_until_char(mac_call.span, '!'),
                                 message,
diff --git a/clippy_lints/src/format_impl.rs b/clippy_lints/src/format_impl.rs
index e6f27cb82d17a..7cd12ac7db852 100644
--- a/clippy_lints/src/format_impl.rs
+++ b/clippy_lints/src/format_impl.rs
@@ -196,7 +196,7 @@ impl<'a, 'tcx> FormatImplExpr<'a, 'tcx> {
                     && trait_name == self.format_trait_impl.name
                     && let Ok(index) = placeholder.argument.index
                     && let Some(arg) = format_args.arguments.all_args().get(index)
-                    && let Ok(arg_expr) = find_format_arg_expr(self.expr, arg)
+                    && let Some(arg_expr) = find_format_arg_expr(self.expr, arg)
                 {
                     self.check_format_arg_self(arg_expr);
                 }
diff --git a/clippy_utils/src/macros.rs b/clippy_utils/src/macros.rs
index 455239cc37f35..4a635535a4e0c 100644
--- a/clippy_utils/src/macros.rs
+++ b/clippy_utils/src/macros.rs
@@ -426,12 +426,8 @@ impl FormatArgsStorage {
     }
 }
 
-/// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if
-/// it cannot be found it will return the [`rustc_ast::Expr`].
-pub fn find_format_arg_expr<'hir, 'ast>(
-    start: &'hir Expr<'hir>,
-    target: &'ast FormatArgument,
-) -> Result<&'hir Expr<'hir>, &'ast rustc_ast::Expr> {
+/// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value
+pub fn find_format_arg_expr<'hir>(start: &'hir Expr<'hir>, target: &FormatArgument) -> Option<&'hir Expr<'hir>> {
     let SpanData {
         lo,
         hi,
@@ -449,7 +445,6 @@ pub fn find_format_arg_expr<'hir, 'ast>(
             ControlFlow::Continue(())
         }
     })
-    .ok_or(&target.expr)
 }
 
 /// Span of the `:` and format specifiers

From 6de27a027223b3fed221d256ed6e8a9abf4b6b51 Mon Sep 17 00:00:00 2001
From: Alex Macleod <alex@macleod.io>
Date: Sat, 10 Aug 2024 18:36:52 +0000
Subject: [PATCH 24/54] lintcheck: key lints on line start rather than byte
 start/end

---
 lintcheck/src/json.rs   | 39 +++++++++++++++++++++------------------
 lintcheck/src/output.rs | 14 +++++++-------
 2 files changed, 28 insertions(+), 25 deletions(-)

diff --git a/lintcheck/src/json.rs b/lintcheck/src/json.rs
index 4211bce90b2fb..ee0c80aea522e 100644
--- a/lintcheck/src/json.rs
+++ b/lintcheck/src/json.rs
@@ -12,21 +12,21 @@ const TRUNCATION_TOTAL_TARGET: usize = 1000;
 
 #[derive(Debug, Deserialize, Serialize)]
 struct LintJson {
-    lint: String,
-    krate: String,
-    file_name: String,
-    byte_pos: (u32, u32),
-    file_link: String,
+    /// The lint name e.g. `clippy::bytes_nth`
+    name: String,
+    /// The filename and line number e.g. `anyhow-1.0.86/src/error.rs:42`
+    file_line: String,
+    file_url: String,
     rendered: String,
 }
 
 impl LintJson {
     fn key(&self) -> impl Ord + '_ {
-        (self.lint.as_str(), self.file_name.as_str(), self.byte_pos)
+        (self.name.as_str(), self.file_line.as_str())
     }
 
     fn info_text(&self, action: &str) -> String {
-        format!("{action} `{}` in `{}` at {}", self.lint, self.krate, self.file_link)
+        format!("{action} `{}` at [`{}`]({})", self.name, self.file_line, self.file_url)
     }
 }
 
@@ -36,13 +36,16 @@ pub(crate) fn output(clippy_warnings: Vec<ClippyWarning>) -> String {
         .into_iter()
         .map(|warning| {
             let span = warning.span();
+            let file_name = span
+                .file_name
+                .strip_prefix("target/lintcheck/sources/")
+                .unwrap_or(&span.file_name);
+            let file_line = format!("{file_name}:{}", span.line_start);
             LintJson {
-                file_name: span.file_name.clone(),
-                byte_pos: (span.byte_start, span.byte_end),
-                krate: warning.krate,
-                file_link: warning.url,
-                lint: warning.lint,
-                rendered: warning.diag.rendered.unwrap(),
+                name: warning.name,
+                file_line,
+                file_url: warning.url,
+                rendered: warning.diag.rendered.unwrap().trim().to_string(),
             }
         })
         .collect();
@@ -63,7 +66,7 @@ pub(crate) fn diff(old_path: &Path, new_path: &Path, truncate: bool) {
     let mut lint_warnings = vec![];
 
     for (name, changes) in &itertools::merge_join_by(old_warnings, new_warnings, |old, new| old.key().cmp(&new.key()))
-        .chunk_by(|change| change.as_ref().into_left().lint.to_string())
+        .chunk_by(|change| change.as_ref().into_left().name.clone())
     {
         let mut added = Vec::new();
         let mut removed = Vec::new();
@@ -162,7 +165,7 @@ fn print_warnings(title: &str, warnings: &[LintJson], truncate_after: usize) {
         return;
     }
 
-    print_h3(&warnings[0].lint, title);
+    print_h3(&warnings[0].name, title);
     println!();
 
     let warnings = truncate(warnings, truncate_after);
@@ -171,7 +174,7 @@ fn print_warnings(title: &str, warnings: &[LintJson], truncate_after: usize) {
         println!("{}", warning.info_text(title));
         println!();
         println!("```");
-        println!("{}", warning.rendered.trim_end());
+        println!("{}", warning.rendered);
         println!("```");
         println!();
     }
@@ -182,7 +185,7 @@ fn print_changed_diff(changed: &[(LintJson, LintJson)], truncate_after: usize) {
         return;
     }
 
-    print_h3(&changed[0].0.lint, "Changed");
+    print_h3(&changed[0].0.name, "Changed");
     println!();
 
     let changed = truncate(changed, truncate_after);
@@ -191,7 +194,7 @@ fn print_changed_diff(changed: &[(LintJson, LintJson)], truncate_after: usize) {
         println!("{}", new.info_text("Changed"));
         println!();
         println!("```diff");
-        for change in diff::lines(old.rendered.trim_end(), new.rendered.trim_end()) {
+        for change in diff::lines(&old.rendered, &new.rendered) {
             use diff::Result::{Both, Left, Right};
 
             match change {
diff --git a/lintcheck/src/output.rs b/lintcheck/src/output.rs
index 153786306956a..e38036315c2ce 100644
--- a/lintcheck/src/output.rs
+++ b/lintcheck/src/output.rs
@@ -51,7 +51,7 @@ impl RustcIce {
 /// A single warning that clippy issued while checking a `Crate`
 #[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
 pub struct ClippyWarning {
-    pub lint: String,
+    pub name: String,
     pub diag: Diagnostic,
     pub krate: String,
     /// The URL that points to the file and line of the lint emission
@@ -60,8 +60,8 @@ pub struct ClippyWarning {
 
 impl ClippyWarning {
     pub fn new(mut diag: Diagnostic, base_url: &str, krate: &str) -> Option<Self> {
-        let lint = diag.code.clone()?.code;
-        if !(lint.contains("clippy") || diag.message.contains("clippy"))
+        let name = diag.code.clone()?.code;
+        if !(name.contains("clippy") || diag.message.contains("clippy"))
             || diag.message.contains("could not read cargo metadata")
         {
             return None;
@@ -92,7 +92,7 @@ impl ClippyWarning {
         };
 
         Some(Self {
-            lint,
+            name,
             diag,
             url,
             krate: krate.to_string(),
@@ -108,7 +108,7 @@ impl ClippyWarning {
         let mut file = span.file_name.clone();
         let file_with_pos = format!("{file}:{}:{}", span.line_start, span.line_end);
         match format {
-            OutputFormat::Text => format!("{file_with_pos} {} \"{}\"\n", self.lint, self.diag.message),
+            OutputFormat::Text => format!("{file_with_pos} {} \"{}\"\n", self.name, self.diag.message),
             OutputFormat::Markdown => {
                 if file.starts_with("target") {
                     file.insert_str(0, "../");
@@ -116,7 +116,7 @@ impl ClippyWarning {
 
                 let mut output = String::from("| ");
                 write!(output, "[`{file_with_pos}`]({file}#L{})", span.line_start).unwrap();
-                write!(output, r#" | `{:<50}` | "{}" |"#, self.lint, self.diag.message).unwrap();
+                write!(output, r#" | `{:<50}` | "{}" |"#, self.name, self.diag.message).unwrap();
                 output.push('\n');
                 output
             },
@@ -164,7 +164,7 @@ fn gather_stats(warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>)
     let mut counter: HashMap<&String, usize> = HashMap::new();
     warnings
         .iter()
-        .for_each(|wrn| *counter.entry(&wrn.lint).or_insert(0) += 1);
+        .for_each(|wrn| *counter.entry(&wrn.name).or_insert(0) += 1);
 
     // collect into a tupled list for sorting
     let mut stats: Vec<(&&String, &usize)> = counter.iter().collect();

From 837d9045c5cf40b2ebf4b4ae2a02f35f8c7e0825 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?John=20K=C3=A5re=20Alsaker?= <john.kare.alsaker@gmail.com>
Date: Tue, 12 Mar 2024 02:12:28 +0100
Subject: [PATCH 25/54] Link `std` statically in `rustc_driver`

---
 src/main.rs           | 3 +++
 tests/compile-test.rs | 3 +++
 2 files changed, 6 insertions(+)

diff --git a/src/main.rs b/src/main.rs
index c9af2138a7233..c9853e53f3b38 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,6 @@
+// We need this feature as it changes `dylib` linking behavior and allows us to link to
+// `rustc_driver`.
+#![feature(rustc_private)]
 // warn on lints, that are included in `rust-lang/rust`s bootstrap
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
diff --git a/tests/compile-test.rs b/tests/compile-test.rs
index c7080e5dcdc9e..64253514fbe67 100644
--- a/tests/compile-test.rs
+++ b/tests/compile-test.rs
@@ -1,3 +1,6 @@
+// We need this feature as it changes `dylib` linking behavior and allows us to link to
+// `rustc_driver`.
+#![feature(rustc_private)]
 #![warn(rust_2018_idioms, unused_lifetimes)]
 #![allow(unused_extern_crates)]
 

From f93fb9a41716b0aae160900999170dc1a83e670b Mon Sep 17 00:00:00 2001
From: Alexey Semenyuk <alexsemenyuk88@gmail.com>
Date: Sun, 11 Aug 2024 20:33:28 +0500
Subject: [PATCH 26/54] Clarify message for unwrap lint

---
 clippy_lints/src/unwrap.rs                    |  6 ++---
 .../complex_conditionals_nested.stderr        |  2 +-
 .../checked_unwrap/simple_conditionals.stderr | 26 +++++++++----------
 3 files changed, 17 insertions(+), 17 deletions(-)

diff --git a/clippy_lints/src/unwrap.rs b/clippy_lints/src/unwrap.rs
index c0d9bcdd259c7..0b4ea0752b6ba 100644
--- a/clippy_lints/src/unwrap.rs
+++ b/clippy_lints/src/unwrap.rs
@@ -89,15 +89,15 @@ enum UnwrappableKind {
 impl UnwrappableKind {
     fn success_variant_pattern(self) -> &'static str {
         match self {
-            UnwrappableKind::Option => "Some(..)",
-            UnwrappableKind::Result => "Ok(..)",
+            UnwrappableKind::Option => "Some(<item>)",
+            UnwrappableKind::Result => "Ok(<item>)",
         }
     }
 
     fn error_variant_pattern(self) -> &'static str {
         match self {
             UnwrappableKind::Option => "None",
-            UnwrappableKind::Result => "Err(..)",
+            UnwrappableKind::Result => "Err(<item>)",
         }
     }
 }
diff --git a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr
index f7e659b10de07..329be4d366210 100644
--- a/tests/ui/checked_unwrap/complex_conditionals_nested.stderr
+++ b/tests/ui/checked_unwrap/complex_conditionals_nested.stderr
@@ -2,7 +2,7 @@ error: called `unwrap` on `x` after checking its variant with `is_some`
   --> tests/ui/checked_unwrap/complex_conditionals_nested.rs:13:13
    |
 LL |         if x.is_some() {
-   |         -------------- help: try: `if let Some(..) = x`
+   |         -------------- help: try: `if let Some(<item>) = x`
 LL |             // unnecessary
 LL |             x.unwrap();
    |             ^^^^^^^^^^
diff --git a/tests/ui/checked_unwrap/simple_conditionals.stderr b/tests/ui/checked_unwrap/simple_conditionals.stderr
index ddd600418afec..f7e338935a770 100644
--- a/tests/ui/checked_unwrap/simple_conditionals.stderr
+++ b/tests/ui/checked_unwrap/simple_conditionals.stderr
@@ -2,7 +2,7 @@ error: called `unwrap` on `x` after checking its variant with `is_some`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:46:9
    |
 LL |     if x.is_some() {
-   |     -------------- help: try: `if let Some(..) = x`
+   |     -------------- help: try: `if let Some(<item>) = x`
 LL |         // unnecessary
 LL |         x.unwrap();
    |         ^^^^^^^^^^
@@ -17,7 +17,7 @@ error: called `expect` on `x` after checking its variant with `is_some`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:49:9
    |
 LL |     if x.is_some() {
-   |     -------------- help: try: `if let Some(..) = x`
+   |     -------------- help: try: `if let Some(<item>) = x`
 ...
 LL |         x.expect("an error message");
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -59,7 +59,7 @@ error: called `unwrap` on `x` after checking its variant with `is_none`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:65:9
    |
 LL |     if x.is_none() {
-   |     -------------- help: try: `if let Some(..) = x`
+   |     -------------- help: try: `if let Some(<item>) = x`
 ...
 LL |         x.unwrap();
    |         ^^^^^^^^^^
@@ -68,7 +68,7 @@ error: called `unwrap` on `x` after checking its variant with `is_some`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:13:13
    |
 LL |         if $a.is_some() {
-   |         --------------- help: try: `if let Some(..) = x`
+   |         --------------- help: try: `if let Some(<item>) = x`
 LL |             // unnecessary
 LL |             $a.unwrap();
    |             ^^^^^^^^^^^
@@ -82,7 +82,7 @@ error: called `unwrap` on `x` after checking its variant with `is_ok`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:78:9
    |
 LL |     if x.is_ok() {
-   |     ------------ help: try: `if let Ok(..) = x`
+   |     ------------ help: try: `if let Ok(<item>) = x`
 LL |         // unnecessary
 LL |         x.unwrap();
    |         ^^^^^^^^^^
@@ -91,7 +91,7 @@ error: called `expect` on `x` after checking its variant with `is_ok`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:81:9
    |
 LL |     if x.is_ok() {
-   |     ------------ help: try: `if let Ok(..) = x`
+   |     ------------ help: try: `if let Ok(<item>) = x`
 ...
 LL |         x.expect("an error message");
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -127,7 +127,7 @@ error: called `unwrap_err` on `x` after checking its variant with `is_ok`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:94:9
    |
 LL |     if x.is_ok() {
-   |     ------------ help: try: `if let Err(..) = x`
+   |     ------------ help: try: `if let Err(<item>) = x`
 ...
 LL |         x.unwrap_err();
    |         ^^^^^^^^^^^^^^
@@ -145,7 +145,7 @@ error: called `unwrap_err` on `x` after checking its variant with `is_err`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:102:9
    |
 LL |     if x.is_err() {
-   |     ------------- help: try: `if let Err(..) = x`
+   |     ------------- help: try: `if let Err(<item>) = x`
 ...
 LL |         x.unwrap_err();
    |         ^^^^^^^^^^^^^^
@@ -154,7 +154,7 @@ error: called `unwrap` on `x` after checking its variant with `is_err`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:106:9
    |
 LL |     if x.is_err() {
-   |     ------------- help: try: `if let Ok(..) = x`
+   |     ------------- help: try: `if let Ok(<item>) = x`
 ...
 LL |         x.unwrap();
    |         ^^^^^^^^^^
@@ -172,7 +172,7 @@ error: called `unwrap` on `option` after checking its variant with `is_some`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:134:9
    |
 LL |     if option.is_some() {
-   |     ------------------- help: try: `if let Some(..) = &option`
+   |     ------------------- help: try: `if let Some(<item>) = &option`
 LL |         option.as_ref().unwrap();
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -189,7 +189,7 @@ error: called `unwrap` on `result` after checking its variant with `is_ok`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:144:9
    |
 LL |     if result.is_ok() {
-   |     ----------------- help: try: `if let Ok(..) = &result`
+   |     ----------------- help: try: `if let Ok(<item>) = &result`
 LL |         result.as_ref().unwrap();
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -206,7 +206,7 @@ error: called `unwrap` on `option` after checking its variant with `is_some`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:153:9
    |
 LL |     if option.is_some() {
-   |     ------------------- help: try: `if let Some(..) = &mut option`
+   |     ------------------- help: try: `if let Some(<item>) = &mut option`
 LL |         option.as_mut().unwrap();
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
 
@@ -223,7 +223,7 @@ error: called `unwrap` on `result` after checking its variant with `is_ok`
   --> tests/ui/checked_unwrap/simple_conditionals.rs:162:9
    |
 LL |     if result.is_ok() {
-   |     ----------------- help: try: `if let Ok(..) = &mut result`
+   |     ----------------- help: try: `if let Ok(<item>) = &mut result`
 LL |         result.as_mut().unwrap();
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
 

From 934e98d59135b64647e9756dafbd2151d67dd234 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Tue, 6 Aug 2024 21:38:14 +0200
Subject: [PATCH 27/54] Unify theme and settings menus

---
 util/gh-pages/index.html | 30 ++++---------
 util/gh-pages/script.js  | 93 +++++++++++++++++++---------------------
 util/gh-pages/style.css  | 47 ++++++++------------
 3 files changed, 72 insertions(+), 98 deletions(-)

diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html
index 12c9608f6f5f7..845e37feaddd3 100644
--- a/util/gh-pages/index.html
+++ b/util/gh-pages/index.html
@@ -26,27 +26,15 @@
     <link rel="stylesheet" href="style.css">
 </head>
 <body ng-app="clippy" ng-controller="lintList">
-    <div id="settings">
-        <div theme-dropdown class="theme-dropdown">
-            <div class="menu-container">
-                <div id="theme-icon" class="theme-icon">&#128396;</div>
-                <ul id="theme-menu" class="theme-choice">
-                    <li id="{{id}}" ng-repeat="(id, name) in themes" ng-click="selectTheme(id)">{{name}}</li>
-                </ul>
-            </div>
-        </div>
-        <div settings-dropdown class="settings-dropdown">
-            <div class="menu-container">
-                <div id="settings-icon" class="settings-icon"></div>
-                <ul id="settings-menu" class="settings-choice">
-                    <li>
-                        <label>
-                            <input type="checkbox" id="disable-shortcuts" onchange="changeSetting(this)">
-                            <span>Disable keyboard shortcuts</span>
-                        </label>
-                    </li>
-                </ul>
-            </div>
+    <div id="settings-dropdown">
+        <div class="settings-icon" tabindex="-1"></div>
+        <div class="settings-menu" tabindex="-1">
+            <div class="setting-radio-name">Theme</div>
+            <select id="theme-choice" onchange="setTheme(this.value, true)"></select>
+            <label>
+                <input type="checkbox" id="disable-shortcuts" onchange="changeSetting(this)">
+                <span>Disable keyboard shortcuts</span>
+            </label>
         </div>
     </div>
 
diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js
index f072327bc340f..fc643309db366 100644
--- a/util/gh-pages/script.js
+++ b/util/gh-pages/script.js
@@ -50,42 +50,6 @@
                 );
             };
         })
-        .directive('themeDropdown', function ($document) {
-            return {
-                restrict: 'A',
-                link: function ($scope, $element, $attr) {
-                    $element.bind('click', function () {
-                        $element.toggleClass('open');
-                        $element.addClass('open-recent');
-                    });
-
-                    $document.bind('click', function () {
-                        if (!$element.hasClass('open-recent')) {
-                            $element.removeClass('open');
-                        }
-                        $element.removeClass('open-recent');
-                    })
-                }
-            }
-        })
-        .directive('settingsDropdown', function ($document) {
-            return {
-                restrict: 'A',
-                link: function ($scope, $element, $attr) {
-                    $element.bind('click', function () {
-                        $element.toggleClass('open');
-                        $element.addClass('open-recent');
-                    });
-
-                    $document.bind('click', function () {
-                        if (!$element.hasClass('open-recent')) {
-                            $element.removeClass('open');
-                        }
-                        $element.removeClass('open-recent');
-                    })
-                }
-            }
-        })
         .directive('filterDropdown', function ($document) {
             return {
                 restrict: 'A',
@@ -145,15 +109,6 @@
                 ...GROUPS_FILTER_DEFAULT
             };
 
-            const THEMES_DEFAULT = {
-                light: "Light",
-                rust: "Rust",
-                coal: "Coal",
-                navy: "Navy",
-                ayu: "Ayu"
-            };
-            $scope.themes = THEMES_DEFAULT;
-
             $scope.versionFilters = {
                 "≥": {enabled: false, minorVersion: null },
                 "≤": {enabled: false, minorVersion: null },
@@ -339,10 +294,6 @@
                 $location.path($scope.search);
             }
 
-            $scope.selectTheme = function (theme) {
-                setTheme(theme, true);
-            }
-
             $scope.toggleLevels = function (value) {
                 const levels = $scope.levels;
                 for (const key in levels) {
@@ -598,6 +549,8 @@ function setTheme(theme, store) {
 
     if (store) {
         storeValue("theme", theme);
+    } else {
+        document.getElementById(`theme-choice`).value = theme;
     }
 }
 
@@ -634,6 +587,48 @@ function changeSetting(elem) {
     }
 }
 
+function onEachLazy(lazyArray, func) {
+    const arr = Array.prototype.slice.call(lazyArray);
+    for (const el of arr) {
+        func(el);
+    }
+}
+
+function handleBlur(event) {
+    const parent = document.getElementById("settings-dropdown");
+    if (!parent.contains(document.activeElement) &&
+        !parent.contains(event.relatedTarget)
+    ) {
+        parent.classList.remove("open");
+    }
+}
+
+function generateSettings() {
+    const THEMES = ["Ayu", "Coal", "Light", "Navy", "Rust"];
+    const themesElem = document.getElementById("theme-choice");
+    let children = '';
+
+    for (const theme of THEMES) {
+        const id = theme.toLowerCase();
+        children += `<option value="${id}">${theme}</option>`;
+    }
+    themesElem.innerHTML = children;
+    themesElem.onblur = handleBlur;
+
+    const settings = document.getElementById("settings-dropdown");
+    const settingsButton = settings.querySelector(".settings-icon")
+    settingsButton.onclick = () => settings.classList.toggle("open");
+    settingsButton.onblur = handleBlur;
+    const settingsMenu = settings.querySelector(".settings-menu");
+    settingsMenu.onblur = handleBlur;
+    onEachLazy(
+        settingsMenu.querySelectorAll("input"),
+        el => el.onblur = handleBlur,
+    );
+}
+
+generateSettings();
+
 // loading the theme after the initial load
 const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
 const theme = loadValue('theme');
diff --git a/util/gh-pages/style.css b/util/gh-pages/style.css
index 4ad8b502dd82f..a9485d511047a 100644
--- a/util/gh-pages/style.css
+++ b/util/gh-pages/style.css
@@ -220,20 +220,15 @@ details[open] {
     --inline-code-bg: #191f26;
 }
 
-#settings {
+#settings-dropdown {
     position: absolute;
     margin: 0.7em;
     z-index: 10;
     display: flex;
 }
 
-.menu-container {
-    position: relative;
-    width: 28px;
-}
-
 /* Applying the mdBook theme */
-.theme-icon, .settings-icon {
+.settings-icon {
     text-align: center;
     width: 2em;
     height: 2em;
@@ -242,24 +237,20 @@ details[open] {
     border-radius: 5px;
     user-select: none;
     cursor: pointer;
-}
-.theme-icon:hover, .settings-icon:hover {
     background: var(--theme-hover);
 }
-.theme-choice, .settings-choice {
+.settings-menu {
     display: none;
     list-style: none;
     border: 1px solid var(--theme-popup-border);
     border-radius: 5px;
     color: var(--fg);
     background: var(--theme-popup-bg);
-    padding: 0 0;
     overflow: hidden;
+    padding: 9px;
+    width: 207px;
     position: absolute;
-}
-
-.settings-dropdown {
-    margin-left: 4px;
+    top: 28px;
 }
 
 .settings-icon::before {
@@ -285,28 +276,28 @@ L4.75,12h2.5l0.5393066-2.1572876  c0.2276001-0.1062012,0.4459839-0.2269287,0.649
   padding-top: 3px;
 }
 
-.settings-choice {
-    padding: 4px;
-    width: 212px;
+.settings-menu * {
+    font-weight: normal;
 }
 
-.settings-choice label {
+.settings-menu label {
     cursor: pointer;
 }
 
-.theme-dropdown.open .theme-choice, .settings-dropdown.open .settings-choice {
+#settings-dropdown.open .settings-menu {
     display: block;
 }
 
-.theme-choice > li {
-    padding: 5px 10px;
-    font-size: 0.8em;
-    user-select: none;
+#theme-choice {
+    margin-bottom: 10px;
+    background: var(--searchbar-bg);
+    color: var(--searchbar-fg);
+    border-color: var(--theme-popup-border);
+    border-radius: 5px;
     cursor: pointer;
-}
-
-.theme-choice > li:hover {
-    background: var(--theme-hover);
+    width: 100%;
+    border-width: 1px;
+    padding: 5px;
 }
 
 .alert {

From 2ae6a4954b21e26bfc3d612c8e4dd8b23dc483a1 Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Sun, 11 Aug 2024 19:44:14 +0200
Subject: [PATCH 28/54] Move themes directly into HTML

---
 util/gh-pages/index.html |  8 +++++++-
 util/gh-pages/script.js  | 11 -----------
 2 files changed, 7 insertions(+), 12 deletions(-)

diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html
index 845e37feaddd3..37f964c226c99 100644
--- a/util/gh-pages/index.html
+++ b/util/gh-pages/index.html
@@ -30,7 +30,13 @@
         <div class="settings-icon" tabindex="-1"></div>
         <div class="settings-menu" tabindex="-1">
             <div class="setting-radio-name">Theme</div>
-            <select id="theme-choice" onchange="setTheme(this.value, true)"></select>
+            <select id="theme-choice" onchange="setTheme(this.value, true)">
+                <option value="ayu">Ayu</option>
+                <option value="coal">Coal</option>
+                <option value="light">Light</option>
+                <option value="navy">Navy</option>
+                <option value="rust">Rust</option>
+            </select>
             <label>
                 <input type="checkbox" id="disable-shortcuts" onchange="changeSetting(this)">
                 <span>Disable keyboard shortcuts</span>
diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js
index fc643309db366..d54b5e851c7c0 100644
--- a/util/gh-pages/script.js
+++ b/util/gh-pages/script.js
@@ -604,17 +604,6 @@ function handleBlur(event) {
 }
 
 function generateSettings() {
-    const THEMES = ["Ayu", "Coal", "Light", "Navy", "Rust"];
-    const themesElem = document.getElementById("theme-choice");
-    let children = '';
-
-    for (const theme of THEMES) {
-        const id = theme.toLowerCase();
-        children += `<option value="${id}">${theme}</option>`;
-    }
-    themesElem.innerHTML = children;
-    themesElem.onblur = handleBlur;
-
     const settings = document.getElementById("settings-dropdown");
     const settingsButton = settings.querySelector(".settings-icon")
     settingsButton.onclick = () => settings.classList.toggle("open");

From ddf2ba58860b5858a8e57e39595133b2509e955a Mon Sep 17 00:00:00 2001
From: Jason Newcomb <jsnewcomb@pm.me>
Date: Sun, 11 Aug 2024 17:59:35 -0400
Subject: [PATCH 29/54] Remove more instances of `snippet_opt`.

---
 clippy_lints/src/attrs/empty_line_after.rs    |  4 +--
 clippy_lints/src/attrs/non_minimal_cfg.rs     | 11 +++++---
 .../src/attrs/unnecessary_clippy_cfg.rs       | 11 +++-----
 clippy_lints/src/attrs/useless_attribute.rs   | 10 ++++----
 clippy_lints/src/casts/as_ptr_cast_mut.rs     |  4 +--
 clippy_lints/src/manual_async_fn.rs           |  8 +++---
 clippy_lints/src/needless_pass_by_value.rs    | 10 ++++----
 clippy_lints/src/pathbuf_init_then_push.rs    |  8 +++---
 clippy_lints/src/ptr.rs                       | 14 ++++++-----
 clippy_lints/src/ptr_offset_with_cast.rs      |  6 ++---
 clippy_lints/src/redundant_clone.rs           |  4 +--
 clippy_lints/src/reference.rs                 | 10 ++++----
 clippy_lints/src/regex.rs                     |  4 +--
 clippy_lints/src/returns.rs                   | 25 +++++++++++--------
 clippy_lints/src/single_range_in_vec_init.rs  |  8 +++---
 clippy_lints/src/trait_bounds.rs              | 10 +++-----
 clippy_lints/src/unused_unit.rs               | 22 ++++++++--------
 clippy_lints/src/visibility.rs                | 14 +++++------
 clippy_lints/src/write.rs                     |  8 +++---
 clippy_utils/src/source.rs                    | 11 +++++++-
 20 files changed, 109 insertions(+), 93 deletions(-)

diff --git a/clippy_lints/src/attrs/empty_line_after.rs b/clippy_lints/src/attrs/empty_line_after.rs
index ca43e76ac5780..7ff644b4c445f 100644
--- a/clippy_lints/src/attrs/empty_line_after.rs
+++ b/clippy_lints/src/attrs/empty_line_after.rs
@@ -1,6 +1,6 @@
 use super::{EMPTY_LINE_AFTER_DOC_COMMENTS, EMPTY_LINE_AFTER_OUTER_ATTR};
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::source::{is_present_in_source, snippet_opt, without_block_comments};
+use clippy_utils::source::{is_present_in_source, without_block_comments, SpanRangeExt};
 use rustc_ast::{AttrKind, AttrStyle};
 use rustc_lint::EarlyContext;
 use rustc_span::Span;
@@ -26,7 +26,7 @@ pub(super) fn check(cx: &EarlyContext<'_>, item: &rustc_ast::Item) {
                 item.span.parent(),
             );
 
-            if let Some(snippet) = snippet_opt(cx, end_of_attr_to_next_attr_or_item) {
+            if let Some(snippet) = end_of_attr_to_next_attr_or_item.get_source_text(cx) {
                 let lines = snippet.split('\n').collect::<Vec<_>>();
                 let lines = without_block_comments(lines);
 
diff --git a/clippy_lints/src/attrs/non_minimal_cfg.rs b/clippy_lints/src/attrs/non_minimal_cfg.rs
index 3fde70615853d..877025cce4c2b 100644
--- a/clippy_lints/src/attrs/non_minimal_cfg.rs
+++ b/clippy_lints/src/attrs/non_minimal_cfg.rs
@@ -1,6 +1,6 @@
 use super::{Attribute, NON_MINIMAL_CFG};
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_ast::{MetaItemKind, NestedMetaItem};
 use rustc_errors::Applicability;
 use rustc_lint::EarlyContext;
@@ -29,8 +29,13 @@ fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
                         meta.span,
                         "unneeded sub `cfg` when there is only one condition",
                         |diag| {
-                            if let Some(snippet) = snippet_opt(cx, list[0].span()) {
-                                diag.span_suggestion(meta.span, "try", snippet, Applicability::MaybeIncorrect);
+                            if let Some(snippet) = list[0].span().get_source_text(cx) {
+                                diag.span_suggestion(
+                                    meta.span,
+                                    "try",
+                                    snippet.to_owned(),
+                                    Applicability::MaybeIncorrect,
+                                );
                             }
                         },
                     );
diff --git a/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs b/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs
index 486e7c6ec4f4a..478ba7a187be5 100644
--- a/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs
+++ b/clippy_lints/src/attrs/unnecessary_clippy_cfg.rs
@@ -1,6 +1,7 @@
 use super::{Attribute, UNNECESSARY_CLIPPY_CFG};
 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
+use itertools::Itertools;
 use rustc_ast::AttrStyle;
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, Level};
@@ -31,7 +32,7 @@ pub(super) fn check(
             return;
         }
         if nb_items == clippy_lints.len() {
-            if let Some(snippet) = snippet_opt(cx, behind_cfg_attr.span) {
+            if let Some(snippet) = behind_cfg_attr.span.get_source_text(cx) {
                 span_lint_and_sugg(
                     cx,
                     UNNECESSARY_CLIPPY_CFG,
@@ -47,11 +48,7 @@ pub(super) fn check(
                 );
             }
         } else {
-            let snippet = clippy_lints
-                .iter()
-                .filter_map(|sp| snippet_opt(cx, *sp))
-                .collect::<Vec<_>>()
-                .join(",");
+            let snippet = clippy_lints.iter().filter_map(|sp| sp.get_source_text(cx)).join(",");
             span_lint_and_note(
                 cx,
                 UNNECESSARY_CLIPPY_CFG,
diff --git a/clippy_lints/src/attrs/useless_attribute.rs b/clippy_lints/src/attrs/useless_attribute.rs
index f0868231d01a3..67ba605a59fa5 100644
--- a/clippy_lints/src/attrs/useless_attribute.rs
+++ b/clippy_lints/src/attrs/useless_attribute.rs
@@ -1,7 +1,7 @@
 use super::utils::{extract_clippy_lint, is_lint_level, is_word};
 use super::{Attribute, USELESS_ATTRIBUTE};
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::{first_line_of_span, snippet_opt};
+use clippy_utils::source::{first_line_of_span, SpanRangeExt};
 use rustc_ast::NestedMetaItem;
 use rustc_errors::Applicability;
 use rustc_hir::{Item, ItemKind};
@@ -69,14 +69,14 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute])
                 }
                 let line_span = first_line_of_span(cx, attr.span);
 
-                if let Some(mut sugg) = snippet_opt(cx, line_span) {
-                    if sugg.contains("#[") {
+                if let Some(src) = line_span.get_source_text(cx) {
+                    if src.contains("#[") {
+                        #[expect(clippy::collapsible_span_lint_calls)]
                         span_lint_and_then(cx, USELESS_ATTRIBUTE, line_span, "useless lint attribute", |diag| {
-                            sugg = sugg.replacen("#[", "#![", 1);
                             diag.span_suggestion(
                                 line_span,
                                 "if you just forgot a `!`, use",
-                                sugg,
+                                src.replacen("#[", "#![", 1),
                                 Applicability::MaybeIncorrect,
                             );
                         });
diff --git a/clippy_lints/src/casts/as_ptr_cast_mut.rs b/clippy_lints/src/casts/as_ptr_cast_mut.rs
index f05fd3fcde500..15ecba20a0bc4 100644
--- a/clippy_lints/src/casts/as_ptr_cast_mut.rs
+++ b/clippy_lints/src/casts/as_ptr_cast_mut.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::LateContext;
@@ -19,7 +19,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
         && let as_ptr_sig = cx.tcx.fn_sig(as_ptr_did).instantiate_identity()
         && let Some(first_param_ty) = as_ptr_sig.skip_binder().inputs().iter().next()
         && let ty::Ref(_, _, Mutability::Not) = first_param_ty.kind()
-        && let Some(recv) = snippet_opt(cx, receiver.span)
+        && let Some(recv) = receiver.span.get_source_text(cx)
     {
         // `as_mut_ptr` might not exist
         let applicability = Applicability::MaybeIncorrect;
diff --git a/clippy_lints/src/manual_async_fn.rs b/clippy_lints/src/manual_async_fn.rs
index 25c7e5d38b319..61723aec59049 100644
--- a/clippy_lints/src/manual_async_fn.rs
+++ b/clippy_lints/src/manual_async_fn.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::{position_before_rarrow, snippet_block, snippet_opt};
+use clippy_utils::source::{position_before_rarrow, snippet_block, SpanRangeExt};
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
@@ -68,8 +68,8 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
                 header_span,
                 "this function can be simplified using the `async fn` syntax",
                 |diag| {
-                    if let Some(vis_snip) = snippet_opt(cx, *vis_span)
-                        && let Some(header_snip) = snippet_opt(cx, header_span)
+                    if let Some(vis_snip) = vis_span.get_source_text(cx)
+                        && let Some(header_snip) = header_span.get_source_text(cx)
                         && let Some(ret_pos) = position_before_rarrow(&header_snip)
                         && let Some((ret_sugg, ret_snip)) = suggested_ret(cx, output)
                     {
@@ -190,6 +190,6 @@ fn suggested_ret(cx: &LateContext<'_>, output: &Ty<'_>) -> Option<(&'static str,
         Some((sugg, String::new()))
     } else {
         let sugg = "return the output of the future directly";
-        snippet_opt(cx, output.span).map(|snip| (sugg, format!(" -> {snip}")))
+        output.span.get_source_text(cx).map(|src| (sugg, format!(" -> {src}")))
     }
 }
diff --git a/clippy_lints/src/needless_pass_by_value.rs b/clippy_lints/src/needless_pass_by_value.rs
index a0bbf6b14b217..addb4b1aee807 100644
--- a/clippy_lints/src/needless_pass_by_value.rs
+++ b/clippy_lints/src/needless_pass_by_value.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_self;
 use clippy_utils::ptr::get_spans;
-use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::source::{snippet, SpanRangeExt};
 use clippy_utils::ty::{
     implements_trait, implements_trait_with_env_from_iter, is_copy, is_type_diagnostic_item, is_type_lang_item,
 };
@@ -242,8 +242,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
                         for (span, suggestion) in clone_spans {
                             diag.span_suggestion(
                                 span,
-                                snippet_opt(cx, span)
-                                    .map_or("change the call to".into(), |x| format!("change `{x}` to")),
+                                span.get_source_text(cx)
+                                    .map_or("change the call to".to_owned(), |src| format!("change `{src}` to")),
                                 suggestion,
                                 Applicability::Unspecified,
                             );
@@ -267,8 +267,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
                             for (span, suggestion) in clone_spans {
                                 diag.span_suggestion(
                                     span,
-                                    snippet_opt(cx, span)
-                                        .map_or("change the call to".into(), |x| format!("change `{x}` to")),
+                                    span.get_source_text(cx)
+                                        .map_or("change the call to".to_owned(), |src| format!("change `{src}` to")),
                                     suggestion,
                                     Applicability::Unspecified,
                                 );
diff --git a/clippy_lints/src/pathbuf_init_then_push.rs b/clippy_lints/src/pathbuf_init_then_push.rs
index 18bfb588a1187..d7fa48c1e38dd 100644
--- a/clippy_lints/src/pathbuf_init_then_push.rs
+++ b/clippy_lints/src/pathbuf_init_then_push.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::path_to_local_id;
-use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::source::{snippet, SpanRangeExt};
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_ast::{LitKind, StrStyle};
 use rustc_errors::Applicability;
@@ -74,7 +74,7 @@ impl<'tcx> PathbufPushSearcher<'tcx> {
             && let Some(arg) = self.arg
             && let ExprKind::Lit(x) = arg.kind
             && let LitKind::Str(_, StrStyle::Cooked) = x.node
-            && let Some(s) = snippet_opt(cx, arg.span)
+            && let Some(s) = arg.span.get_source_text(cx)
         {
             Some(format!(" = PathBuf::from({s});"))
         } else {
@@ -84,8 +84,8 @@ impl<'tcx> PathbufPushSearcher<'tcx> {
 
     fn gen_pathbuf_join(&self, cx: &LateContext<'_>) -> Option<String> {
         let arg = self.arg?;
-        let arg_str = snippet_opt(cx, arg.span)?;
-        let init_val = snippet_opt(cx, self.init_val.span)?;
+        let arg_str = arg.span.get_source_text(cx)?;
+        let init_val = self.init_val.span.get_source_text(cx)?;
         Some(format!(" = {init_val}.join({arg_str});"))
     }
 
diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs
index 02c05e0aaf9ce..1e824681fa12f 100644
--- a/clippy_lints/src/ptr.rs
+++ b/clippy_lints/src/ptr.rs
@@ -1,7 +1,7 @@
 //! Checks for usage of  `&Vec[_]` and `&String`.
 
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::expr_sig;
 use clippy_utils::visitors::contains_unsafe_block;
 use clippy_utils::{get_expr_use_or_unification_node, is_lint_allowed, path_def_id, path_to_local};
@@ -243,7 +243,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
                         .chain(result.replacements.iter().map(|r| {
                             (
                                 r.expr_span,
-                                format!("{}{}", snippet_opt(cx, r.self_span).unwrap(), r.replacement),
+                                format!("{}{}", r.self_span.get_source_text(cx).unwrap(), r.replacement),
                             )
                         }))
                         .collect(),
@@ -372,7 +372,7 @@ impl fmt::Display for DerefTyDisplay<'_, '_> {
             DerefTy::Path => f.write_str("Path"),
             DerefTy::Slice(hir_ty, ty) => {
                 f.write_char('[')?;
-                match hir_ty.and_then(|s| snippet_opt(self.0, s)) {
+                match hir_ty.and_then(|s| s.get_source_text(self.0)) {
                     Some(s) => f.write_str(&s)?,
                     None => ty.fmt(f)?,
                 }
@@ -413,6 +413,7 @@ impl<'tcx> DerefTy<'tcx> {
     }
 }
 
+#[expect(clippy::too_many_lines)]
 fn check_fn_args<'cx, 'tcx: 'cx>(
     cx: &'cx LateContext<'tcx>,
     fn_sig: ty::FnSig<'tcx>,
@@ -488,8 +489,6 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                                 return None;
                             }
 
-                            let ty_name = snippet_opt(cx, ty.span()).unwrap_or_else(|| args.type_at(1).to_string());
-
                             span_lint_hir_and_then(
                                 cx,
                                 PTR_ARG,
@@ -500,7 +499,10 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                                     diag.span_suggestion(
                                         hir_ty.span,
                                         "change this to",
-                                        format!("&{}{ty_name}", mutability.prefix_str()),
+                                        match ty.span().get_source_text(cx) {
+                                            Some(s) => format!("&{}{s}", mutability.prefix_str()),
+                                            None => format!("&{}{}", mutability.prefix_str(), args.type_at(1)),
+                                        },
                                         Applicability::Unspecified,
                                     );
                                 },
diff --git a/clippy_lints/src/ptr_offset_with_cast.rs b/clippy_lints/src/ptr_offset_with_cast.rs
index 7c82895d60919..87a52cb2186c5 100644
--- a/clippy_lints/src/ptr_offset_with_cast.rs
+++ b/clippy_lints/src/ptr_offset_with_cast.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -120,8 +120,8 @@ fn build_suggestion(
     receiver_expr: &Expr<'_>,
     cast_lhs_expr: &Expr<'_>,
 ) -> Option<String> {
-    let receiver = snippet_opt(cx, receiver_expr.span)?;
-    let cast_lhs = snippet_opt(cx, cast_lhs_expr.span)?;
+    let receiver = receiver_expr.span.get_source_text(cx)?;
+    let cast_lhs = cast_lhs_expr.span.get_source_text(cx)?;
     Some(format!("{receiver}.{}({cast_lhs})", method.suggestion()))
 }
 
diff --git a/clippy_lints/src/redundant_clone.rs b/clippy_lints/src/redundant_clone.rs
index a1231c082e68a..bfdc1cbeed7e1 100644
--- a/clippy_lints/src/redundant_clone.rs
+++ b/clippy_lints/src/redundant_clone.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
 use clippy_utils::mir::{visit_local_usage, LocalUsage, PossibleBorrowerMap};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::{has_drop, is_copy, is_type_diagnostic_item, is_type_lang_item, walk_ptrs_ty_depth};
 use clippy_utils::{fn_has_unsatisfiable_preds, match_def_path, paths};
 use rustc_errors::Applicability;
@@ -208,7 +208,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClone {
                 .assert_crate_local()
                 .lint_root;
 
-            if let Some(snip) = snippet_opt(cx, span)
+            if let Some(snip) = span.get_source_text(cx)
                 && let Some(dot) = snip.rfind('.')
             {
                 let sugg_span = span.with_lo(span.lo() + BytePos(u32::try_from(dot).unwrap()));
diff --git a/clippy_lints/src/reference.rs b/clippy_lints/src/reference.rs
index 8f32cf5f2a1db..2b4ef21fc48e1 100644
--- a/clippy_lints/src/reference.rs
+++ b/clippy_lints/src/reference.rs
@@ -1,10 +1,10 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{snippet_opt, snippet_with_applicability};
+use clippy_utils::source::{snippet_with_applicability, SpanRangeExt};
 use rustc_ast::ast::{Expr, ExprKind, Mutability, UnOp};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::BytePos;
+use rustc_span::{BytePos, Span};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -56,11 +56,11 @@ impl EarlyLintPass for DerefAddrOf {
         {
             let mut applicability = Applicability::MachineApplicable;
             let sugg = if e.span.from_expansion() {
-                if let Some(macro_source) = snippet_opt(cx, e.span) {
+                if let Some(macro_source) = e.span.get_source_text(cx) {
                     // Remove leading whitespace from the given span
                     // e.g: ` $visitor` turns into `$visitor`
-                    let trim_leading_whitespaces = |span| {
-                        snippet_opt(cx, span)
+                    let trim_leading_whitespaces = |span: Span| {
+                        span.get_source_text(cx)
                             .and_then(|snip| {
                                 #[expect(clippy::cast_possible_truncation)]
                                 snip.find(|c: char| !c.is_whitespace())
diff --git a/clippy_lints/src/regex.rs b/clippy_lints/src/regex.rs
index 95014b230431c..f6ef02b7c2330 100644
--- a/clippy_lints/src/regex.rs
+++ b/clippy_lints/src/regex.rs
@@ -2,7 +2,7 @@ use std::fmt::Display;
 
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{def_path_def_ids, path_def_id, paths};
 use rustc_ast::ast::{LitKind, StrStyle};
 use rustc_hir::def_id::DefIdMap;
@@ -122,7 +122,7 @@ fn lint_syntax_error(cx: &LateContext<'_>, error: &regex_syntax::Error, unescape
     };
 
     if let Some((primary, auxiliary, kind)) = parts
-        && let Some(literal_snippet) = snippet_opt(cx, base)
+        && let Some(literal_snippet) = base.get_source_text(cx)
         && let Some(inner) = literal_snippet.get(offset as usize..)
         // Only convert to native rustc spans if the parsed regex matches the
         // source snippet exactly, to ensure the span offsets are correct
diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs
index 13016cdadb072..ffc9475cf9cdf 100644
--- a/clippy_lints/src/returns.rs
+++ b/clippy_lints/src/returns.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
-use clippy_utils::source::{snippet_opt, snippet_with_context};
+use clippy_utils::source::{snippet_with_context, SpanRangeExt};
 use clippy_utils::sugg::has_enclosing_paren;
 use clippy_utils::visitors::{for_each_expr, Descend};
 use clippy_utils::{
@@ -250,20 +250,25 @@ impl<'tcx> LateLintPass<'tcx> for Return {
                 |err| {
                     err.span_label(local.span, "unnecessary `let` binding");
 
-                    if let Some(mut snippet) = snippet_opt(cx, initexpr.span) {
-                        if binary_expr_needs_parentheses(initexpr) {
-                            if !has_enclosing_paren(&snippet) {
-                                snippet = format!("({snippet})");
+                    if let Some(src) = initexpr.span.get_source_text(cx) {
+                        let sugg = if binary_expr_needs_parentheses(initexpr) {
+                            if has_enclosing_paren(&src) {
+                                src.to_owned()
+                            } else {
+                                format!("({src})")
                             }
                         } else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
-                            if !has_enclosing_paren(&snippet) {
-                                snippet = format!("({snippet})");
+                            if has_enclosing_paren(&src) {
+                                format!("{src} as _")
+                            } else {
+                                format!("({src}) as _")
                             }
-                            snippet.push_str(" as _");
-                        }
+                        } else {
+                            src.to_owned()
+                        };
                         err.multipart_suggestion(
                             "return the expression directly",
-                            vec![(local.span, String::new()), (retexpr.span, snippet)],
+                            vec![(local.span, String::new()), (retexpr.span, sugg)],
                             Applicability::MachineApplicable,
                         );
                     } else {
diff --git a/clippy_lints/src/single_range_in_vec_init.rs b/clippy_lints/src/single_range_in_vec_init.rs
index e05584296381c..44e585953bfb9 100644
--- a/clippy_lints/src/single_range_in_vec_init.rs
+++ b/clippy_lints/src/single_range_in_vec_init.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::get_trait_def_id;
 use clippy_utils::higher::VecArgs;
 use clippy_utils::macros::root_macro_call_first_node;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::implements_trait;
 use rustc_ast::{LitIntType, LitKind, UintTy};
 use rustc_errors::Applicability;
@@ -92,12 +92,12 @@ impl LateLintPass<'_> for SingleRangeInVecInit {
 
         if matches!(lang_item, LangItem::Range)
             && let ty = cx.typeck_results().expr_ty(start.expr)
-            && let Some(snippet) = snippet_opt(cx, span)
+            && let Some(snippet) = span.get_source_text(cx)
             // `is_from_proc_macro` will skip any `vec![]`. Let's not!
             && snippet.starts_with(suggested_type.starts_with())
             && snippet.ends_with(suggested_type.ends_with())
-            && let Some(start_snippet) = snippet_opt(cx, start.span)
-            && let Some(end_snippet) = snippet_opt(cx, end.span)
+            && 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"])
                 && implements_trait(cx, ty, step_def_id, &[])
diff --git a/clippy_lints/src/trait_bounds.rs b/clippy_lints/src/trait_bounds.rs
index 59b74122f3084..2e87d36df3121 100644
--- a/clippy_lints/src/trait_bounds.rs
+++ b/clippy_lints/src/trait_bounds.rs
@@ -1,7 +1,7 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_sugg};
-use clippy_utils::source::{snippet, snippet_opt, snippet_with_applicability};
+use clippy_utils::source::{snippet, snippet_with_applicability, SpanRangeExt};
 use clippy_utils::{is_from_proc_macro, SpanlessEq, SpanlessHash};
 use core::hash::{Hash, Hasher};
 use itertools::Itertools;
@@ -206,8 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
 
                 let fixed_trait_snippet = unique_traits
                     .iter()
-                    .filter_map(|b| snippet_opt(cx, b.span))
-                    .collect::<Vec<_>>()
+                    .filter_map(|b| b.span.get_source_text(cx))
                     .join(" + ");
 
                 span_lint_and_sugg(
@@ -462,9 +461,8 @@ fn rollup_traits(
 
         let traits = comparable_bounds
             .iter()
-            .filter_map(|&(_, span)| snippet_opt(cx, span))
-            .collect::<Vec<_>>();
-        let traits = traits.join(" + ");
+            .filter_map(|&(_, span)| span.get_source_text(cx))
+            .join(" + ");
 
         span_lint_and_sugg(
             cx,
diff --git a/clippy_lints/src/unused_unit.rs b/clippy_lints/src/unused_unit.rs
index b70aa768b46b1..65f431d338bdb 100644
--- a/clippy_lints/src/unused_unit.rs
+++ b/clippy_lints/src/unused_unit.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{position_before_rarrow, snippet_opt};
+use clippy_utils::source::{position_before_rarrow, SpanRangeExt};
 use rustc_ast::visit::FnKind;
 use rustc_ast::{ast, ClosureBinder};
 use rustc_errors::Applicability;
@@ -128,15 +128,17 @@ fn is_unit_expr(expr: &ast::Expr) -> bool {
 
 fn lint_unneeded_unit_return(cx: &EarlyContext<'_>, ty: &ast::Ty, span: Span) {
     let (ret_span, appl) =
-        snippet_opt(cx, span.with_hi(ty.span.hi())).map_or((ty.span, Applicability::MaybeIncorrect), |fn_source| {
-            position_before_rarrow(&fn_source).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
-                (
-                    #[expect(clippy::cast_possible_truncation)]
-                    ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
-                    Applicability::MachineApplicable,
-                )
-            })
-        });
+        span.with_hi(ty.span.hi())
+            .get_source_text(cx)
+            .map_or((ty.span, Applicability::MaybeIncorrect), |src| {
+                position_before_rarrow(&src).map_or((ty.span, Applicability::MaybeIncorrect), |rpos| {
+                    (
+                        #[expect(clippy::cast_possible_truncation)]
+                        ty.span.with_lo(BytePos(span.lo().0 + rpos as u32)),
+                        Applicability::MachineApplicable,
+                    )
+                })
+            });
     span_lint_and_sugg(
         cx,
         UNUSED_UNIT,
diff --git a/clippy_lints/src/visibility.rs b/clippy_lints/src/visibility.rs
index 63f3a5d7f8305..7a85196cecaaa 100644
--- a/clippy_lints/src/visibility.rs
+++ b/clippy_lints/src/visibility.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_ast::ast::{Item, VisibilityKind};
 use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
@@ -82,9 +82,7 @@ impl EarlyLintPass for Visibility {
         if !in_external_macro(cx.sess(), item.span)
             && let VisibilityKind::Restricted { path, shorthand, .. } = &item.vis.kind
         {
-            if **path == kw::SelfLower
-                && let Some(false) = is_from_proc_macro(cx, item.vis.span)
-            {
+            if **path == kw::SelfLower && !is_from_proc_macro(cx, item.vis.span) {
                 span_lint_and_then(
                     cx,
                     NEEDLESS_PUB_SELF,
@@ -104,7 +102,7 @@ impl EarlyLintPass for Visibility {
             if (**path == kw::Super || **path == kw::SelfLower || **path == kw::Crate)
                 && !*shorthand
                 && let [.., last] = &*path.segments
-                && let Some(false) = is_from_proc_macro(cx, item.vis.span)
+                && !is_from_proc_macro(cx, item.vis.span)
             {
                 #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
                 span_lint_and_then(
@@ -125,7 +123,7 @@ impl EarlyLintPass for Visibility {
 
             if *shorthand
                 && let [.., last] = &*path.segments
-                && let Some(false) = is_from_proc_macro(cx, item.vis.span)
+                && !is_from_proc_macro(cx, item.vis.span)
             {
                 #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
                 span_lint_and_then(
@@ -147,6 +145,6 @@ impl EarlyLintPass for Visibility {
     }
 }
 
-fn is_from_proc_macro(cx: &EarlyContext<'_>, span: Span) -> Option<bool> {
-    snippet_opt(cx, span).map(|s| !s.starts_with("pub"))
+fn is_from_proc_macro(cx: &EarlyContext<'_>, span: Span) -> bool {
+    !span.check_source_text(cx, |src| src.starts_with("pub"))
 }
diff --git a/clippy_lints/src/write.rs b/clippy_lints/src/write.rs
index 54e7e92f0c45d..ec5a5896fb2bf 100644
--- a/clippy_lints/src/write.rs
+++ b/clippy_lints/src/write.rs
@@ -2,7 +2,7 @@ use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::is_in_test;
 use clippy_utils::macros::{format_arg_removal_span, root_macro_call_first_node, FormatArgsStorage, MacroCall};
-use clippy_utils::source::{expand_past_previous_comma, snippet_opt};
+use clippy_utils::source::{expand_past_previous_comma, SpanRangeExt};
 use rustc_ast::token::LitKind;
 use rustc_ast::{
     FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder,
@@ -397,7 +397,7 @@ fn check_newline(cx: &LateContext<'_>, format_args: &FormatArgs, macro_call: &Ma
             format!("using `{name}!()` with a format string that ends in a single newline"),
             |diag| {
                 let name_span = cx.sess().source_map().span_until_char(macro_call.span, '!');
-                let Some(format_snippet) = snippet_opt(cx, format_string_span) else {
+                let Some(format_snippet) = format_string_span.get_source_text(cx) else {
                     return;
                 };
 
@@ -492,7 +492,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
             && let Some(arg) = format_args.arguments.by_index(index)
             && let rustc_ast::ExprKind::Lit(lit) = &arg.expr.kind
             && !arg.expr.span.from_expansion()
-            && let Some(value_string) = snippet_opt(cx, arg.expr.span)
+            && let Some(value_string) = arg.expr.span.get_source_text(cx)
         {
             let (replacement, replace_raw) = match lit.kind {
                 LitKind::Str | LitKind::StrRaw(_) => match extract_str_literal(&value_string) {
@@ -515,7 +515,7 @@ fn check_literal(cx: &LateContext<'_>, format_args: &FormatArgs, name: &str) {
                 _ => continue,
             };
 
-            let Some(format_string_snippet) = snippet_opt(cx, format_args.span) else {
+            let Some(format_string_snippet) = format_args.span.get_source_text(cx) else {
                 continue;
             };
             let format_string_is_raw = format_string_snippet.starts_with('r');
diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs
index 1be1a9b28b9ea..8b49b013e0313 100644
--- a/clippy_utils/src/source.rs
+++ b/clippy_utils/src/source.rs
@@ -16,7 +16,7 @@ use rustc_span::{
 };
 use std::borrow::Cow;
 use std::fmt;
-use std::ops::{Deref, Range};
+use std::ops::{Deref, Index, Range};
 
 pub trait HasSession {
     fn sess(&self) -> &Session;
@@ -186,6 +186,15 @@ impl AsRef<str> for SourceText {
         self.as_str()
     }
 }
+impl<T> Index<T> for SourceText
+where
+    str: Index<T>,
+{
+    type Output = <str as Index<T>>::Output;
+    fn index(&self, idx: T) -> &Self::Output {
+        &self.as_str()[idx]
+    }
+}
 impl fmt::Display for SourceText {
     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
         self.as_str().fmt(f)

From d7f1252ddf7ae02667ac9de295171596c7075640 Mon Sep 17 00:00:00 2001
From: Antoni Spaanderman <56turtle56@gmail.com>
Date: Mon, 12 Aug 2024 18:42:13 +0200
Subject: [PATCH 30/54] fix code blocks in doc comments inconsistently using 3
 or 4 spaces of indentation

---
 clippy_lints/src/doc/mod.rs                                 | 2 +-
 clippy_lints/src/methods/mod.rs                             | 6 +++---
 clippy_lints/src/utils/internal_lints/metadata_collector.rs | 6 ++++--
 3 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs
index d7b3a7c74f3ce..762d5595e145e 100644
--- a/clippy_lints/src/doc/mod.rs
+++ b/clippy_lints/src/doc/mod.rs
@@ -309,7 +309,7 @@ declare_clippy_lint! {
     /// ### Known problems
     /// Inner doc comments can only appear before items, so there are certain cases where the suggestion
     /// made by this lint is not valid code. For example:
-    /// ```rs
+    /// ```rust
     /// fn foo() {}
     /// ///!
     /// fn bar() {}
diff --git a/clippy_lints/src/methods/mod.rs b/clippy_lints/src/methods/mod.rs
index 1d7b10fe8f045..d7126990edb1d 100644
--- a/clippy_lints/src/methods/mod.rs
+++ b/clippy_lints/src/methods/mod.rs
@@ -2702,10 +2702,10 @@ declare_clippy_lint! {
     ///             }
     ///         })
     /// }
-    ///  ```
+    /// ```
     ///
-    ///  After:
-    ///  ```rust
+    /// After:
+    /// ```rust
     /// use std::{fmt, num::ParseIntError};
     ///
     /// #[derive(Debug)]
diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index 57f45aa3e4851..c73ec262637f7 100644
--- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -687,10 +687,12 @@ fn cleanup_docs(docs_collection: &Vec<String>) -> String {
                     .trim()
                     .split(',')
                     // remove rustdoc directives
-                    .find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic"))
+                    .find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic" | "compile_fail"))
                     // if no language is present, fill in "rust"
                     .unwrap_or("rust");
-                let len_diff = line.len() - line.trim_start().len();
+                let len_diff = line
+                    .strip_prefix(' ')
+                    .map_or(0, |line| line.len() - line.trim_start().len());
                 if len_diff != 0 {
                     // We put back the indentation.
                     docs.push_str(&line[..len_diff]);

From 182cd5f2785ae77f3a05cf75ee301ed8850c8d0a Mon Sep 17 00:00:00 2001
From: Alex Macleod <alex@macleod.io>
Date: Mon, 5 Aug 2024 14:08:04 +0000
Subject: [PATCH 31/54] Replace the metadata collector with tests

---
 .cargo/config.toml                            |    6 +-
 .github/workflows/clippy_bors.yml             |    5 -
 Cargo.toml                                    |    4 +-
 book/src/development/adding_lints.md          |    2 +-
 book/src/lint_configuration.md                |    4 +-
 clippy_dev/src/update_lints.rs                |    2 +-
 clippy_lints/src/declared_lints.rs            |    4 +-
 clippy_lints/src/lib.rs                       |   48 +-
 clippy_lints/src/utils/internal_lints.rs      |    1 -
 .../internal_lints/metadata_collector.rs      | 1080 -----------------
 declare_clippy_lint/src/lib.rs                |   25 +-
 tests/compile-test.rs                         |  382 ++++--
 tests/config-metadata.rs                      |   76 ++
 util/gh-pages/index.html                      |    6 +-
 util/gh-pages/script.js                       |    9 +-
 15 files changed, 448 insertions(+), 1206 deletions(-)
 delete mode 100644 clippy_lints/src/utils/internal_lints/metadata_collector.rs
 create mode 100644 tests/config-metadata.rs

diff --git a/.cargo/config.toml b/.cargo/config.toml
index ce07290d1e1e5..d9c635df5dc79 100644
--- a/.cargo/config.toml
+++ b/.cargo/config.toml
@@ -1,10 +1,10 @@
 [alias]
+bless = "test --config env.RUSTC_BLESS='1'"
 uitest = "test --test compile-test"
-uibless = "test --test compile-test -- -- --bless"
-bless = "test -- -- --bless"
+uibless = "bless --test compile-test"
 dev = "run --package clippy_dev --bin clippy_dev --manifest-path clippy_dev/Cargo.toml --"
 lintcheck = "run --package lintcheck --bin lintcheck --manifest-path lintcheck/Cargo.toml  -- "
-collect-metadata = "test --test dogfood --features internal -- collect_metadata"
+collect-metadata = "test --test compile-test --config env.COLLECT_METADATA='1'"
 
 [build]
 # -Zbinary-dep-depinfo allows us to track which rlib files to use for compiling UI tests
diff --git a/.github/workflows/clippy_bors.yml b/.github/workflows/clippy_bors.yml
index 10e18e84c89fd..2aa13313fa518 100644
--- a/.github/workflows/clippy_bors.yml
+++ b/.github/workflows/clippy_bors.yml
@@ -136,11 +136,6 @@ jobs:
     - name: Test metadata collection
       run: cargo collect-metadata
 
-    - name: Test lint_configuration.md is up-to-date
-      run: |
-        echo "run \`cargo collect-metadata\` if this fails"
-        git update-index --refresh
-
   integration_build:
     needs: changelog
     runs-on: ubuntu-latest
diff --git a/Cargo.toml b/Cargo.toml
index 78409c7a09e29..b48b881097f47 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -30,8 +30,11 @@ color-print = "0.3.4"
 anstream = "0.6.0"
 
 [dev-dependencies]
+cargo_metadata = "0.18.1"
 ui_test = "0.25"
 regex = "1.5.5"
+serde = { version = "1.0.145", features = ["derive"] }
+serde_json = "1.0.122"
 toml = "0.7.3"
 walkdir = "2.3"
 filetime = "0.2.9"
@@ -41,7 +44,6 @@ itertools = "0.12"
 clippy_utils = { path = "clippy_utils" }
 if_chain = "1.0"
 quote = "1.0.25"
-serde = { version = "1.0.145", features = ["derive"] }
 syn = { version = "2.0", features = ["full"] }
 futures = "0.3"
 parking_lot = "0.12"
diff --git a/book/src/development/adding_lints.md b/book/src/development/adding_lints.md
index a71d94daca74c..963e02e5c1616 100644
--- a/book/src/development/adding_lints.md
+++ b/book/src/development/adding_lints.md
@@ -739,7 +739,7 @@ for some users. Adding a configuration is done in the following steps:
 
 5. Update [Lint Configuration](../lint_configuration.md)
 
-   Run `cargo collect-metadata` to generate documentation changes for the book.
+   Run `cargo bless --test config-metadata` to generate documentation changes for the book.
 
 [`clippy_config::conf`]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_config/src/conf.rs
 [`clippy_lints` lib file]: https://github.com/rust-lang/rust-clippy/blob/master/clippy_lints/src/lib.rs
diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md
index e3d550b146629..4f23afe2e118a 100644
--- a/book/src/lint_configuration.md
+++ b/book/src/lint_configuration.md
@@ -1,5 +1,5 @@
 <!--
-This file is generated by `cargo collect-metadata`.
+This file is generated by `cargo bless --test config-metadata`.
 Please use that command to update the file and do not edit it by hand.
 -->
 
@@ -949,5 +949,3 @@ Whether to also emit warnings for unsafe blocks with metavariable expansions in
 ---
 **Affected lints:**
 * [`macro_metavars_in_unsafe`](https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe)
-
-
diff --git a/clippy_dev/src/update_lints.rs b/clippy_dev/src/update_lints.rs
index 15578d69c3a9d..8dbda1c634c28 100644
--- a/clippy_dev/src/update_lints.rs
+++ b/clippy_dev/src/update_lints.rs
@@ -604,7 +604,7 @@ fn gen_declared_lints<'a>(
     details.sort_unstable();
 
     let mut output = GENERATED_FILE_COMMENT.to_string();
-    output.push_str("pub(crate) static LINTS: &[&crate::LintInfo] = &[\n");
+    output.push_str("pub static LINTS: &[&crate::LintInfo] = &[\n");
 
     for (is_public, module_name, lint_name) in details {
         if !is_public {
diff --git a/clippy_lints/src/declared_lints.rs b/clippy_lints/src/declared_lints.rs
index 3fb083dd83310..8754a4dff87b1 100644
--- a/clippy_lints/src/declared_lints.rs
+++ b/clippy_lints/src/declared_lints.rs
@@ -2,7 +2,7 @@
 // Use that command to update this file and do not edit by hand.
 // Manual edits will be overwritten.
 
-pub(crate) static LINTS: &[&crate::LintInfo] = &[
+pub static LINTS: &[&crate::LintInfo] = &[
     #[cfg(feature = "internal")]
     crate::utils::internal_lints::almost_standard_lint_formulation::ALMOST_STANDARD_LINT_FORMULATION_INFO,
     #[cfg(feature = "internal")]
@@ -22,8 +22,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     #[cfg(feature = "internal")]
     crate::utils::internal_lints::lint_without_lint_pass::MISSING_CLIPPY_VERSION_ATTRIBUTE_INFO,
     #[cfg(feature = "internal")]
-    crate::utils::internal_lints::metadata_collector::METADATA_COLLECTOR_INFO,
-    #[cfg(feature = "internal")]
     crate::utils::internal_lints::msrv_attr_impl::MISSING_MSRV_ATTR_IMPL_INFO,
     #[cfg(feature = "internal")]
     crate::utils::internal_lints::outer_expn_data_pass::OUTER_EXPN_EXPN_DATA_INFO,
diff --git a/clippy_lints/src/lib.rs b/clippy_lints/src/lib.rs
index ce13a9afef5b0..2ac06b360bea4 100644
--- a/clippy_lints/src/lib.rs
+++ b/clippy_lints/src/lib.rs
@@ -66,8 +66,8 @@ extern crate declare_clippy_lint;
 #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
 mod utils;
 
-mod declared_lints;
-mod deprecated_lints;
+pub mod declared_lints;
+pub mod deprecated_lints;
 
 // begin lints modules, do not remove this comment, it’s used in `update_lints`
 mod absolute_paths;
@@ -440,7 +440,7 @@ impl RegistrationGroups {
     }
 }
 
-#[derive(Copy, Clone)]
+#[derive(Copy, Clone, Debug)]
 pub(crate) enum LintCategory {
     Cargo,
     Complexity,
@@ -479,11 +479,39 @@ impl LintCategory {
     }
 }
 
-pub(crate) struct LintInfo {
+pub struct LintInfo {
     /// Double reference to maintain pointer equality
-    lint: &'static &'static Lint,
+    pub lint: &'static &'static Lint,
     category: LintCategory,
-    explanation: &'static str,
+    pub explanation: &'static str,
+    /// e.g. `clippy_lints/src/absolute_paths.rs#43`
+    pub location: &'static str,
+    pub version: Option<&'static str>,
+}
+
+impl LintInfo {
+    /// Returns the lint name in lowercase without the `clippy::` prefix
+    #[allow(clippy::missing_panics_doc)]
+    pub fn name_lower(&self) -> String {
+        self.lint.name.strip_prefix("clippy::").unwrap().to_ascii_lowercase()
+    }
+
+    /// Returns the name of the lint's category in lowercase (`style`, `pedantic`)
+    pub fn category_str(&self) -> &'static str {
+        match self.category {
+            Cargo => "cargo",
+            Complexity => "complexity",
+            Correctness => "correctness",
+            Nursery => "nursery",
+            Pedantic => "pedantic",
+            Perf => "perf",
+            Restriction => "restriction",
+            Style => "style",
+            Suspicious => "suspicious",
+            #[cfg(feature = "internal")]
+            Internal => "internal",
+        }
+    }
 }
 
 pub fn explain(name: &str) -> i32 {
@@ -538,14 +566,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
         store.register_removed(name, reason);
     }
 
-    #[cfg(feature = "internal")]
-    {
-        if std::env::var("ENABLE_METADATA_COLLECTION").eq(&Ok("1".to_string())) {
-            store.register_late_pass(|_| Box::new(utils::internal_lints::metadata_collector::MetadataCollector::new()));
-            return;
-        }
-    }
-
     let format_args_storage = FormatArgsStorage::default();
     let format_args = format_args_storage.clone();
     store.register_early_pass(move || {
diff --git a/clippy_lints/src/utils/internal_lints.rs b/clippy_lints/src/utils/internal_lints.rs
index 1d294c2944b2c..f662c7651f6bb 100644
--- a/clippy_lints/src/utils/internal_lints.rs
+++ b/clippy_lints/src/utils/internal_lints.rs
@@ -3,7 +3,6 @@ pub mod collapsible_calls;
 pub mod interning_defined_symbol;
 pub mod invalid_paths;
 pub mod lint_without_lint_pass;
-pub mod metadata_collector;
 pub mod msrv_attr_impl;
 pub mod outer_expn_data_pass;
 pub mod produce_ice;
diff --git a/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/clippy_lints/src/utils/internal_lints/metadata_collector.rs
deleted file mode 100644
index c73ec262637f7..0000000000000
--- a/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ /dev/null
@@ -1,1080 +0,0 @@
-//! This lint is used to collect metadata about clippy lints. This metadata is exported as a json
-//! file and then used to generate the [clippy lint list](https://rust-lang.github.io/rust-clippy/master/index.html)
-//!
-//! This module and therefore the entire lint is guarded by a feature flag called `internal`
-//!
-//! The module transforms all lint names to ascii lowercase to ensure that we don't have mismatches
-//! during any comparison or mapping. (Please take care of this, it's not fun to spend time on such
-//! a simple mistake)
-
-use crate::utils::internal_lints::lint_without_lint_pass::{extract_clippy_version_value, is_lint_ref_type};
-use clippy_config::{get_configuration_metadata, ClippyConfiguration};
-
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::ty::{match_type, walk_ptrs_ty_depth};
-use clippy_utils::{last_path_segment, match_function_call, match_path, paths};
-use itertools::Itertools;
-use rustc_ast as ast;
-use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::def::DefKind;
-use rustc_hir::intravisit::Visitor;
-use rustc_hir::{self as hir, intravisit, Closure, ExprKind, Item, ItemKind, Mutability, QPath};
-use rustc_lint::{unerased_lint_store, CheckLintNameResult, LateContext, LateLintPass, LintContext, LintId};
-use rustc_middle::hir::nested_filter;
-use rustc_session::impl_lint_pass;
-use rustc_span::symbol::Ident;
-use rustc_span::{sym, Loc, Span, Symbol};
-use serde::ser::SerializeStruct;
-use serde::{Serialize, Serializer};
-use std::collections::{BTreeSet, BinaryHeap};
-use std::fmt::Write as _;
-use std::fs::{self, File};
-use std::io::prelude::*;
-use std::path::{Path, PathBuf};
-use std::process::Command;
-use std::{env, fmt};
-
-/// This is the json output file of the lint collector.
-const JSON_OUTPUT_FILE: &str = "../util/gh-pages/lints.json";
-/// This is the markdown output file of the lint collector.
-const MARKDOWN_OUTPUT_FILE: &str = "../book/src/lint_configuration.md";
-/// These groups will be ignored by the lint group matcher. This is useful for collections like
-/// `clippy::all`
-const IGNORED_LINT_GROUPS: [&str; 1] = ["clippy::all"];
-/// Lints within this group will be excluded from the collection. These groups
-/// have to be defined without the `clippy::` prefix.
-const EXCLUDED_LINT_GROUPS: [&str; 1] = ["internal"];
-/// Collected deprecated lint will be assigned to this group in the JSON output
-const DEPRECATED_LINT_GROUP_STR: &str = "deprecated";
-/// This is the lint level for deprecated lints that will be displayed in the lint list
-const DEPRECATED_LINT_LEVEL: &str = "none";
-/// This array holds Clippy's lint groups with their corresponding default lint level. The
-/// lint level for deprecated lints is set in `DEPRECATED_LINT_LEVEL`.
-const DEFAULT_LINT_LEVELS: &[(&str, &str)] = &[
-    ("correctness", "deny"),
-    ("suspicious", "warn"),
-    ("restriction", "allow"),
-    ("style", "warn"),
-    ("pedantic", "allow"),
-    ("complexity", "warn"),
-    ("perf", "warn"),
-    ("cargo", "allow"),
-    ("nursery", "allow"),
-];
-/// This prefix is in front of the lint groups in the lint store. The prefix will be trimmed
-/// to only keep the actual lint group in the output.
-const CLIPPY_LINT_GROUP_PREFIX: &str = "clippy::";
-const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [
-    &["clippy_utils", "diagnostics", "span_lint"],
-    &["clippy_utils", "diagnostics", "span_lint_and_help"],
-    &["clippy_utils", "diagnostics", "span_lint_and_note"],
-    &["clippy_utils", "diagnostics", "span_lint_hir"],
-    &["clippy_utils", "diagnostics", "span_lint_and_sugg"],
-    &["clippy_utils", "diagnostics", "span_lint_and_then"],
-    &["clippy_utils", "diagnostics", "span_lint_hir_and_then"],
-];
-const SUGGESTION_DIAG_METHODS: [(&str, bool); 9] = [
-    ("span_suggestion", false),
-    ("span_suggestion_short", false),
-    ("span_suggestion_verbose", false),
-    ("span_suggestion_hidden", false),
-    ("tool_only_span_suggestion", false),
-    ("multipart_suggestion", true),
-    ("multipart_suggestions", true),
-    ("tool_only_multipart_suggestion", true),
-    ("span_suggestions", true),
-];
-
-/// The index of the applicability name of `paths::APPLICABILITY_VALUES`
-const APPLICABILITY_NAME_INDEX: usize = 2;
-/// This applicability will be set for unresolved applicability values.
-const APPLICABILITY_UNRESOLVED_STR: &str = "Unresolved";
-/// The version that will be displayed if none has been defined
-const VERSION_DEFAULT_STR: &str = "Unknown";
-
-const CHANGELOG_PATH: &str = "../CHANGELOG.md";
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Collects metadata about clippy lints for the website.
-    ///
-    /// This lint will be used to report problems of syntax parsing. You should hopefully never
-    /// see this but never say never I guess ^^
-    ///
-    /// ### Why is this bad?
-    /// This is not a bad thing but definitely a hacky way to do it. See
-    /// issue [#4310](https://github.com/rust-lang/rust-clippy/issues/4310) for a discussion
-    /// about the implementation.
-    ///
-    /// ### Known problems
-    /// Hopefully none. It would be pretty uncool to have a problem here :)
-    ///
-    /// ### Example output
-    /// ```json,ignore
-    /// {
-    ///     "id": "metadata_collector",
-    ///     "id_span": {
-    ///         "path": "clippy_lints/src/utils/internal_lints/metadata_collector.rs",
-    ///         "line": 1
-    ///     },
-    ///     "group": "clippy::internal",
-    ///     "docs": " ### What it does\nCollects metadata about clippy lints for the website. [...] "
-    /// }
-    /// ```
-    #[clippy::version = "1.56.0"]
-    pub METADATA_COLLECTOR,
-    internal,
-    "A busy bee collection metadata about lints"
-}
-
-impl_lint_pass!(MetadataCollector => [METADATA_COLLECTOR]);
-
-#[allow(clippy::module_name_repetitions)]
-#[derive(Debug, Clone)]
-pub struct MetadataCollector {
-    /// All collected lints
-    ///
-    /// We use a Heap here to have the lints added in alphabetic order in the export
-    lints: BinaryHeap<LintMetadata>,
-    applicability_info: FxHashMap<String, ApplicabilityInfo>,
-    config: Vec<ClippyConfiguration>,
-    clippy_project_root: PathBuf,
-}
-
-impl MetadataCollector {
-    pub fn new() -> Self {
-        Self {
-            lints: BinaryHeap::<LintMetadata>::default(),
-            applicability_info: FxHashMap::<String, ApplicabilityInfo>::default(),
-            config: get_configuration_metadata(),
-            clippy_project_root: env::current_dir()
-                .expect("failed to get current dir")
-                .ancestors()
-                .nth(1)
-                .expect("failed to get project root")
-                .to_path_buf(),
-        }
-    }
-
-    fn get_lint_configs(&self, lint_name: &str) -> Option<String> {
-        self.config
-            .iter()
-            .filter(|config| config.lints.iter().any(|&lint| lint == lint_name))
-            .map(ToString::to_string)
-            .reduce(|acc, x| acc + "\n\n" + &x)
-            .map(|configurations| {
-                format!(
-                    r#"
-### Configuration
-This lint has the following configuration variables:
-
-{configurations}
-"#
-                )
-            })
-    }
-
-    fn configs_to_markdown(&self, map_fn: fn(&ClippyConfiguration) -> String) -> String {
-        self.config
-            .iter()
-            .filter(|config| config.deprecation_reason.is_none())
-            .filter(|config| !config.lints.is_empty())
-            .map(map_fn)
-            .join("\n")
-    }
-
-    fn get_markdown_docs(&self) -> String {
-        format!(
-            r#"# Lint Configuration Options
-
-The following list shows each configuration option, along with a description, its default value, an example
-and lints affected.
-
----
-
-{}"#,
-            self.configs_to_markdown(ClippyConfiguration::to_markdown_paragraph),
-        )
-    }
-}
-
-impl Drop for MetadataCollector {
-    /// You might ask: How hacky is this?
-    /// My answer:     YES
-    fn drop(&mut self) {
-        // The metadata collector gets dropped twice, this makes sure that we only write
-        // when the list is full
-        if self.lints.is_empty() {
-            return;
-        }
-
-        let mut applicability_info = std::mem::take(&mut self.applicability_info);
-
-        // Add deprecated lints
-        self.lints.extend(
-            crate::deprecated_lints::DEPRECATED
-                .iter()
-                .zip(crate::deprecated_lints::DEPRECATED_VERSION)
-                .filter_map(|((lint, reason), version)| LintMetadata::new_deprecated(lint, reason, version)),
-        );
-        // Mapping the final data
-        let mut lints = std::mem::take(&mut self.lints).into_sorted_vec();
-        for x in &mut lints {
-            x.applicability = Some(applicability_info.remove(&x.id).unwrap_or_default());
-            replace_produces(&x.id, &mut x.docs, &self.clippy_project_root);
-        }
-
-        collect_renames(&mut lints);
-
-        // Outputting json
-        fs::write(JSON_OUTPUT_FILE, serde_json::to_string_pretty(&lints).unwrap()).unwrap();
-
-        // Outputting markdown
-        let mut file = File::create(MARKDOWN_OUTPUT_FILE).unwrap();
-        writeln!(
-            file,
-            "<!--
-This file is generated by `cargo collect-metadata`.
-Please use that command to update the file and do not edit it by hand.
--->
-
-{}",
-            self.get_markdown_docs(),
-        )
-        .unwrap();
-
-        // Write configuration links to CHANGELOG.md
-        let changelog = fs::read_to_string(CHANGELOG_PATH).unwrap();
-        let mut changelog_file = File::create(CHANGELOG_PATH).unwrap();
-        let position = changelog
-            .find("<!-- begin autogenerated links to configuration documentation -->")
-            .unwrap();
-        writeln!(
-            changelog_file,
-            "{}<!-- begin autogenerated links to configuration documentation -->\n{}\n<!-- end autogenerated links to configuration documentation -->",
-            &changelog[..position],
-            self.configs_to_markdown(ClippyConfiguration::to_markdown_link)
-        )
-        .unwrap();
-    }
-}
-
-#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
-struct LintMetadata {
-    id: String,
-    id_span: Option<SerializableSpan>,
-    group: String,
-    level: String,
-    docs: String,
-    version: String,
-    /// This field is only used in the output and will only be
-    /// mapped shortly before the actual output.
-    applicability: Option<ApplicabilityInfo>,
-    /// All the past names of lints which have been renamed.
-    #[serde(skip_serializing_if = "BTreeSet::is_empty")]
-    former_ids: BTreeSet<String>,
-}
-
-impl LintMetadata {
-    fn new(
-        id: String,
-        id_span: SerializableSpan,
-        group: String,
-        level: &'static str,
-        version: String,
-        docs: String,
-    ) -> Self {
-        Self {
-            id,
-            id_span: Some(id_span),
-            group,
-            level: level.to_string(),
-            version,
-            docs,
-            applicability: None,
-            former_ids: BTreeSet::new(),
-        }
-    }
-
-    fn new_deprecated(name: &str, reason: &str, version: &str) -> Option<Self> {
-        // The reason starts with a lowercase letter and end without a period.
-        // This needs to be fixed for the website.
-        let mut reason = reason.to_owned();
-        if let Some(reason) = reason.get_mut(0..1) {
-            reason.make_ascii_uppercase();
-        }
-        name.strip_prefix("clippy::").map(|name| Self {
-            id: name.into(),
-            id_span: None,
-            group: DEPRECATED_LINT_GROUP_STR.into(),
-            level: DEPRECATED_LINT_LEVEL.into(),
-            version: version.into(),
-            docs: format!(
-                "### What it does\n\n\
-                Nothing. This lint has been deprecated\n\n\
-                ### Deprecation reason\n\n{reason}.\n",
-            ),
-            applicability: None,
-            former_ids: BTreeSet::new(),
-        })
-    }
-}
-
-fn replace_produces(lint_name: &str, docs: &mut String, clippy_project_root: &Path) {
-    let mut doc_lines = docs.lines().map(ToString::to_string).collect::<Vec<_>>();
-    let mut lines = doc_lines.iter_mut();
-
-    'outer: loop {
-        // Find the start of the example
-
-        // ```rust
-        loop {
-            match lines.next() {
-                Some(line) if line.trim_start().starts_with("```rust") => {
-                    if line.contains("ignore") || line.contains("no_run") {
-                        // A {{produces}} marker may have been put on a ignored code block by mistake,
-                        // just seek to the end of the code block and continue checking.
-                        if lines.any(|line| line.trim_start().starts_with("```")) {
-                            continue;
-                        }
-
-                        panic!("lint `{lint_name}` has an unterminated code block")
-                    }
-
-                    break;
-                },
-                Some(line) if line.trim_start() == "{{produces}}" => {
-                    panic!("lint `{lint_name}` has marker {{{{produces}}}} with an ignored or missing code block")
-                },
-                Some(line) => {
-                    let line = line.trim();
-                    // These are the two most common markers of the corrections section
-                    if line.eq_ignore_ascii_case("Use instead:") || line.eq_ignore_ascii_case("Could be written as:") {
-                        break 'outer;
-                    }
-                },
-                None => break 'outer,
-            }
-        }
-
-        // Collect the example
-        let mut example = Vec::new();
-        loop {
-            match lines.next() {
-                Some(line) if line.trim_start() == "```" => break,
-                Some(line) => example.push(line),
-                None => panic!("lint `{lint_name}` has an unterminated code block"),
-            }
-        }
-
-        // Find the {{produces}} and attempt to generate the output
-        loop {
-            match lines.next() {
-                Some(line) if line.is_empty() => {},
-                Some(line) if line.trim() == "{{produces}}" => {
-                    let output = get_lint_output(lint_name, &example, clippy_project_root);
-                    line.replace_range(
-                        ..,
-                        &format!(
-                            "<details>\
-                            <summary>Produces</summary>\n\
-                            \n\
-                            ```text\n\
-                            {output}\n\
-                            ```\n\
-                        </details>"
-                        ),
-                    );
-
-                    break;
-                },
-                // No {{produces}}, we can move on to the next example
-                Some(_) => break,
-                None => break 'outer,
-            }
-        }
-    }
-
-    *docs = cleanup_docs(&doc_lines);
-}
-
-fn get_lint_output(lint_name: &str, example: &[&mut String], clippy_project_root: &Path) -> String {
-    let dir = tempfile::tempdir().unwrap_or_else(|e| panic!("failed to create temp dir: {e}"));
-    let file = dir.path().join("lint_example.rs");
-
-    let mut source = String::new();
-    let unhidden = example
-        .iter()
-        .map(|line| line.trim_start().strip_prefix("# ").unwrap_or(line));
-
-    // Get any attributes
-    let mut lines = unhidden.peekable();
-    while let Some(line) = lines.peek() {
-        if line.starts_with("#!") {
-            source.push_str(line);
-            source.push('\n');
-            lines.next();
-        } else {
-            break;
-        }
-    }
-
-    let needs_main = !example.iter().any(|line| line.contains("fn main"));
-    if needs_main {
-        source.push_str("fn main() {\n");
-    }
-
-    for line in lines {
-        source.push_str(line);
-        source.push('\n');
-    }
-
-    if needs_main {
-        source.push_str("}\n");
-    }
-
-    if let Err(e) = fs::write(&file, &source) {
-        panic!("failed to write to `{}`: {e}", file.as_path().to_string_lossy());
-    }
-
-    let prefixed_name = format!("{CLIPPY_LINT_GROUP_PREFIX}{lint_name}");
-
-    let mut cmd = Command::new(env::var("CARGO").unwrap_or("cargo".into()));
-
-    cmd.current_dir(clippy_project_root)
-        .env("CARGO_INCREMENTAL", "0")
-        .env("CLIPPY_ARGS", "")
-        .env("CLIPPY_DISABLE_DOCS_LINKS", "1")
-        // We need to disable this to enable all lints
-        .env("ENABLE_METADATA_COLLECTION", "0")
-        .args(["run", "--bin", "clippy-driver"])
-        .args(["--target-dir", "./clippy_lints/target"])
-        .args(["--", "--error-format=json"])
-        .args(["--edition", "2021"])
-        .arg("-Cdebuginfo=0")
-        .args(["-A", "clippy::all"])
-        .args(["-W", &prefixed_name])
-        .args(["-L", "./target/debug"])
-        .args(["-Z", "no-codegen"]);
-
-    let output = cmd
-        .arg(file.as_path())
-        .output()
-        .unwrap_or_else(|e| panic!("failed to run `{cmd:?}`: {e}"));
-
-    let tmp_file_path = file.to_string_lossy();
-    let stderr = std::str::from_utf8(&output.stderr).unwrap();
-    let msgs = stderr
-        .lines()
-        .filter(|line| line.starts_with('{'))
-        .map(|line| serde_json::from_str(line).unwrap())
-        .collect::<Vec<serde_json::Value>>();
-
-    let mut rendered = String::new();
-    let iter = msgs
-        .iter()
-        .filter(|msg| matches!(&msg["code"]["code"], serde_json::Value::String(s) if s == &prefixed_name));
-
-    for message in iter {
-        let rendered_part = message["rendered"].as_str().expect("rendered field should exist");
-        rendered.push_str(rendered_part);
-    }
-
-    if rendered.is_empty() {
-        let rendered: Vec<&str> = msgs.iter().filter_map(|msg| msg["rendered"].as_str()).collect();
-        let non_json: Vec<&str> = stderr.lines().filter(|line| !line.starts_with('{')).collect();
-        panic!(
-            "did not find lint `{lint_name}` in output of example, got:\n{}\n{}",
-            non_json.join("\n"),
-            rendered.join("\n")
-        );
-    }
-
-    // The reader doesn't need to see `/tmp/.tmpfiy2Qd/lint_example.rs` :)
-    rendered.trim_end().replace(&*tmp_file_path, "lint_example.rs")
-}
-
-#[derive(Debug, Clone, Serialize, PartialEq, Eq, PartialOrd, Ord)]
-struct SerializableSpan {
-    path: String,
-    line: usize,
-}
-
-impl fmt::Display for SerializableSpan {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(f, "{}:{}", self.path.rsplit('/').next().unwrap_or_default(), self.line)
-    }
-}
-
-impl SerializableSpan {
-    fn from_item(cx: &LateContext<'_>, item: &Item<'_>) -> Self {
-        Self::from_span(cx, item.ident.span)
-    }
-
-    fn from_span(cx: &LateContext<'_>, span: Span) -> Self {
-        let loc: Loc = cx.sess().source_map().lookup_char_pos(span.lo());
-
-        Self {
-            path: format!("{}", loc.file.name.prefer_remapped_unconditionaly()),
-            line: loc.line,
-        }
-    }
-}
-
-#[derive(Debug, Clone, Default, PartialEq, Eq, PartialOrd, Ord)]
-struct ApplicabilityInfo {
-    /// Indicates if any of the lint emissions uses multiple spans. This is related to
-    /// [rustfix#141](https://github.com/rust-lang/rustfix/issues/141) as such suggestions can
-    /// currently not be applied automatically.
-    is_multi_part_suggestion: bool,
-    applicability: Option<usize>,
-}
-
-impl Serialize for ApplicabilityInfo {
-    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-    where
-        S: Serializer,
-    {
-        let mut s = serializer.serialize_struct("ApplicabilityInfo", 2)?;
-        s.serialize_field("is_multi_part_suggestion", &self.is_multi_part_suggestion)?;
-        if let Some(index) = self.applicability {
-            s.serialize_field(
-                "applicability",
-                &paths::APPLICABILITY_VALUES[index][APPLICABILITY_NAME_INDEX],
-            )?;
-        } else {
-            s.serialize_field("applicability", APPLICABILITY_UNRESOLVED_STR)?;
-        }
-        s.end()
-    }
-}
-
-// ==================================================================
-// Lint pass
-// ==================================================================
-impl<'hir> LateLintPass<'hir> for MetadataCollector {
-    /// Collecting lint declarations like:
-    /// ```rust, ignore
-    /// declare_clippy_lint! {
-    ///     /// ### What it does
-    ///     /// Something IDK.
-    ///     pub SOME_LINT,
-    ///     internal,
-    ///     "Who am I?"
-    /// }
-    /// ```
-    fn check_item(&mut self, cx: &LateContext<'hir>, item: &'hir Item<'_>) {
-        if let ItemKind::Static(ty, Mutability::Not, _) = item.kind {
-            // Normal lint
-            if is_lint_ref_type(cx, ty)
-                // item validation
-                // disallow check
-                && let lint_name = sym_to_string(item.ident.name).to_ascii_lowercase()
-                // metadata extraction
-                && let Some((group, level)) = get_lint_group_and_level_or_lint(cx, &lint_name, item)
-                && let Some(mut raw_docs) = extract_attr_docs_or_lint(cx, item)
-            {
-                if let Some(configuration_section) = self.get_lint_configs(&lint_name) {
-                    raw_docs.push_str(&configuration_section);
-                }
-                let version = get_lint_version(cx, item);
-
-                self.lints.push(LintMetadata::new(
-                    lint_name,
-                    SerializableSpan::from_item(cx, item),
-                    group,
-                    level,
-                    version,
-                    raw_docs,
-                ));
-            }
-        }
-    }
-
-    /// Collecting constant applicability from the actual lint emissions
-    ///
-    /// Example:
-    /// ```rust, ignore
-    /// span_lint_and_sugg(
-    ///     cx,
-    ///     SOME_LINT,
-    ///     item.span,
-    ///     "Le lint message",
-    ///     "Here comes help:",
-    ///     "#![allow(clippy::all)]",
-    ///     Applicability::MachineApplicable, // <-- Extracts this constant value
-    /// );
-    /// ```
-    fn check_expr(&mut self, cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) {
-        if let Some(args) = match_lint_emission(cx, expr) {
-            let emission_info = extract_emission_info(cx, args);
-            if emission_info.is_empty() {
-                // See:
-                // - src/misc.rs:734:9
-                // - src/methods/mod.rs:3545:13
-                // - src/methods/mod.rs:3496:13
-                // We are basically unable to resolve the lint name itself.
-                return;
-            }
-
-            for (lint_name, applicability, is_multi_part) in emission_info {
-                let app_info = self.applicability_info.entry(lint_name).or_default();
-                app_info.applicability = applicability;
-                app_info.is_multi_part_suggestion = is_multi_part;
-            }
-        }
-    }
-}
-
-// ==================================================================
-// Lint definition extraction
-// ==================================================================
-fn sym_to_string(sym: Symbol) -> String {
-    sym.as_str().to_string()
-}
-
-fn extract_attr_docs_or_lint(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
-    extract_attr_docs(cx, item).or_else(|| {
-        lint_collection_error_item(cx, item, "could not collect the lint documentation");
-        None
-    })
-}
-
-/// This function collects all documentation that has been added to an item using
-/// `#[doc = r""]` attributes. Several attributes are aggravated using line breaks
-///
-/// ```ignore
-/// #[doc = r"Hello world!"]
-/// #[doc = r"=^.^="]
-/// struct SomeItem {}
-/// ```
-///
-/// Would result in `Hello world!\n=^.^=\n`
-fn extract_attr_docs(cx: &LateContext<'_>, item: &Item<'_>) -> Option<String> {
-    let attrs = cx.tcx.hir().attrs(item.hir_id());
-    let mut lines = attrs.iter().filter_map(ast::Attribute::doc_str);
-
-    if let Some(line) = lines.next() {
-        let raw_docs = lines.fold(String::from(line.as_str()) + "\n", |s, line| s + line.as_str() + "\n");
-        return Some(raw_docs);
-    }
-
-    None
-}
-
-/// This function may modify the doc comment to ensure that the string can be displayed using a
-/// markdown viewer in Clippy's lint list. The following modifications could be applied:
-/// * Removal of leading space after a new line. (Important to display tables)
-/// * Ensures that code blocks only contain language information
-fn cleanup_docs(docs_collection: &Vec<String>) -> String {
-    let mut in_code_block = false;
-    let mut is_code_block_rust = false;
-
-    let mut docs = String::new();
-    for line in docs_collection {
-        // Rustdoc hides code lines starting with `# ` and this removes them from Clippy's lint list :)
-        if is_code_block_rust && line.trim_start().starts_with("# ") {
-            continue;
-        }
-
-        // The line should be represented in the lint list, even if it's just an empty line
-        docs.push('\n');
-        if let Some(info) = line.trim_start().strip_prefix("```") {
-            in_code_block = !in_code_block;
-            is_code_block_rust = false;
-            if in_code_block {
-                let lang = info
-                    .trim()
-                    .split(',')
-                    // remove rustdoc directives
-                    .find(|&s| !matches!(s, "" | "ignore" | "no_run" | "should_panic" | "compile_fail"))
-                    // if no language is present, fill in "rust"
-                    .unwrap_or("rust");
-                let len_diff = line
-                    .strip_prefix(' ')
-                    .map_or(0, |line| line.len() - line.trim_start().len());
-                if len_diff != 0 {
-                    // We put back the indentation.
-                    docs.push_str(&line[..len_diff]);
-                }
-                docs.push_str("```");
-                docs.push_str(lang);
-
-                is_code_block_rust = lang == "rust";
-                continue;
-            }
-        }
-        // This removes the leading space that the macro translation introduces
-        if let Some(stripped_doc) = line.strip_prefix(' ') {
-            docs.push_str(stripped_doc);
-        } else if !line.is_empty() {
-            docs.push_str(line);
-        }
-    }
-
-    docs
-}
-
-fn get_lint_version(cx: &LateContext<'_>, item: &Item<'_>) -> String {
-    extract_clippy_version_value(cx, item).map_or_else(
-        || VERSION_DEFAULT_STR.to_string(),
-        |version| version.as_str().to_string(),
-    )
-}
-
-fn get_lint_group_and_level_or_lint(
-    cx: &LateContext<'_>,
-    lint_name: &str,
-    item: &Item<'_>,
-) -> Option<(String, &'static str)> {
-    let result = unerased_lint_store(cx.tcx.sess).check_lint_name(
-        lint_name,
-        Some(sym::clippy),
-        &std::iter::once(Ident::with_dummy_span(sym::clippy)).collect(),
-    );
-    if let CheckLintNameResult::Tool(lint_lst, None) = result {
-        if let Some(group) = get_lint_group(cx, lint_lst[0]) {
-            if EXCLUDED_LINT_GROUPS.contains(&group.as_str()) {
-                return None;
-            }
-
-            if let Some(level) = get_lint_level_from_group(&group) {
-                Some((group, level))
-            } else {
-                lint_collection_error_item(
-                    cx,
-                    item,
-                    &format!("Unable to determine lint level for found group `{group}`"),
-                );
-                None
-            }
-        } else {
-            lint_collection_error_item(cx, item, "Unable to determine lint group");
-            None
-        }
-    } else {
-        lint_collection_error_item(cx, item, "Unable to find lint in lint_store");
-        None
-    }
-}
-
-fn get_lint_group(cx: &LateContext<'_>, lint_id: LintId) -> Option<String> {
-    for (group_name, lints, _) in unerased_lint_store(cx.tcx.sess).get_lint_groups() {
-        if IGNORED_LINT_GROUPS.contains(&group_name) {
-            continue;
-        }
-
-        if lints.iter().any(|group_lint| *group_lint == lint_id) {
-            let group = group_name.strip_prefix(CLIPPY_LINT_GROUP_PREFIX).unwrap_or(group_name);
-            return Some((*group).to_string());
-        }
-    }
-
-    None
-}
-
-fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> {
-    DEFAULT_LINT_LEVELS
-        .iter()
-        .find_map(|(group_name, group_level)| (*group_name == lint_group).then_some(*group_level))
-}
-
-fn collect_renames(lints: &mut Vec<LintMetadata>) {
-    for lint in lints {
-        let mut collected = String::new();
-        let mut names = vec![lint.id.clone()];
-
-        loop {
-            if let Some(lint_name) = names.pop() {
-                for (k, v) in crate::deprecated_lints::RENAMED {
-                    if let Some(name) = v.strip_prefix(CLIPPY_LINT_GROUP_PREFIX)
-                        && name == lint_name
-                        && let Some(past_name) = k.strip_prefix(CLIPPY_LINT_GROUP_PREFIX)
-                    {
-                        lint.former_ids.insert(past_name.to_owned());
-                        writeln!(collected, "* `{past_name}`").unwrap();
-                        names.push(past_name.to_string());
-                    }
-                }
-
-                continue;
-            }
-
-            break;
-        }
-
-        if !collected.is_empty() {
-            write!(
-                &mut lint.docs,
-                r#"
-### Past names
-
-{collected}
-"#
-            )
-            .unwrap();
-        }
-    }
-}
-
-// ==================================================================
-// Lint emission
-// ==================================================================
-fn lint_collection_error_item(cx: &LateContext<'_>, item: &Item<'_>, message: &str) {
-    span_lint(
-        cx,
-        METADATA_COLLECTOR,
-        item.ident.span,
-        format!("metadata collection error for `{}`: {message}", item.ident.name),
-    );
-}
-
-// ==================================================================
-// Applicability
-// ==================================================================
-/// This function checks if a given expression is equal to a simple lint emission function call.
-/// It will return the function arguments if the emission matched any function.
-fn match_lint_emission<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'_>) -> Option<&'hir [hir::Expr<'hir>]> {
-    LINT_EMISSION_FUNCTIONS
-        .iter()
-        .find_map(|emission_fn| match_function_call(cx, expr, emission_fn))
-}
-
-fn take_higher_applicability(a: Option<usize>, b: Option<usize>) -> Option<usize> {
-    a.map_or(b, |a| a.max(b.unwrap_or_default()).into())
-}
-
-fn extract_emission_info<'hir>(
-    cx: &LateContext<'hir>,
-    args: &'hir [hir::Expr<'hir>],
-) -> Vec<(String, Option<usize>, bool)> {
-    let mut lints = Vec::new();
-    let mut applicability = None;
-    let mut multi_part = false;
-
-    for arg in args {
-        let (arg_ty, _) = walk_ptrs_ty_depth(cx.typeck_results().expr_ty(arg));
-
-        if match_type(cx, arg_ty, &paths::LINT) {
-            // If we found the lint arg, extract the lint name
-            let mut resolved_lints = resolve_lints(cx, arg);
-            lints.append(&mut resolved_lints);
-        } else if match_type(cx, arg_ty, &paths::APPLICABILITY) {
-            applicability = resolve_applicability(cx, arg);
-        } else if arg_ty.is_closure() {
-            multi_part |= check_is_multi_part(cx, arg);
-            applicability = applicability.or_else(|| resolve_applicability(cx, arg));
-        }
-    }
-
-    lints
-        .into_iter()
-        .map(|lint_name| (lint_name, applicability, multi_part))
-        .collect()
-}
-
-/// Resolves the possible lints that this expression could reference
-fn resolve_lints<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Vec<String> {
-    let mut resolver = LintResolver::new(cx);
-    resolver.visit_expr(expr);
-    resolver.lints
-}
-
-/// This function tries to resolve the linked applicability to the given expression.
-fn resolve_applicability<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<usize> {
-    let mut resolver = ApplicabilityResolver::new(cx);
-    resolver.visit_expr(expr);
-    resolver.complete()
-}
-
-fn check_is_multi_part<'hir>(cx: &LateContext<'hir>, closure_expr: &'hir hir::Expr<'hir>) -> bool {
-    if let ExprKind::Closure(&Closure { body, .. }) = closure_expr.kind {
-        let mut scanner = IsMultiSpanScanner::new(cx);
-        intravisit::walk_body(&mut scanner, cx.tcx.hir().body(body));
-        return scanner.is_multi_part();
-    } else if let Some(local) = get_parent_local(cx, closure_expr) {
-        if let Some(local_init) = local.init {
-            return check_is_multi_part(cx, local_init);
-        }
-    }
-
-    false
-}
-
-struct LintResolver<'a, 'hir> {
-    cx: &'a LateContext<'hir>,
-    lints: Vec<String>,
-}
-
-impl<'a, 'hir> LintResolver<'a, 'hir> {
-    fn new(cx: &'a LateContext<'hir>) -> Self {
-        Self {
-            cx,
-            lints: Vec::<String>::default(),
-        }
-    }
-}
-
-impl<'a, 'hir> Visitor<'hir> for LintResolver<'a, 'hir> {
-    type NestedFilter = nested_filter::All;
-
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
-    }
-
-    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
-        if let ExprKind::Path(qpath) = &expr.kind
-            && let QPath::Resolved(_, path) = qpath
-            && let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr))
-            && match_type(self.cx, expr_ty, &paths::LINT)
-        {
-            if let hir::def::Res::Def(DefKind::Static { .. }, _) = path.res {
-                let lint_name = last_path_segment(qpath).ident.name;
-                self.lints.push(sym_to_string(lint_name).to_ascii_lowercase());
-            } else if let Some(local) = get_parent_local(self.cx, expr) {
-                if let Some(local_init) = local.init {
-                    intravisit::walk_expr(self, local_init);
-                }
-            }
-        }
-
-        intravisit::walk_expr(self, expr);
-    }
-}
-
-/// This visitor finds the highest applicability value in the visited expressions
-struct ApplicabilityResolver<'a, 'hir> {
-    cx: &'a LateContext<'hir>,
-    /// This is the index of highest `Applicability` for `paths::APPLICABILITY_VALUES`
-    applicability_index: Option<usize>,
-}
-
-impl<'a, 'hir> ApplicabilityResolver<'a, 'hir> {
-    fn new(cx: &'a LateContext<'hir>) -> Self {
-        Self {
-            cx,
-            applicability_index: None,
-        }
-    }
-
-    fn add_new_index(&mut self, new_index: usize) {
-        self.applicability_index = take_higher_applicability(self.applicability_index, Some(new_index));
-    }
-
-    fn complete(self) -> Option<usize> {
-        self.applicability_index
-    }
-}
-
-impl<'a, 'hir> Visitor<'hir> for ApplicabilityResolver<'a, 'hir> {
-    type NestedFilter = nested_filter::All;
-
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
-    }
-
-    fn visit_path(&mut self, path: &hir::Path<'hir>, _id: hir::HirId) {
-        for (index, enum_value) in paths::APPLICABILITY_VALUES.iter().enumerate() {
-            if match_path(path, enum_value) {
-                self.add_new_index(index);
-                return;
-            }
-        }
-    }
-
-    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
-        let (expr_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(expr));
-
-        if match_type(self.cx, expr_ty, &paths::APPLICABILITY)
-            && let Some(local) = get_parent_local(self.cx, expr)
-            && let Some(local_init) = local.init
-        {
-            intravisit::walk_expr(self, local_init);
-        };
-
-        intravisit::walk_expr(self, expr);
-    }
-}
-
-/// This returns the parent local node if the expression is a reference one
-fn get_parent_local<'hir>(cx: &LateContext<'hir>, expr: &'hir hir::Expr<'hir>) -> Option<&'hir hir::LetStmt<'hir>> {
-    if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind {
-        if let hir::def::Res::Local(local_hir) = path.res {
-            return get_parent_local_hir_id(cx, local_hir);
-        }
-    }
-
-    None
-}
-
-fn get_parent_local_hir_id<'hir>(cx: &LateContext<'hir>, hir_id: hir::HirId) -> Option<&'hir hir::LetStmt<'hir>> {
-    match cx.tcx.parent_hir_node(hir_id) {
-        hir::Node::LetStmt(local) => Some(local),
-        hir::Node::Pat(pattern) => get_parent_local_hir_id(cx, pattern.hir_id),
-        _ => None,
-    }
-}
-
-/// This visitor finds the highest applicability value in the visited expressions
-struct IsMultiSpanScanner<'a, 'hir> {
-    cx: &'a LateContext<'hir>,
-    suggestion_count: usize,
-}
-
-impl<'a, 'hir> IsMultiSpanScanner<'a, 'hir> {
-    fn new(cx: &'a LateContext<'hir>) -> Self {
-        Self {
-            cx,
-            suggestion_count: 0,
-        }
-    }
-
-    /// Add a new single expression suggestion to the counter
-    fn add_single_span_suggestion(&mut self) {
-        self.suggestion_count += 1;
-    }
-
-    /// Signals that a suggestion with possible multiple spans was found
-    fn add_multi_part_suggestion(&mut self) {
-        self.suggestion_count += 2;
-    }
-
-    /// Checks if the suggestions include multiple spans
-    fn is_multi_part(&self) -> bool {
-        self.suggestion_count > 1
-    }
-}
-
-impl<'a, 'hir> Visitor<'hir> for IsMultiSpanScanner<'a, 'hir> {
-    type NestedFilter = nested_filter::All;
-
-    fn nested_visit_map(&mut self) -> Self::Map {
-        self.cx.tcx.hir()
-    }
-
-    fn visit_expr(&mut self, expr: &'hir hir::Expr<'hir>) {
-        // Early return if the lint is already multi span
-        if self.is_multi_part() {
-            return;
-        }
-
-        if let ExprKind::MethodCall(path, recv, _, _arg_span) = &expr.kind {
-            let (self_ty, _) = walk_ptrs_ty_depth(self.cx.typeck_results().expr_ty(recv));
-            if match_type(self.cx, self_ty, &paths::DIAG) {
-                let called_method = path.ident.name.as_str().to_string();
-                for (method_name, is_multi_part) in &SUGGESTION_DIAG_METHODS {
-                    if *method_name == called_method {
-                        if *is_multi_part {
-                            self.add_multi_part_suggestion();
-                        } else {
-                            self.add_single_span_suggestion();
-                        }
-                        break;
-                    }
-                }
-            }
-        }
-
-        intravisit::walk_expr(self, expr);
-    }
-}
diff --git a/declare_clippy_lint/src/lib.rs b/declare_clippy_lint/src/lib.rs
index ca070f6c250bf..fd366b6b26277 100644
--- a/declare_clippy_lint/src/lib.rs
+++ b/declare_clippy_lint/src/lib.rs
@@ -1,4 +1,4 @@
-#![feature(let_chains)]
+#![feature(let_chains, proc_macro_span)]
 // warn on lints, that are included in `rust-lang/rust`s bootstrap
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
@@ -23,6 +23,7 @@ fn parse_attr<const LEN: usize>(path: [&'static str; LEN], attr: &Attribute) ->
 
 struct ClippyLint {
     attrs: Vec<Attribute>,
+    version: Option<LitStr>,
     explanation: String,
     name: Ident,
     category: Ident,
@@ -41,8 +42,14 @@ impl Parse for ClippyLint {
                 let value = lit.value();
                 let line = value.strip_prefix(' ').unwrap_or(&value);
 
-                if line.starts_with("```") {
-                    explanation += "```\n";
+                if let Some(lang) = line.strip_prefix("```") {
+                    let tag = lang.split_once(',').map_or(lang, |(left, _)| left);
+                    if !in_code && matches!(tag, "" | "rust" | "ignore" | "should_panic" | "no_run" | "compile_fail") {
+                        explanation += "```rust\n";
+                    } else {
+                        explanation += line;
+                        explanation.push('\n');
+                    }
                     in_code = !in_code;
                 } else if !(in_code && line.starts_with("# ")) {
                     explanation += line;
@@ -68,6 +75,7 @@ impl Parse for ClippyLint {
 
         Ok(Self {
             attrs,
+            version,
             explanation,
             name,
             category,
@@ -123,6 +131,7 @@ impl Parse for ClippyLint {
 pub fn declare_clippy_lint(input: TokenStream) -> TokenStream {
     let ClippyLint {
         attrs,
+        version,
         explanation,
         name,
         category,
@@ -146,6 +155,14 @@ pub fn declare_clippy_lint(input: TokenStream) -> TokenStream {
     (&mut category[0..1]).make_ascii_uppercase();
     let category_variant = format_ident!("{category}");
 
+    let name_span = name.span().unwrap();
+    let location = format!("{}#{}", name_span.source_file().path().display(), name_span.line());
+
+    let version = match version {
+        Some(version) => quote!(Some(#version)),
+        None => quote!(None),
+    };
+
     let output = quote! {
         rustc_session::declare_tool_lint! {
             #(#attrs)*
@@ -159,6 +176,8 @@ pub fn declare_clippy_lint(input: TokenStream) -> TokenStream {
             lint: &#name,
             category: crate::LintCategory::#category_variant,
             explanation: #explanation,
+            location: #location,
+            version: #version,
         };
     };
 
diff --git a/tests/compile-test.rs b/tests/compile-test.rs
index c7080e5dcdc9e..9754254cdd0df 100644
--- a/tests/compile-test.rs
+++ b/tests/compile-test.rs
@@ -1,22 +1,31 @@
+#![feature(rustc_private, let_chains)]
 #![warn(rust_2018_idioms, unused_lifetimes)]
 #![allow(unused_extern_crates)]
 
+use cargo_metadata::diagnostic::{Applicability, Diagnostic};
+use cargo_metadata::Message;
+use clippy_config::ClippyConfiguration;
+use clippy_lints::declared_lints::LINTS;
+use clippy_lints::deprecated_lints::{DEPRECATED, DEPRECATED_VERSION, RENAMED};
+use clippy_lints::LintInfo;
+use serde::{Deserialize, Serialize};
+use test_utils::IS_RUSTC_TEST_SUITE;
 use ui_test::custom_flags::rustfix::RustfixMode;
+use ui_test::custom_flags::Flag;
 use ui_test::spanned::Spanned;
+use ui_test::test_result::TestRun;
 use ui_test::{status_emitter, Args, CommandBuilder, Config, Match, OutputConflictHandling};
 
-use std::collections::BTreeMap;
+use std::collections::{BTreeMap, HashMap};
 use std::env::{self, set_var, var_os};
 use std::ffi::{OsStr, OsString};
-use std::fs;
+use std::fmt::Write;
 use std::path::{Path, PathBuf};
-use std::sync::LazyLock;
-use test_utils::IS_RUSTC_TEST_SUITE;
+use std::sync::mpsc::{channel, Sender};
+use std::{fs, iter, thread};
 
 // Test dependencies may need an `extern crate` here to ensure that they show up
 // in the depinfo file (otherwise cargo thinks they are unused)
-extern crate clippy_lints;
-extern crate clippy_utils;
 extern crate futures;
 extern crate if_chain;
 extern crate itertools;
@@ -52,7 +61,7 @@ static TEST_DEPENDENCIES: &[&str] = &[
 /// dependencies must be added to Cargo.toml at the project root. Test
 /// dependencies that are not *directly* used by this test module require an
 /// `extern crate` declaration.
-static EXTERN_FLAGS: LazyLock<Vec<String>> = LazyLock::new(|| {
+fn extern_flags() -> Vec<String> {
     let current_exe_depinfo = {
         let mut path = env::current_exe().unwrap();
         path.set_extension("d");
@@ -100,70 +109,93 @@ static EXTERN_FLAGS: LazyLock<Vec<String>> = LazyLock::new(|| {
         .into_iter()
         .map(|(name, path)| format!("--extern={name}={path}"))
         .collect()
-});
+}
 
 // whether to run internal tests or not
 const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal");
 
-fn base_config(test_dir: &str) -> (Config, Args) {
-    let mut args = Args::test().unwrap();
-    args.bless |= var_os("RUSTC_BLESS").is_some_and(|v| v != "0");
-
-    let target_dir = PathBuf::from(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into()));
-    let mut config = Config {
-        output_conflict_handling: OutputConflictHandling::Error,
-        filter_files: env::var("TESTNAME")
-            .map(|filters| filters.split(',').map(str::to_string).collect())
-            .unwrap_or_default(),
-        target: None,
-        bless_command: Some("cargo uibless".into()),
-        out_dir: target_dir.join("ui_test"),
-        ..Config::rustc(Path::new("tests").join(test_dir))
-    };
-    config.comment_defaults.base().exit_status = None.into();
-    config.comment_defaults.base().require_annotations = None.into();
-    config
-        .comment_defaults
-        .base()
-        .set_custom("rustfix", RustfixMode::Everything);
-    config.comment_defaults.base().diagnostic_code_prefix = Some(Spanned::dummy("clippy::".into())).into();
-    config.with_args(&args);
-    let current_exe_path = env::current_exe().unwrap();
-    let deps_path = current_exe_path.parent().unwrap();
-    let profile_path = deps_path.parent().unwrap();
-
-    config.program.args.extend(
-        [
-            "--emit=metadata",
-            "-Aunused",
-            "-Ainternal_features",
-            "-Zui-testing",
-            "-Zdeduplicate-diagnostics=no",
-            "-Dwarnings",
-            &format!("-Ldependency={}", deps_path.display()),
-        ]
-        .map(OsString::from),
-    );
-
-    config.program.args.extend(EXTERN_FLAGS.iter().map(OsString::from));
-    // Prevent rustc from creating `rustc-ice-*` files the console output is enough.
-    config.program.envs.push(("RUSTC_ICE".into(), Some("0".into())));
+struct TestContext {
+    args: Args,
+    extern_flags: Vec<String>,
+    diagnostic_collector: Option<DiagnosticCollector>,
+    collector_thread: Option<thread::JoinHandle<()>>,
+}
 
-    if let Some(host_libs) = option_env!("HOST_LIBS") {
-        let dep = format!("-Ldependency={}", Path::new(host_libs).join("deps").display());
-        config.program.args.push(dep.into());
+impl TestContext {
+    fn new() -> Self {
+        let mut args = Args::test().unwrap();
+        args.bless |= var_os("RUSTC_BLESS").is_some_and(|v| v != "0");
+        let (diagnostic_collector, collector_thread) = var_os("COLLECT_METADATA")
+            .is_some()
+            .then(DiagnosticCollector::spawn)
+            .unzip();
+        Self {
+            args,
+            extern_flags: extern_flags(),
+            diagnostic_collector,
+            collector_thread,
+        }
     }
 
-    config.program.program = profile_path.join(if cfg!(windows) {
-        "clippy-driver.exe"
-    } else {
-        "clippy-driver"
-    });
-    (config, args)
+    fn base_config(&self, test_dir: &str) -> Config {
+        let target_dir = PathBuf::from(var_os("CARGO_TARGET_DIR").unwrap_or_else(|| "target".into()));
+        let mut config = Config {
+            output_conflict_handling: OutputConflictHandling::Error,
+            filter_files: env::var("TESTNAME")
+                .map(|filters| filters.split(',').map(str::to_string).collect())
+                .unwrap_or_default(),
+            target: None,
+            bless_command: Some("cargo uibless".into()),
+            out_dir: target_dir.join("ui_test"),
+            ..Config::rustc(Path::new("tests").join(test_dir))
+        };
+        let defaults = config.comment_defaults.base();
+        defaults.exit_status = None.into();
+        defaults.require_annotations = None.into();
+        defaults.diagnostic_code_prefix = Some(Spanned::dummy("clippy::".into())).into();
+        defaults.set_custom("rustfix", RustfixMode::Everything);
+        if let Some(collector) = self.diagnostic_collector.clone() {
+            defaults.set_custom("diagnostic-collector", collector);
+        }
+        config.with_args(&self.args);
+        let current_exe_path = env::current_exe().unwrap();
+        let deps_path = current_exe_path.parent().unwrap();
+        let profile_path = deps_path.parent().unwrap();
+
+        config.program.args.extend(
+            [
+                "--emit=metadata",
+                "-Aunused",
+                "-Ainternal_features",
+                "-Zui-testing",
+                "-Zdeduplicate-diagnostics=no",
+                "-Dwarnings",
+                &format!("-Ldependency={}", deps_path.display()),
+            ]
+            .map(OsString::from),
+        );
+
+        config.program.args.extend(self.extern_flags.iter().map(OsString::from));
+        // Prevent rustc from creating `rustc-ice-*` files the console output is enough.
+        config.program.envs.push(("RUSTC_ICE".into(), Some("0".into())));
+
+        if let Some(host_libs) = option_env!("HOST_LIBS") {
+            let dep = format!("-Ldependency={}", Path::new(host_libs).join("deps").display());
+            config.program.args.push(dep.into());
+        }
+
+        config.program.program = profile_path.join(if cfg!(windows) {
+            "clippy-driver.exe"
+        } else {
+            "clippy-driver"
+        });
+
+        config
+    }
 }
 
-fn run_ui() {
-    let (mut config, args) = base_config("ui");
+fn run_ui(cx: &TestContext) {
+    let mut config = cx.base_config("ui");
     config
         .program
         .envs
@@ -173,30 +205,29 @@ fn run_ui() {
         vec![config],
         ui_test::default_file_filter,
         ui_test::default_per_file_config,
-        status_emitter::Text::from(args.format),
+        status_emitter::Text::from(cx.args.format),
     )
     .unwrap();
 }
 
-fn run_internal_tests() {
-    // only run internal tests with the internal-tests feature
+fn run_internal_tests(cx: &TestContext) {
     if !RUN_INTERNAL_TESTS {
         return;
     }
-    let (mut config, args) = base_config("ui-internal");
+    let mut config = cx.base_config("ui-internal");
     config.bless_command = Some("cargo uitest --features internal -- -- --bless".into());
 
     ui_test::run_tests_generic(
         vec![config],
         ui_test::default_file_filter,
         ui_test::default_per_file_config,
-        status_emitter::Text::from(args.format),
+        status_emitter::Text::from(cx.args.format),
     )
     .unwrap();
 }
 
-fn run_ui_toml() {
-    let (mut config, args) = base_config("ui-toml");
+fn run_ui_toml(cx: &TestContext) {
+    let mut config = cx.base_config("ui-toml");
 
     config
         .comment_defaults
@@ -214,19 +245,19 @@ fn run_ui_toml() {
                 .envs
                 .push(("CLIPPY_CONF_DIR".into(), Some(path.parent().unwrap().into())));
         },
-        status_emitter::Text::from(args.format),
+        status_emitter::Text::from(cx.args.format),
     )
     .unwrap();
 }
 
 // Allow `Default::default` as `OptWithSpan` is not nameable
 #[allow(clippy::default_trait_access)]
-fn run_ui_cargo() {
+fn run_ui_cargo(cx: &TestContext) {
     if IS_RUSTC_TEST_SUITE {
         return;
     }
 
-    let (mut config, args) = base_config("ui-cargo");
+    let mut config = cx.base_config("ui-cargo");
     config.program.input_file_flag = CommandBuilder::cargo().input_file_flag;
     config.program.out_dir_flag = CommandBuilder::cargo().out_dir_flag;
     config.program.args = vec!["clippy".into(), "--color".into(), "never".into(), "--quiet".into()];
@@ -261,23 +292,25 @@ fn run_ui_cargo() {
                 .then(|| ui_test::default_any_file_filter(path, config) && !ignored_32bit(path))
         },
         |_config, _file_contents| {},
-        status_emitter::Text::from(args.format),
+        status_emitter::Text::from(cx.args.format),
     )
     .unwrap();
 }
 
 fn main() {
     set_var("CLIPPY_DISABLE_DOCS_LINKS", "true");
+
+    let cx = TestContext::new();
+
     // The SPEEDTEST_* env variables can be used to check Clippy's performance on your PR. It runs the
     // affected test 1000 times and gets the average.
     if let Ok(speedtest) = std::env::var("SPEEDTEST") {
         println!("----------- STARTING SPEEDTEST -----------");
         let f = match speedtest.as_str() {
-            "ui" => run_ui as fn(),
-            "cargo" => run_ui_cargo as fn(),
-            "toml" => run_ui_toml as fn(),
-            "internal" => run_internal_tests as fn(),
-            "ui-cargo-toml-metadata" => ui_cargo_toml_metadata as fn(),
+            "ui" => run_ui,
+            "cargo" => run_ui_cargo,
+            "toml" => run_ui_toml,
+            "internal" => run_internal_tests,
 
             _ => panic!("unknown speedtest: {speedtest} || accepted speedtests are: [ui, cargo, toml, internal]"),
         };
@@ -294,7 +327,7 @@ fn main() {
         let mut sum = 0;
         for _ in 0..iterations {
             let start = std::time::Instant::now();
-            f();
+            f(&cx);
             sum += start.elapsed().as_millis();
         }
         println!(
@@ -303,11 +336,17 @@ fn main() {
             sum / u128::from(iterations)
         );
     } else {
-        run_ui();
-        run_ui_toml();
-        run_ui_cargo();
-        run_internal_tests();
+        run_ui(&cx);
+        run_ui_toml(&cx);
+        run_ui_cargo(&cx);
+        run_internal_tests(&cx);
+        drop(cx.diagnostic_collector);
+
         ui_cargo_toml_metadata();
+
+        if let Some(thread) = cx.collector_thread {
+            thread.join().unwrap();
+        }
     }
 }
 
@@ -346,3 +385,180 @@ fn ui_cargo_toml_metadata() {
         );
     }
 }
+
+#[derive(Deserialize)]
+#[serde(untagged)]
+enum DiagnosticOrMessage {
+    Diagnostic(Diagnostic),
+    Message(Message),
+}
+
+/// Collects applicabilities from the diagnostics produced for each UI test, producing the
+/// `util/gh-pages/lints.json` file used by <https://rust-lang.github.io/rust-clippy/>
+#[derive(Debug, Clone)]
+struct DiagnosticCollector {
+    sender: Sender<Vec<u8>>,
+}
+
+impl DiagnosticCollector {
+    #[allow(clippy::assertions_on_constants)]
+    fn spawn() -> (Self, thread::JoinHandle<()>) {
+        assert!(!IS_RUSTC_TEST_SUITE && !RUN_INTERNAL_TESTS);
+
+        let (sender, receiver) = channel::<Vec<u8>>();
+
+        let handle = thread::spawn(|| {
+            let mut applicabilities = HashMap::new();
+
+            for stderr in receiver {
+                for line in stderr.split(|&byte| byte == b'\n') {
+                    let diag = match serde_json::from_slice(line) {
+                        Ok(DiagnosticOrMessage::Diagnostic(diag)) => diag,
+                        Ok(DiagnosticOrMessage::Message(Message::CompilerMessage(message))) => message.message,
+                        _ => continue,
+                    };
+
+                    if let Some(lint) = diag.code.as_ref().and_then(|code| code.code.strip_prefix("clippy::")) {
+                        let applicability = applicabilities
+                            .entry(lint.to_string())
+                            .or_insert(Applicability::Unspecified);
+                        let diag_applicability = diag
+                            .children
+                            .iter()
+                            .flat_map(|child| &child.spans)
+                            .filter_map(|span| span.suggestion_applicability.clone())
+                            .max_by_key(applicability_ord);
+                        if let Some(diag_applicability) = diag_applicability
+                            && applicability_ord(&diag_applicability) > applicability_ord(applicability)
+                        {
+                            *applicability = diag_applicability;
+                        }
+                    }
+                }
+            }
+
+            let configs = clippy_config::get_configuration_metadata();
+            let mut metadata: Vec<LintMetadata> = LINTS
+                .iter()
+                .map(|lint| LintMetadata::new(lint, &applicabilities, &configs))
+                .chain(
+                    iter::zip(DEPRECATED, DEPRECATED_VERSION)
+                        .map(|((lint, reason), version)| LintMetadata::new_deprecated(lint, reason, version)),
+                )
+                .collect();
+            metadata.sort_unstable_by(|a, b| a.id.cmp(&b.id));
+
+            let json = serde_json::to_string_pretty(&metadata).unwrap();
+            fs::write("util/gh-pages/lints.json", json).unwrap();
+        });
+
+        (Self { sender }, handle)
+    }
+}
+
+fn applicability_ord(applicability: &Applicability) -> u8 {
+    match applicability {
+        Applicability::MachineApplicable => 4,
+        Applicability::HasPlaceholders => 3,
+        Applicability::MaybeIncorrect => 2,
+        Applicability::Unspecified => 1,
+        _ => unimplemented!(),
+    }
+}
+
+impl Flag for DiagnosticCollector {
+    fn post_test_action(
+        &self,
+        _config: &ui_test::per_test_config::TestConfig<'_>,
+        _cmd: &mut std::process::Command,
+        output: &std::process::Output,
+        _build_manager: &ui_test::build_manager::BuildManager<'_>,
+    ) -> Result<Vec<TestRun>, ui_test::Errored> {
+        if !output.stderr.is_empty() {
+            self.sender.send(output.stderr.clone()).unwrap();
+        }
+        Ok(Vec::new())
+    }
+
+    fn clone_inner(&self) -> Box<dyn Flag> {
+        Box::new(self.clone())
+    }
+
+    fn must_be_unique(&self) -> bool {
+        true
+    }
+}
+
+#[derive(Debug, Serialize)]
+struct LintMetadata {
+    id: String,
+    id_location: Option<&'static str>,
+    group: &'static str,
+    level: &'static str,
+    docs: String,
+    version: &'static str,
+    applicability: Applicability,
+}
+
+impl LintMetadata {
+    fn new(lint: &LintInfo, applicabilities: &HashMap<String, Applicability>, configs: &[ClippyConfiguration]) -> Self {
+        let name = lint.name_lower();
+        let applicability = applicabilities
+            .get(&name)
+            .cloned()
+            .unwrap_or(Applicability::Unspecified);
+        let past_names = RENAMED
+            .iter()
+            .filter(|(_, new_name)| new_name.strip_prefix("clippy::") == Some(&name))
+            .map(|(old_name, _)| old_name.strip_prefix("clippy::").unwrap())
+            .collect::<Vec<_>>();
+        let mut docs = lint.explanation.to_string();
+        if !past_names.is_empty() {
+            docs.push_str("\n### Past names\n\n");
+            for past_name in past_names {
+                writeln!(&mut docs, " * {past_name}").unwrap();
+            }
+        }
+        let configs: Vec<_> = configs
+            .iter()
+            .filter(|conf| conf.lints.contains(&name.as_str()))
+            .collect();
+        if !configs.is_empty() {
+            docs.push_str("\n### Configuration\n\n");
+            for config in configs {
+                writeln!(&mut docs, "{config}").unwrap();
+            }
+        }
+        Self {
+            id: name,
+            id_location: Some(lint.location),
+            group: lint.category_str(),
+            level: lint.lint.default_level.as_str(),
+            docs,
+            version: lint.version.unwrap(),
+            applicability,
+        }
+    }
+
+    fn new_deprecated(name: &str, reason: &str, version: &'static str) -> Self {
+        // The reason starts with a lowercase letter and ends without a period.
+        // This needs to be fixed for the website.
+        let mut reason = reason.to_owned();
+        if let Some(reason) = reason.get_mut(0..1) {
+            reason.make_ascii_uppercase();
+        }
+        Self {
+            id: name.strip_prefix("clippy::").unwrap().into(),
+            id_location: None,
+            group: "deprecated",
+            level: "none",
+            version,
+            docs: format!(
+                "### What it does\n\n\
+                Nothing. This lint has been deprecated\n\n\
+                ### Deprecation reason\n\n{reason}.\n",
+            ),
+            applicability: Applicability::Unspecified,
+        }
+    }
+}
diff --git a/tests/config-metadata.rs b/tests/config-metadata.rs
new file mode 100644
index 0000000000000..c0b048270601a
--- /dev/null
+++ b/tests/config-metadata.rs
@@ -0,0 +1,76 @@
+use clippy_config::{get_configuration_metadata, ClippyConfiguration};
+use itertools::Itertools;
+use regex::Regex;
+use std::borrow::Cow;
+use std::{env, fs};
+
+fn metadata() -> impl Iterator<Item = ClippyConfiguration> {
+    get_configuration_metadata()
+        .into_iter()
+        .filter(|config| config.deprecation_reason.is_none())
+        .filter(|config| !config.lints.is_empty())
+}
+
+#[test]
+fn book() {
+    let path = "book/src/lint_configuration.md";
+    let current = fs::read_to_string(path).unwrap();
+
+    let configs = metadata().map(|conf| conf.to_markdown_paragraph()).join("\n");
+    let expected = format!(
+        r#"<!--
+This file is generated by `cargo bless --test config-metadata`.
+Please use that command to update the file and do not edit it by hand.
+-->
+
+# Lint Configuration Options
+
+The following list shows each configuration option, along with a description, its default value, an example
+and lints affected.
+
+---
+
+{}
+"#,
+        configs.trim(),
+    );
+
+    if current != expected {
+        if env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0") {
+            fs::write(path, expected).unwrap();
+        } else {
+            panic!("`{path}` is out of date, run `cargo bless --test config-metadata` to update it");
+        }
+    }
+}
+
+#[test]
+fn changelog() {
+    let path = "CHANGELOG.md";
+    let current = fs::read_to_string(path).unwrap();
+
+    let configs = metadata().map(|conf| conf.to_markdown_link()).join("\n");
+
+    let re = Regex::new(
+        "(?s)\
+        (<!-- begin autogenerated links to configuration documentation -->)\
+        .*\
+        (<!-- end autogenerated links to configuration documentation -->)\
+        ",
+    )
+    .unwrap();
+    let expected = re.replace(&current, format!("$1\n{configs}\n$2"));
+
+    assert!(
+        matches!(expected, Cow::Owned(_)),
+        "failed to find configuration section in `{path}`"
+    );
+
+    if current != expected {
+        if env::var_os("RUSTC_BLESS").is_some_and(|v| v != "0") {
+            fs::write(path, expected.as_bytes()).unwrap();
+        } else {
+            panic!("`{path}` is out of date, run `cargo bless --test config-metadata` to update it");
+        }
+    }
+}
diff --git a/util/gh-pages/index.html b/util/gh-pages/index.html
index dd58fa2282c64..f3d7e504fdf80 100644
--- a/util/gh-pages/index.html
+++ b/util/gh-pages/index.html
@@ -239,7 +239,7 @@ <h2 class="panel-title">
                         <!-- Applicability -->
                         <div class="lint-additional-info-item">
                             <span> Applicability: </span>
-                            <span class="label label-default label-applicability">{{lint.applicability.applicability}}</span>
+                            <span class="label label-default label-applicability">{{lint.applicability}}</span>
                             <a href="https://doc.rust-lang.org/nightly/nightly-rustc/rustc_lint_defs/enum.Applicability.html#variants">(?)</a>
                         </div>
                         <!-- Clippy version -->
@@ -252,8 +252,8 @@ <h2 class="panel-title">
                             <a href="https://github.com/rust-lang/rust-clippy/issues?q=is%3Aissue+{{lint.id}}">Related Issues</a>
                         </div>
                         <!-- Jump to source -->
-                        <div class="lint-additional-info-item" ng-if="lint.id_span">
-                            <a href="https://github.com/rust-lang/rust-clippy/blob/{{docVersion}}/clippy_lints/{{lint.id_span.path}}#L{{lint.id_span.line}}">View Source</a>
+                        <div class="lint-additional-info-item" ng-if="lint.id_location">
+                            <a href="https://github.com/rust-lang/rust-clippy/blob/{{docVersion}}/{{lint.id_location}}">View Source</a>
                         </div>
                     </div>
                 </div>
diff --git a/util/gh-pages/script.js b/util/gh-pages/script.js
index d54b5e851c7c0..1a5330bc0e571 100644
--- a/util/gh-pages/script.js
+++ b/util/gh-pages/script.js
@@ -96,13 +96,13 @@
                 cargo: true,
                 complexity: true,
                 correctness: true,
-                deprecated: false,
                 nursery: true,
                 pedantic: true,
                 perf: true,
                 restriction: true,
                 style: true,
                 suspicious: true,
+                deprecated: false,
             }
 
             $scope.groups = {
@@ -126,11 +126,10 @@
             );
 
             const APPLICABILITIES_FILTER_DEFAULT = {
-                Unspecified: true,
-                Unresolved: true,
                 MachineApplicable: true,
                 MaybeIncorrect: true,
-                HasPlaceholders: true
+                HasPlaceholders: true,
+                Unspecified: true,
             };
 
             $scope.applicabilities = {
@@ -425,7 +424,7 @@
             }
 
             $scope.byApplicabilities = function (lint) {
-                return $scope.applicabilities[lint.applicability.applicability];
+                return $scope.applicabilities[lint.applicability];
             };
 
             // Show details for one lint

From a22ce2d00578bfdfbb079d8bacf626c2789d4b5a Mon Sep 17 00:00:00 2001
From: Alex Macleod <alex@macleod.io>
Date: Mon, 12 Aug 2024 16:25:14 +0000
Subject: [PATCH 32/54] Remove `HashSet`s from `Conf`

---
 book/src/lint_configuration.md                |  4 ++--
 clippy_config/src/conf.rs                     | 23 +++++++++----------
 clippy_config/src/lib.rs                      |  1 -
 clippy_lints/src/cargo/mod.rs                 |  6 ++---
 clippy_lints/src/doc/mod.rs                   |  6 ++---
 clippy_lints/src/min_ident_chars.rs           |  4 ++--
 .../src/operators/arithmetic_side_effects.rs  |  2 +-
 clippy_lints/src/wildcard_imports.rs          |  6 ++---
 8 files changed, 25 insertions(+), 27 deletions(-)

diff --git a/book/src/lint_configuration.md b/book/src/lint_configuration.md
index 4f23afe2e118a..78348797588a6 100644
--- a/book/src/lint_configuration.md
+++ b/book/src/lint_configuration.md
@@ -199,7 +199,7 @@ Allowed names below the minimum allowed characters. The value `".."` can be used
 the list to indicate, that the configured values should be appended to the default
 configuration of Clippy. By default, any configuration will replace the default value.
 
-**Default Value:** `["j", "z", "i", "y", "n", "x", "w"]`
+**Default Value:** `["i", "j", "x", "y", "z", "w", "n"]`
 
 ---
 **Affected lints:**
@@ -455,7 +455,7 @@ default configuration of Clippy. By default, any configuration will replace the
 * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
 * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
 
-**Default Value:** `["TiB", "CoreGraphics", "CoffeeScript", "TeX", "Direct2D", "PiB", "DirectX", "NetBSD", "OAuth", "NaN", "OpenType", "WebGL2", "WebTransport", "JavaScript", "OpenSSL", "OpenSSH", "EiB", "PureScript", "OpenAL", "MiB", "WebAssembly", "MinGW", "CoreFoundation", "WebGPU", "ClojureScript", "CamelCase", "OpenDNS", "NaNs", "OpenMP", "GitLab", "KiB", "sRGB", "CoreText", "macOS", "TypeScript", "GiB", "OpenExr", "YCbCr", "OpenTelemetry", "OpenBSD", "FreeBSD", "GPLv2", "PostScript", "WebP", "LaTeX", "TensorFlow", "AccessKit", "TrueType", "OpenStreetMap", "OpenGL", "DevOps", "OCaml", "WebRTC", "WebGL", "BibLaTeX", "GitHub", "GraphQL", "iOS", "Direct3D", "BibTeX", "DirectWrite", "GPLv3", "IPv6", "WebSocket", "IPv4", "ECMAScript"]`
+**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "AccessKit", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]`
 
 ---
 **Affected lints:**
diff --git a/clippy_config/src/conf.rs b/clippy_config/src/conf.rs
index 4c2a8255d6b65..a6f1b958bfb17 100644
--- a/clippy_config/src/conf.rs
+++ b/clippy_config/src/conf.rs
@@ -1,7 +1,6 @@
 use crate::msrvs::Msrv;
 use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename};
 use crate::ClippyConfiguration;
-use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_session::Session;
 use rustc_span::edit_distance::edit_distance;
@@ -218,7 +217,7 @@ macro_rules! define_Conf {
 define_Conf! {
     /// Which crates to allow absolute paths from
     #[lints(absolute_paths)]
-    absolute_paths_allowed_crates: FxHashSet<String> = FxHashSet::default(),
+    absolute_paths_allowed_crates: Vec<String> = Vec::new(),
     /// The maximum number of segments a path can have before being linted, anything above this will
     /// be linted.
     #[lints(absolute_paths)]
@@ -280,12 +279,12 @@ define_Conf! {
     allowed_dotfiles: Vec<String> = Vec::default(),
     /// A list of crate names to allow duplicates of
     #[lints(multiple_crate_versions)]
-    allowed_duplicate_crates: FxHashSet<String> = FxHashSet::default(),
+    allowed_duplicate_crates: Vec<String> = Vec::new(),
     /// Allowed names below the minimum allowed characters. The value `".."` can be used as part of
     /// the list to indicate, that the configured values should be appended to the default
     /// configuration of Clippy. By default, any configuration will replace the default value.
     #[lints(min_ident_chars)]
-    allowed_idents_below_min_chars: FxHashSet<String> =
+    allowed_idents_below_min_chars: Vec<String> =
         DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string).collect(),
     /// List of prefixes to allow when determining whether an item's name ends with the module's name.
     /// If the rest of an item's name is an allowed prefix (e.g. item `ToFoo` or `to_foo` in module `foo`),
@@ -323,7 +322,7 @@ define_Conf! {
     /// 2. Paths with any segment that containing the word 'prelude'
     /// are already allowed by default.
     #[lints(wildcard_imports)]
-    allowed_wildcard_imports: FxHashSet<String> = FxHashSet::default(),
+    allowed_wildcard_imports: Vec<String> = Vec::new(),
     /// Suppress checking of the passed type names in all types of operations.
     ///
     /// If a specific operation is desired, consider using `arithmetic_side_effects_allowed_binary` or `arithmetic_side_effects_allowed_unary` instead.
@@ -355,7 +354,7 @@ define_Conf! {
     /// arithmetic-side-effects-allowed-binary = [["SomeType" , "f32"], ["AnotherType", "*"]]
     /// ```
     #[lints(arithmetic_side_effects)]
-    arithmetic_side_effects_allowed_binary: Vec<[String; 2]> = <_>::default(),
+    arithmetic_side_effects_allowed_binary: Vec<(String, String)> = <_>::default(),
     /// Suppress checking of the passed type names in unary operations like "negation" (`-`).
     ///
     /// #### Example
@@ -431,7 +430,7 @@ define_Conf! {
     /// * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
     /// * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
     #[lints(doc_markdown)]
-    doc_valid_idents: FxHashSet<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect(),
+    doc_valid_idents: Vec<String> = DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string).collect(),
     /// Whether to apply the raw pointer heuristic to determine if a type is `Send`.
     #[lints(non_send_fields_in_send_ty)]
     enable_raw_pointer_heuristic_for_send: bool = true,
@@ -706,12 +705,12 @@ fn deserialize(file: &SourceFile) -> TryConf {
                 DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS,
             );
             // TODO: THIS SHOULD BE TESTED, this comment will be gone soon
-            if conf.conf.allowed_idents_below_min_chars.contains("..") {
+            if conf.conf.allowed_idents_below_min_chars.iter().any(|e| e == "..") {
                 conf.conf
                     .allowed_idents_below_min_chars
                     .extend(DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS.iter().map(ToString::to_string));
             }
-            if conf.conf.doc_valid_idents.contains("..") {
+            if conf.conf.doc_valid_idents.iter().any(|e| e == "..") {
                 conf.conf
                     .doc_valid_idents
                     .extend(DEFAULT_DOC_VALID_IDENTS.iter().map(ToString::to_string));
@@ -890,14 +889,14 @@ fn calculate_dimensions(fields: &[&str]) -> (usize, Vec<usize>) {
 
 #[cfg(test)]
 mod tests {
-    use rustc_data_structures::fx::{FxHashMap, FxHashSet};
     use serde::de::IgnoredAny;
+    use std::collections::{HashMap, HashSet};
     use std::fs;
     use walkdir::WalkDir;
 
     #[test]
     fn configs_are_tested() {
-        let mut names: FxHashSet<String> = crate::get_configuration_metadata()
+        let mut names: HashSet<String> = crate::get_configuration_metadata()
             .into_iter()
             .map(|meta| meta.name.replace('_', "-"))
             .collect();
@@ -910,7 +909,7 @@ mod tests {
         for entry in toml_files {
             let file = fs::read_to_string(entry.path()).unwrap();
             #[allow(clippy::zero_sized_map_values)]
-            if let Ok(map) = toml::from_str::<FxHashMap<String, IgnoredAny>>(&file) {
+            if let Ok(map) = toml::from_str::<HashMap<String, IgnoredAny>>(&file) {
                 for name in map.keys() {
                     names.remove(name.as_str());
                 }
diff --git a/clippy_config/src/lib.rs b/clippy_config/src/lib.rs
index 3afe599c7b6e4..ac838cc81d2f0 100644
--- a/clippy_config/src/lib.rs
+++ b/clippy_config/src/lib.rs
@@ -15,7 +15,6 @@
 
 extern crate rustc_ast;
 extern crate rustc_attr;
-extern crate rustc_data_structures;
 #[allow(unused_extern_crates)]
 extern crate rustc_driver;
 extern crate rustc_errors;
diff --git a/clippy_lints/src/cargo/mod.rs b/clippy_lints/src/cargo/mod.rs
index 312ad4c299007..96a2b1614646e 100644
--- a/clippy_lints/src/cargo/mod.rs
+++ b/clippy_lints/src/cargo/mod.rs
@@ -205,7 +205,7 @@ declare_clippy_lint! {
 }
 
 pub struct Cargo {
-    allowed_duplicate_crates: &'static FxHashSet<String>,
+    allowed_duplicate_crates: FxHashSet<String>,
     ignore_publish: bool,
 }
 
@@ -221,7 +221,7 @@ impl_lint_pass!(Cargo => [
 impl Cargo {
     pub fn new(conf: &'static Conf) -> Self {
         Self {
-            allowed_duplicate_crates: &conf.allowed_duplicate_crates,
+            allowed_duplicate_crates: conf.allowed_duplicate_crates.iter().cloned().collect(),
             ignore_publish: conf.cargo_ignore_publish,
         }
     }
@@ -263,7 +263,7 @@ impl LateLintPass<'_> for Cargo {
         {
             match MetadataCommand::new().exec() {
                 Ok(metadata) => {
-                    multiple_crate_versions::check(cx, &metadata, self.allowed_duplicate_crates);
+                    multiple_crate_versions::check(cx, &metadata, &self.allowed_duplicate_crates);
                 },
                 Err(e) => {
                     for lint in WITH_DEPS_LINTS {
diff --git a/clippy_lints/src/doc/mod.rs b/clippy_lints/src/doc/mod.rs
index 762d5595e145e..daea0d18e4060 100644
--- a/clippy_lints/src/doc/mod.rs
+++ b/clippy_lints/src/doc/mod.rs
@@ -423,14 +423,14 @@ declare_clippy_lint! {
 }
 
 pub struct Documentation {
-    valid_idents: &'static FxHashSet<String>,
+    valid_idents: FxHashSet<String>,
     check_private_items: bool,
 }
 
 impl Documentation {
     pub fn new(conf: &'static Conf) -> Self {
         Self {
-            valid_idents: &conf.doc_valid_idents,
+            valid_idents: conf.doc_valid_idents.iter().cloned().collect(),
             check_private_items: conf.check_private_items,
         }
     }
@@ -452,7 +452,7 @@ impl_lint_pass!(Documentation => [
 
 impl<'tcx> LateLintPass<'tcx> for Documentation {
     fn check_attributes(&mut self, cx: &LateContext<'tcx>, attrs: &'tcx [Attribute]) {
-        let Some(headers) = check_attrs(cx, self.valid_idents, attrs) else {
+        let Some(headers) = check_attrs(cx, &self.valid_idents, attrs) else {
             return;
         };
 
diff --git a/clippy_lints/src/min_ident_chars.rs b/clippy_lints/src/min_ident_chars.rs
index 53fa444e93c40..c83e5198c2748 100644
--- a/clippy_lints/src/min_ident_chars.rs
+++ b/clippy_lints/src/min_ident_chars.rs
@@ -41,14 +41,14 @@ declare_clippy_lint! {
 impl_lint_pass!(MinIdentChars => [MIN_IDENT_CHARS]);
 
 pub struct MinIdentChars {
-    allowed_idents_below_min_chars: &'static FxHashSet<String>,
+    allowed_idents_below_min_chars: FxHashSet<String>,
     min_ident_chars_threshold: u64,
 }
 
 impl MinIdentChars {
     pub fn new(conf: &'static Conf) -> Self {
         Self {
-            allowed_idents_below_min_chars: &conf.allowed_idents_below_min_chars,
+            allowed_idents_below_min_chars: conf.allowed_idents_below_min_chars.iter().cloned().collect(),
             min_ident_chars_threshold: conf.min_ident_chars_threshold,
         }
     }
diff --git a/clippy_lints/src/operators/arithmetic_side_effects.rs b/clippy_lints/src/operators/arithmetic_side_effects.rs
index bc71a4790b9d4..8b9f899d82d3f 100644
--- a/clippy_lints/src/operators/arithmetic_side_effects.rs
+++ b/clippy_lints/src/operators/arithmetic_side_effects.rs
@@ -35,7 +35,7 @@ impl ArithmeticSideEffects {
             ("f64", FxHashSet::from_iter(["f64"])),
             ("std::string::String", FxHashSet::from_iter(["str"])),
         ]);
-        for [lhs, rhs] in &conf.arithmetic_side_effects_allowed_binary {
+        for (lhs, rhs) in &conf.arithmetic_side_effects_allowed_binary {
             allowed_binary.entry(lhs).or_default().insert(rhs);
         }
         for s in &conf.arithmetic_side_effects_allowed {
diff --git a/clippy_lints/src/wildcard_imports.rs b/clippy_lints/src/wildcard_imports.rs
index c4d64ee4609ee..7d9e58ad2f8d5 100644
--- a/clippy_lints/src/wildcard_imports.rs
+++ b/clippy_lints/src/wildcard_imports.rs
@@ -100,14 +100,14 @@ declare_clippy_lint! {
 
 pub struct WildcardImports {
     warn_on_all: bool,
-    allowed_segments: &'static FxHashSet<String>,
+    allowed_segments: FxHashSet<String>,
 }
 
 impl WildcardImports {
     pub fn new(conf: &'static Conf) -> Self {
         Self {
             warn_on_all: conf.warn_on_all_wildcard_imports,
-            allowed_segments: &conf.allowed_wildcard_imports,
+            allowed_segments: conf.allowed_wildcard_imports.iter().cloned().collect(),
         }
     }
 }
@@ -181,7 +181,7 @@ impl WildcardImports {
     fn check_exceptions(&self, cx: &LateContext<'_>, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool {
         item.span.from_expansion()
             || is_prelude_import(segments)
-            || is_allowed_via_config(segments, self.allowed_segments)
+            || is_allowed_via_config(segments, &self.allowed_segments)
             || (is_super_only_import(segments) && is_in_test(cx.tcx, item.hir_id()))
     }
 }

From 5fee8408758ea1dfc33712065386d8a4c5213cd5 Mon Sep 17 00:00:00 2001
From: Antoni Spaanderman <56turtle56@gmail.com>
Date: Tue, 13 Aug 2024 13:13:02 +0200
Subject: [PATCH 33/54] fix indentation in docs

---
 clippy_lints/src/attrs/mod.rs     | 4 ++--
 clippy_lints/src/operators/mod.rs | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs
index 8f430ae601a85..5d99001e5aa3f 100644
--- a/clippy_lints/src/attrs/mod.rs
+++ b/clippy_lints/src/attrs/mod.rs
@@ -310,8 +310,8 @@ declare_clippy_lint! {
     /// ```rust,ignore
     /// #[allow(unused_mut)]
     /// fn foo() -> usize {
-    ///    let mut a = Vec::new();
-    ///    a.len()
+    ///     let mut a = Vec::new();
+    ///     a.len()
     /// }
     /// ```
     /// Use instead:
diff --git a/clippy_lints/src/operators/mod.rs b/clippy_lints/src/operators/mod.rs
index 9baecff801f21..9e8a821c3f4e8 100644
--- a/clippy_lints/src/operators/mod.rs
+++ b/clippy_lints/src/operators/mod.rs
@@ -80,7 +80,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// // `n` can be any number, including `i32::MAX`.
     /// fn foo(n: i32) -> i32 {
-    ///   n + 1
+    ///     n + 1
     /// }
     /// ```
     ///

From 819fa6f1806c4432c98137e97e3b251ccf047ae8 Mon Sep 17 00:00:00 2001
From: Antoni Spaanderman <56turtle56@gmail.com>
Date: Tue, 13 Aug 2024 13:13:31 +0200
Subject: [PATCH 34/54] fix source location github link

---
 declare_clippy_lint/src/lib.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/declare_clippy_lint/src/lib.rs b/declare_clippy_lint/src/lib.rs
index fd366b6b26277..6aa24329b0659 100644
--- a/declare_clippy_lint/src/lib.rs
+++ b/declare_clippy_lint/src/lib.rs
@@ -156,7 +156,7 @@ pub fn declare_clippy_lint(input: TokenStream) -> TokenStream {
     let category_variant = format_ident!("{category}");
 
     let name_span = name.span().unwrap();
-    let location = format!("{}#{}", name_span.source_file().path().display(), name_span.line());
+    let location = format!("{}#L{}", name_span.source_file().path().display(), name_span.line());
 
     let version = match version {
         Some(version) => quote!(Some(#version)),

From f72b3dbba217eff3b7292517944948311552eaab Mon Sep 17 00:00:00 2001
From: Nicholas Nethercote <n.nethercote@gmail.com>
Date: Fri, 9 Aug 2024 17:44:47 +1000
Subject: [PATCH 35/54] Use `impl PartialEq<TokenKind> for Token` more.

This lets us compare a `Token` with a `TokenKind`. It's used a lot, but
can be used even more, avoiding the need for some `.kind` uses.
---
 clippy_dev/src/new_lint.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clippy_dev/src/new_lint.rs b/clippy_dev/src/new_lint.rs
index de91233d196c8..87117832fb952 100644
--- a/clippy_dev/src/new_lint.rs
+++ b/clippy_dev/src/new_lint.rs
@@ -470,7 +470,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str>
     });
 
     // Find both the last lint declaration (declare_clippy_lint!) and the lint pass impl
-    while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token_kind == TokenKind::Ident) {
+    while let Some(LintDeclSearchResult { content, .. }) = iter.find(|result| result.token == TokenKind::Ident) {
         let mut iter = iter
             .by_ref()
             .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
@@ -480,7 +480,7 @@ fn setup_mod_file(path: &Path, lint: &LintData<'_>) -> io::Result<&'static str>
                 // matches `!{`
                 match_tokens!(iter, Bang OpenBrace);
                 if let Some(LintDeclSearchResult { range, .. }) =
-                    iter.find(|result| result.token_kind == TokenKind::CloseBrace)
+                    iter.find(|result| result.token == TokenKind::CloseBrace)
                 {
                     last_decl_curly_offset = Some(range.end);
                 }

From f3e00339a7009e735dcc280eba62e8f5f30c4c98 Mon Sep 17 00:00:00 2001
From: Philipp Krones <hello@philkrones.com>
Date: Thu, 15 Aug 2024 21:09:17 +0200
Subject: [PATCH 36/54] flip1995: 2 week vacation

---
 triagebot.toml | 1 +
 1 file changed, 1 insertion(+)

diff --git a/triagebot.toml b/triagebot.toml
index dcf00e4e384b0..99b3560a06427 100644
--- a/triagebot.toml
+++ b/triagebot.toml
@@ -20,6 +20,7 @@ new_pr = true
 [assign]
 contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md"
 users_on_vacation = [
+    "flip1995",
     "matthiaskrgr",
     "giraffate",
 ]

From 953c7ed41ef6e98a484c1d15f9b06169f49c56f4 Mon Sep 17 00:00:00 2001
From: Alexey Semenyuk <alexsemenyuk88@gmail.com>
Date: Fri, 16 Aug 2024 16:17:24 +0500
Subject: [PATCH 37/54] Disable assigning_clones for tests

---
 clippy_lints/src/assigning_clones.rs |  3 ++-
 tests/ui/assigning_clones.fixed      | 20 ++++++++++++++++++++
 tests/ui/assigning_clones.rs         | 20 ++++++++++++++++++++
 3 files changed, 42 insertions(+), 1 deletion(-)

diff --git a/clippy_lints/src/assigning_clones.rs b/clippy_lints/src/assigning_clones.rs
index 6e336efbb90a2..55645d04eef36 100644
--- a/clippy_lints/src/assigning_clones.rs
+++ b/clippy_lints/src/assigning_clones.rs
@@ -3,7 +3,7 @@ use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::mir::{enclosing_mir, PossibleBorrowerMap};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{is_diag_trait_item, last_path_segment, local_is_initialized, path_to_local};
+use clippy_utils::{is_diag_trait_item, is_in_test, last_path_segment, local_is_initialized, path_to_local};
 use rustc_errors::Applicability;
 use rustc_hir::{self as hir, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -118,6 +118,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones {
                 }
             )
             && !clone_source_borrows_from_dest(cx, lhs, rhs.span)
+            && !is_in_test(cx.tcx, e.hir_id)
         {
             span_lint_and_then(
                 cx,
diff --git a/tests/ui/assigning_clones.fixed b/tests/ui/assigning_clones.fixed
index b376d55a40250..09732d1a50ce0 100644
--- a/tests/ui/assigning_clones.fixed
+++ b/tests/ui/assigning_clones.fixed
@@ -396,3 +396,23 @@ impl<T: Clone> Clone for DerefWrapperWithClone<T> {
         *self = Self(source.0.clone());
     }
 }
+
+#[cfg(test)]
+mod test {
+    #[derive(Default)]
+    struct Data {
+        field: String,
+    }
+
+    fn test_data() -> Data {
+        Data {
+            field: "default_value".to_string(),
+        }
+    }
+
+    #[test]
+    fn test() {
+        let mut data = test_data();
+        data.field = "override_value".to_owned();
+    }
+}
diff --git a/tests/ui/assigning_clones.rs b/tests/ui/assigning_clones.rs
index 11a5d4459c350..6be25ae17a55d 100644
--- a/tests/ui/assigning_clones.rs
+++ b/tests/ui/assigning_clones.rs
@@ -396,3 +396,23 @@ impl<T: Clone> Clone for DerefWrapperWithClone<T> {
         *self = Self(source.0.clone());
     }
 }
+
+#[cfg(test)]
+mod test {
+    #[derive(Default)]
+    struct Data {
+        field: String,
+    }
+
+    fn test_data() -> Data {
+        Data {
+            field: "default_value".to_string(),
+        }
+    }
+
+    #[test]
+    fn test() {
+        let mut data = test_data();
+        data.field = "override_value".to_owned();
+    }
+}

From c0373616d098fecd38b14b5e42f7d202964b363d Mon Sep 17 00:00:00 2001
From: Alex Macleod <alex@macleod.io>
Date: Fri, 16 Aug 2024 15:28:42 +0000
Subject: [PATCH 38/54] Fix code snippet in from_str_radix_10 docs

---
 clippy_lints/src/from_str_radix_10.rs | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clippy_lints/src/from_str_radix_10.rs b/clippy_lints/src/from_str_radix_10.rs
index 6ab7bbc2dfc0c..e5fa67d696427 100644
--- a/clippy_lints/src/from_str_radix_10.rs
+++ b/clippy_lints/src/from_str_radix_10.rs
@@ -22,8 +22,8 @@ declare_clippy_lint! {
     ///
     /// ### Known problems
     ///
-    /// This lint may suggest using (&<expression>).parse() instead of <expression>.parse() directly
-    /// in some cases, which is correct but adds unnecessary complexity to the code.
+    /// This lint may suggest using `(&<expression>).parse()` instead of `<expression>.parse()`
+    /// directly in some cases, which is correct but adds unnecessary complexity to the code.
     ///
     /// ### Example
     /// ```ignore

From 4f661302c6c5fee303248ea784eb66caa0b0e1cd Mon Sep 17 00:00:00 2001
From: Alex Macleod <alex@macleod.io>
Date: Sat, 17 Aug 2024 18:09:28 +0000
Subject: [PATCH 39/54] Replace `span_suggestion_with_style` with `_verbose`

---
 .../src/casts/cast_possible_truncation.rs         |  6 ++----
 clippy_lints/src/casts/fn_to_numeric_cast_any.rs  |  5 ++---
 clippy_lints/src/create_dir.rs                    |  5 ++---
 clippy_lints/src/doc/lazy_continuation.rs         | 11 ++++-------
 clippy_lints/src/doc/markdown.rs                  | 15 +++------------
 clippy_lints/src/float_literal.rs                 |  8 +++-----
 .../src/functions/impl_trait_in_params.rs         | 11 +++++------
 clippy_lints/src/implicit_return.rs               | 13 +++----------
 clippy_lints/src/legacy_numeric_constants.rs      |  5 ++---
 clippy_lints/src/methods/get_unwrap.rs            |  3 +--
 10 files changed, 27 insertions(+), 55 deletions(-)

diff --git a/clippy_lints/src/casts/cast_possible_truncation.rs b/clippy_lints/src/casts/cast_possible_truncation.rs
index 102fe25fc67b0..5708aae3f3eca 100644
--- a/clippy_lints/src/casts/cast_possible_truncation.rs
+++ b/clippy_lints/src/casts/cast_possible_truncation.rs
@@ -4,7 +4,7 @@ use clippy_utils::expr_or_init;
 use clippy_utils::source::snippet;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::{get_discriminant_value, is_isize_or_usize};
-use rustc_errors::{Applicability, Diag, SuggestionStyle};
+use rustc_errors::{Applicability, Diag};
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
@@ -190,12 +190,10 @@ fn offer_suggestion(
         format!("{cast_to_snip}::try_from({})", Sugg::hir(cx, cast_expr, ".."))
     };
 
-    diag.span_suggestion_with_style(
+    diag.span_suggestion_verbose(
         expr.span,
         "... or use `try_from` and handle the error accordingly",
         suggestion,
         Applicability::Unspecified,
-        // always show the suggestion in a separate line
-        SuggestionStyle::ShowAlways,
     );
 }
diff --git a/clippy_lints/src/casts/fn_to_numeric_cast_any.rs b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs
index 75de53f73ee7a..cb7267c2d1ce6 100644
--- a/clippy_lints/src/casts/fn_to_numeric_cast_any.rs
+++ b/clippy_lints/src/casts/fn_to_numeric_cast_any.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_with_applicability;
-use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_errors::Applicability;
 use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
@@ -24,12 +24,11 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>,
             expr.span,
             format!("casting function pointer `{from_snippet}` to `{cast_to}`"),
             |diag| {
-                diag.span_suggestion_with_style(
+                diag.span_suggestion_verbose(
                     expr.span,
                     "did you mean to invoke the function?",
                     format!("{from_snippet}() as {cast_to}"),
                     applicability,
-                    SuggestionStyle::ShowAlways,
                 );
             },
         );
diff --git a/clippy_lints/src/create_dir.rs b/clippy_lints/src/create_dir.rs
index b49a977dbeaf1..24570d8f4401c 100644
--- a/clippy_lints/src/create_dir.rs
+++ b/clippy_lints/src/create_dir.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_with_applicability;
-use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
@@ -46,7 +46,7 @@ impl LateLintPass<'_> for CreateDir {
                 "calling `std::fs::create_dir` where there may be a better way",
                 |diag| {
                     let mut app = Applicability::MaybeIncorrect;
-                    diag.span_suggestion_with_style(
+                    diag.span_suggestion_verbose(
                         expr.span,
                         "consider calling `std::fs::create_dir_all` instead",
                         format!(
@@ -54,7 +54,6 @@ impl LateLintPass<'_> for CreateDir {
                             snippet_with_applicability(cx, arg.span, "..", &mut app)
                         ),
                         app,
-                        SuggestionStyle::ShowAlways,
                     );
                 },
             );
diff --git a/clippy_lints/src/doc/lazy_continuation.rs b/clippy_lints/src/doc/lazy_continuation.rs
index bd1cc46e1850e..771bcac244187 100644
--- a/clippy_lints/src/doc/lazy_continuation.rs
+++ b/clippy_lints/src/doc/lazy_continuation.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use itertools::Itertools;
-use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_errors::Applicability;
 use rustc_lint::LateContext;
 use rustc_span::{BytePos, Span};
 use std::ops::Range;
@@ -59,12 +59,11 @@ pub(super) fn check(
                 && (doc_comment == "///" || doc_comment == "//!")
             {
                 // suggest filling in a blank line
-                diag.span_suggestion_with_style(
+                diag.span_suggestion_verbose(
                     line_break_span.shrink_to_lo(),
                     "if this should be its own paragraph, add a blank doc comment line",
                     format!("\n{doc_comment}"),
                     Applicability::MaybeIncorrect,
-                    SuggestionStyle::ShowAlways,
                 );
                 if ccount > 0 || blockquote_level > 0 {
                     diag.help("if this not intended to be a quote at all, escape it with `\\>`");
@@ -79,12 +78,11 @@ pub(super) fn check(
             if ccount == 0 && blockquote_level == 0 {
                 // simpler suggestion style for indentation
                 let indent = list_indentation - lcount;
-                diag.span_suggestion_with_style(
+                diag.span_suggestion_verbose(
                     span.shrink_to_hi(),
                     "indent this line",
                     std::iter::repeat(" ").take(indent).join(""),
                     Applicability::MaybeIncorrect,
-                    SuggestionStyle::ShowAlways,
                 );
                 diag.help("if this is supposed to be its own paragraph, add a blank line");
                 return;
@@ -107,12 +105,11 @@ pub(super) fn check(
                     suggested.push_str(text);
                 }
             }
-            diag.span_suggestion_with_style(
+            diag.span_suggestion_verbose(
                 span,
                 "add markers to start of line",
                 suggested,
                 Applicability::MachineApplicable,
-                SuggestionStyle::ShowAlways,
             );
             diag.help("if this not intended to be a quote at all, escape it with `\\>`");
         });
diff --git a/clippy_lints/src/doc/markdown.rs b/clippy_lints/src/doc/markdown.rs
index 237badb3f233c..8cdaba88e5095 100644
--- a/clippy_lints/src/doc/markdown.rs
+++ b/clippy_lints/src/doc/markdown.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::snippet_with_applicability;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_errors::Applicability;
 use rustc_lint::LateContext;
 use rustc_span::{BytePos, Pos, Span};
 use url::Url;
@@ -137,24 +137,15 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b
     }
 
     if has_underscore(word) || word.contains("::") || is_camel_case(word) || word.ends_with("()") {
-        let mut applicability = Applicability::MachineApplicable;
-
         span_lint_and_then(
             cx,
             DOC_MARKDOWN,
             span,
             "item in documentation is missing backticks",
             |diag| {
+                let mut applicability = Applicability::MachineApplicable;
                 let snippet = snippet_with_applicability(cx, span, "..", &mut applicability);
-                diag.span_suggestion_with_style(
-                    span,
-                    "try",
-                    format!("`{snippet}`"),
-                    applicability,
-                    // always show the suggestion in a separate line, since the
-                    // inline presentation adds another pair of backticks
-                    SuggestionStyle::ShowAlways,
-                );
+                diag.span_suggestion_verbose(span, "try", format!("`{snippet}`"), applicability);
             },
         );
     }
diff --git a/clippy_lints/src/float_literal.rs b/clippy_lints/src/float_literal.rs
index f095c1add91f8..012ad8e1a2298 100644
--- a/clippy_lints/src/float_literal.rs
+++ b/clippy_lints/src/float_literal.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::numeric_literal;
 use rustc_ast::ast::{self, LitFloatType, LitKind};
-use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, FloatTy};
@@ -117,12 +117,11 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
                             if type_suffix.is_none() {
                                 float_str.push_str(".0");
                             }
-                            diag.span_suggestion_with_style(
+                            diag.span_suggestion_verbose(
                                 expr.span,
                                 "consider changing the type or replacing it with",
                                 numeric_literal::format(&float_str, type_suffix, true),
                                 Applicability::MachineApplicable,
-                                SuggestionStyle::ShowAlways,
                             );
                         },
                     );
@@ -134,12 +133,11 @@ impl<'tcx> LateLintPass<'tcx> for FloatLiteral {
                     expr.span,
                     "float has excessive precision",
                     |diag| {
-                        diag.span_suggestion_with_style(
+                        diag.span_suggestion_verbose(
                             expr.span,
                             "consider changing the type or truncating it to",
                             numeric_literal::format(&float_str, type_suffix, true),
                             Applicability::MachineApplicable,
-                            SuggestionStyle::ShowAlways,
                         );
                     },
                 );
diff --git a/clippy_lints/src/functions/impl_trait_in_params.rs b/clippy_lints/src/functions/impl_trait_in_params.rs
index cf85c74e688d2..05e341e06fde4 100644
--- a/clippy_lints/src/functions/impl_trait_in_params.rs
+++ b/clippy_lints/src/functions/impl_trait_in_params.rs
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_in_test;
 
+use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Body, GenericParam, Generics, HirId, ImplItem, ImplItemKind, TraitItem, TraitItemKind};
@@ -18,20 +19,18 @@ fn report(cx: &LateContext<'_>, param: &GenericParam<'_>, generics: &Generics<'_
         |diag| {
             if let Some(gen_span) = generics.span_for_param_suggestion() {
                 // If there's already a generic param with the same bound, do not lint **this** suggestion.
-                diag.span_suggestion_with_style(
+                diag.span_suggestion_verbose(
                     gen_span,
                     "add a type parameter",
                     format!(", {{ /* Generic name */ }}: {}", &param.name.ident().as_str()[5..]),
-                    rustc_errors::Applicability::HasPlaceholders,
-                    rustc_errors::SuggestionStyle::ShowAlways,
+                    Applicability::HasPlaceholders,
                 );
             } else {
-                diag.span_suggestion_with_style(
+                diag.span_suggestion_verbose(
                     generics.span,
                     "add a type parameter",
                     format!("<{{ /* Generic name */ }}: {}>", &param.name.ident().as_str()[5..]),
-                    rustc_errors::Applicability::HasPlaceholders,
-                    rustc_errors::SuggestionStyle::ShowAlways,
+                    Applicability::HasPlaceholders,
                 );
             }
         },
diff --git a/clippy_lints/src/implicit_return.rs b/clippy_lints/src/implicit_return.rs
index b926e1e62ba0c..ba06567b9572c 100644
--- a/clippy_lints/src/implicit_return.rs
+++ b/clippy_lints/src/implicit_return.rs
@@ -3,7 +3,7 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context, wal
 use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{get_async_fn_body, is_async_fn, is_from_proc_macro};
 use core::ops::ControlFlow;
-use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Block, Body, Expr, ExprKind, FnDecl, FnRetTy, HirId};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -54,13 +54,7 @@ fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) {
         |diag| {
             let mut app = Applicability::MachineApplicable;
             let snip = snippet_with_applicability(cx, span, "..", &mut app);
-            diag.span_suggestion_with_style(
-                span,
-                "add `return` as shown",
-                format!("return {snip}"),
-                app,
-                SuggestionStyle::ShowAlways,
-            );
+            diag.span_suggestion_verbose(span, "add `return` as shown", format!("return {snip}"), app);
         },
     );
 }
@@ -75,12 +69,11 @@ fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, exp
         |diag| {
             let mut app = Applicability::MachineApplicable;
             let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0;
-            diag.span_suggestion_with_style(
+            diag.span_suggestion_verbose(
                 break_span,
                 "change `break` to `return` as shown",
                 format!("return {snip}"),
                 app,
-                SuggestionStyle::ShowAlways,
             );
         },
     );
diff --git a/clippy_lints/src/legacy_numeric_constants.rs b/clippy_lints/src/legacy_numeric_constants.rs
index 752e1326e3e05..ccab1e27d3b0c 100644
--- a/clippy_lints/src/legacy_numeric_constants.rs
+++ b/clippy_lints/src/legacy_numeric_constants.rs
@@ -3,7 +3,7 @@ use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::{get_parent_expr, is_from_proc_macro};
 use hir::def_id::DefId;
-use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::{ExprKind, Item, ItemKind, QPath, UseKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -143,12 +143,11 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants {
             && !is_from_proc_macro(cx, expr)
         {
             span_lint_hir_and_then(cx, LEGACY_NUMERIC_CONSTANTS, expr.hir_id, span, msg, |diag| {
-                diag.span_suggestion_with_style(
+                diag.span_suggestion_verbose(
                     span,
                     "use the associated constant instead",
                     sugg,
                     Applicability::MaybeIncorrect,
-                    SuggestionStyle::ShowAlways,
                 );
             });
         }
diff --git a/clippy_lints/src/methods/get_unwrap.rs b/clippy_lints/src/methods/get_unwrap.rs
index c6285c87a2636..9daad1a8a949e 100644
--- a/clippy_lints/src/methods/get_unwrap.rs
+++ b/clippy_lints/src/methods/get_unwrap.rs
@@ -74,7 +74,7 @@ pub(super) fn check<'tcx>(
                 "&"
             };
 
-            diag.span_suggestion_with_style(
+            diag.span_suggestion_verbose(
                 span,
                 "using `[]` is clearer and more concise",
                 format!(
@@ -82,7 +82,6 @@ pub(super) fn check<'tcx>(
                     snippet_with_applicability(cx, recv.span, "..", &mut applicability)
                 ),
                 applicability,
-                rustc_errors::SuggestionStyle::ShowAlways,
             );
         },
     );

From ed46141081597395b5299f8c7aaeb489e0465407 Mon Sep 17 00:00:00 2001
From: Alex Macleod <alex@macleod.io>
Date: Sat, 17 Aug 2024 18:10:46 +0000
Subject: [PATCH 40/54] Remove duplicate `type_diagnostic_name` function

---
 clippy_lints/src/eta_reduction.rs | 4 ++--
 clippy_utils/src/ty.rs            | 5 -----
 2 files changed, 2 insertions(+), 7 deletions(-)

diff --git a/clippy_lints/src/eta_reduction.rs b/clippy_lints/src/eta_reduction.rs
index 5a7226d590c4d..a77d09c378afe 100644
--- a/clippy_lints/src/eta_reduction.rs
+++ b/clippy_lints/src/eta_reduction.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::higher::VecArgs;
 use clippy_utils::source::snippet_opt;
-use clippy_utils::ty::type_diagnostic_name;
+use clippy_utils::ty::get_type_diagnostic_name;
 use clippy_utils::usage::{local_used_after_expr, local_used_in};
 use clippy_utils::{get_path_from_caller_to_method_type, is_adjusted, path_to_local, path_to_local_id};
 use rustc_errors::Applicability;
@@ -139,7 +139,7 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc
         {
             let callee_ty_raw = typeck.expr_ty(callee);
             let callee_ty = callee_ty_raw.peel_refs();
-            if matches!(type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc))
+            if matches!(get_type_diagnostic_name(cx, callee_ty), Some(sym::Arc | sym::Rc))
                 || !check_inputs(typeck, body.params, None, args)
             {
                 return;
diff --git a/clippy_utils/src/ty.rs b/clippy_utils/src/ty.rs
index bd48990aea95f..a557a7f31ce80 100644
--- a/clippy_utils/src/ty.rs
+++ b/clippy_utils/src/ty.rs
@@ -455,11 +455,6 @@ pub fn is_type_lang_item(cx: &LateContext<'_>, ty: Ty<'_>, lang_item: LangItem)
     }
 }
 
-/// Gets the diagnostic name of the type, if it has one
-pub fn type_diagnostic_name(cx: &LateContext<'_>, ty: Ty<'_>) -> Option<Symbol> {
-    ty.ty_adt_def().and_then(|adt| cx.tcx.get_diagnostic_name(adt.did()))
-}
-
 /// Return `true` if the passed `typ` is `isize` or `usize`.
 pub fn is_isize_or_usize(typ: Ty<'_>) -> bool {
     matches!(typ.kind(), ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize))

From 9aa656df9f6c68d14e613e56e3a6afac2644957e Mon Sep 17 00:00:00 2001
From: Alex Macleod <alex@macleod.io>
Date: Sat, 17 Aug 2024 18:17:53 +0000
Subject: [PATCH 41/54] Remove redundant doc comments

---
 clippy_lints/src/attrs/mod.rs                 |  2 --
 clippy_lints/src/cargo/common_metadata.rs     |  2 --
 .../src/cargo/multiple_crate_versions.rs      |  2 --
 clippy_lints/src/checked_conversions.rs       |  2 --
 clippy_lints/src/cognitive_complexity.rs      |  2 --
 clippy_lints/src/collapsible_if.rs            | 14 --------
 clippy_lints/src/else_if_without_else.rs      |  2 --
 clippy_lints/src/empty_enum.rs                |  2 --
 clippy_lints/src/enum_clike.rs                |  3 --
 clippy_lints/src/if_not_else.rs               |  3 --
 clippy_lints/src/indexing_slicing.rs          |  2 --
 clippy_lints/src/inherent_impl.rs             |  2 --
 clippy_lints/src/inline_fn_without_body.rs    |  2 --
 clippy_lints/src/int_plus_one.rs              |  2 --
 clippy_lints/src/item_name_repetitions.rs     |  2 --
 clippy_lints/src/items_after_statements.rs    |  2 --
 clippy_lints/src/large_enum_variant.rs        |  2 --
 clippy_lints/src/literal_representation.rs    |  3 --
 .../src/methods/is_digit_ascii_radix.rs       |  2 --
 clippy_lints/src/mutex_atomic.rs              |  4 ---
 clippy_lints/src/needless_bool.rs             |  4 ---
 clippy_lints/src/needless_continue.rs         | 35 -------------------
 clippy_lints/src/non_copy_const.rs            |  4 ---
 clippy_lints/src/ptr.rs                       |  2 --
 clippy_lints/src/size_of_in_element_count.rs  |  3 --
 clippy_lints/src/utils/author.rs              |  3 --
 26 files changed, 108 deletions(-)

diff --git a/clippy_lints/src/attrs/mod.rs b/clippy_lints/src/attrs/mod.rs
index 5d99001e5aa3f..3b14e9aee7fc0 100644
--- a/clippy_lints/src/attrs/mod.rs
+++ b/clippy_lints/src/attrs/mod.rs
@@ -1,5 +1,3 @@
-//! checks for attributes
-
 mod allow_attributes;
 mod allow_attributes_without_reason;
 mod blanket_clippy_restriction_lints;
diff --git a/clippy_lints/src/cargo/common_metadata.rs b/clippy_lints/src/cargo/common_metadata.rs
index 3af2d8c025684..fed0aa8b2758b 100644
--- a/clippy_lints/src/cargo/common_metadata.rs
+++ b/clippy_lints/src/cargo/common_metadata.rs
@@ -1,5 +1,3 @@
-//! lint on missing cargo common metadata
-
 use cargo_metadata::Metadata;
 use clippy_utils::diagnostics::span_lint;
 use rustc_lint::LateContext;
diff --git a/clippy_lints/src/cargo/multiple_crate_versions.rs b/clippy_lints/src/cargo/multiple_crate_versions.rs
index 2769463c8a53c..44cd1f7192fb1 100644
--- a/clippy_lints/src/cargo/multiple_crate_versions.rs
+++ b/clippy_lints/src/cargo/multiple_crate_versions.rs
@@ -1,5 +1,3 @@
-//! lint on multiple versions of a crate being used
-
 use cargo_metadata::{DependencyKind, Metadata, Node, Package, PackageId};
 use clippy_utils::diagnostics::span_lint;
 use itertools::Itertools;
diff --git a/clippy_lints/src/checked_conversions.rs b/clippy_lints/src/checked_conversions.rs
index 1711565fca891..dd7c34d1e4687 100644
--- a/clippy_lints/src/checked_conversions.rs
+++ b/clippy_lints/src/checked_conversions.rs
@@ -1,5 +1,3 @@
-//! lint on manually implemented checked conversions that could be transformed into `try_from`
-
 use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_sugg;
diff --git a/clippy_lints/src/cognitive_complexity.rs b/clippy_lints/src/cognitive_complexity.rs
index 5fa0522e4e5f9..0099eefbc8dc1 100644
--- a/clippy_lints/src/cognitive_complexity.rs
+++ b/clippy_lints/src/cognitive_complexity.rs
@@ -1,5 +1,3 @@
-//! calculate cognitive complexity and warn about overly complex functions
-
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::{IntoSpan, SpanRangeExt};
diff --git a/clippy_lints/src/collapsible_if.rs b/clippy_lints/src/collapsible_if.rs
index f311c052ad6ee..e73bfc6ebf7a1 100644
--- a/clippy_lints/src/collapsible_if.rs
+++ b/clippy_lints/src/collapsible_if.rs
@@ -1,17 +1,3 @@
-//! Checks for if expressions that contain only an if expression.
-//!
-//! For example, the lint would catch:
-//!
-//! ```rust,ignore
-//! if x {
-//!     if y {
-//!         println!("Hello world");
-//!     }
-//! }
-//! ```
-//!
-//! This lint is **warn** by default
-
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::source::{snippet, snippet_block, snippet_block_with_applicability};
 use clippy_utils::sugg::Sugg;
diff --git a/clippy_lints/src/else_if_without_else.rs b/clippy_lints/src/else_if_without_else.rs
index 02f9c2c36488d..5315f55ba3882 100644
--- a/clippy_lints/src/else_if_without_else.rs
+++ b/clippy_lints/src/else_if_without_else.rs
@@ -1,5 +1,3 @@
-//! Lint on if expressions with an else if, but without a final else branch.
-
 use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_ast::ast::{Expr, ExprKind};
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
diff --git a/clippy_lints/src/empty_enum.rs b/clippy_lints/src/empty_enum.rs
index 1869faab1d326..f4c55738cb83a 100644
--- a/clippy_lints/src/empty_enum.rs
+++ b/clippy_lints/src/empty_enum.rs
@@ -1,5 +1,3 @@
-//! lint when there is an enum with no variants
-
 use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
diff --git a/clippy_lints/src/enum_clike.rs b/clippy_lints/src/enum_clike.rs
index e54cd248ead29..4755cefe784c5 100644
--- a/clippy_lints/src/enum_clike.rs
+++ b/clippy_lints/src/enum_clike.rs
@@ -1,6 +1,3 @@
-//! lint on C-like enums that are `repr(isize/usize)` and have values that
-//! don't fit into an `i32`
-
 use clippy_utils::consts::{mir_to_const, Constant};
 use clippy_utils::diagnostics::span_lint;
 use rustc_hir::{Item, ItemKind};
diff --git a/clippy_lints/src/if_not_else.rs b/clippy_lints/src/if_not_else.rs
index 0ebd8d0c237b6..120c5396a1cd5 100644
--- a/clippy_lints/src/if_not_else.rs
+++ b/clippy_lints/src/if_not_else.rs
@@ -1,6 +1,3 @@
-//! lint on if branches that could be swapped so no `!` operation is necessary
-//! on the condition
-
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::is_else_clause;
diff --git a/clippy_lints/src/indexing_slicing.rs b/clippy_lints/src/indexing_slicing.rs
index 3ac50b8f1fba5..22e9674714fe5 100644
--- a/clippy_lints/src/indexing_slicing.rs
+++ b/clippy_lints/src/indexing_slicing.rs
@@ -1,5 +1,3 @@
-//! lint on indexing and slicing operations
-
 use clippy_config::Conf;
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
diff --git a/clippy_lints/src/inherent_impl.rs b/clippy_lints/src/inherent_impl.rs
index 9eed7aa924339..d39f910f9936d 100644
--- a/clippy_lints/src/inherent_impl.rs
+++ b/clippy_lints/src/inherent_impl.rs
@@ -1,5 +1,3 @@
-//! lint on inherent implementations
-
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_lint_allowed;
 use rustc_data_structures::fx::FxHashMap;
diff --git a/clippy_lints/src/inline_fn_without_body.rs b/clippy_lints/src/inline_fn_without_body.rs
index 5657c58bb0a47..1b900f6be8e85 100644
--- a/clippy_lints/src/inline_fn_without_body.rs
+++ b/clippy_lints/src/inline_fn_without_body.rs
@@ -1,5 +1,3 @@
-//! checks for `#[inline]` on trait methods without bodies
-
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::sugg::DiagExt;
 use rustc_errors::Applicability;
diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs
index b8e0eef7c7e9e..941caaab1f1bf 100644
--- a/clippy_lints/src/int_plus_one.rs
+++ b/clippy_lints/src/int_plus_one.rs
@@ -1,5 +1,3 @@
-//! lint on blocks unnecessarily using >= with a + 1 or - 1
-
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_opt;
 use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind};
diff --git a/clippy_lints/src/item_name_repetitions.rs b/clippy_lints/src/item_name_repetitions.rs
index 4d44bae02b87d..74bf82f58bdcf 100644
--- a/clippy_lints/src/item_name_repetitions.rs
+++ b/clippy_lints/src/item_name_repetitions.rs
@@ -1,5 +1,3 @@
-//! lint on enum variants that are prefixed or suffixed by the same characters
-
 use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_hir};
 use clippy_utils::is_bool;
diff --git a/clippy_lints/src/items_after_statements.rs b/clippy_lints/src/items_after_statements.rs
index a88d8e24fda8d..4f066113aea33 100644
--- a/clippy_lints/src/items_after_statements.rs
+++ b/clippy_lints/src/items_after_statements.rs
@@ -1,5 +1,3 @@
-//! lint when items are used after statements
-
 use clippy_utils::diagnostics::span_lint_hir;
 use rustc_hir::{Block, ItemKind, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
diff --git a/clippy_lints/src/large_enum_variant.rs b/clippy_lints/src/large_enum_variant.rs
index 225d79aa71d68..2f027c1170728 100644
--- a/clippy_lints/src/large_enum_variant.rs
+++ b/clippy_lints/src/large_enum_variant.rs
@@ -1,5 +1,3 @@
-//! lint when there is a large size difference between variants on an enum
-
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet_with_applicability;
diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs
index 259e4d6c08fb7..f51c593b2de9e 100644
--- a/clippy_lints/src/literal_representation.rs
+++ b/clippy_lints/src/literal_representation.rs
@@ -1,6 +1,3 @@
-//! Lints concerned with the grouping of digits with underscores in integral or
-//! floating-point literal expressions.
-
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::numeric_literal::{NumericLiteral, Radix};
diff --git a/clippy_lints/src/methods/is_digit_ascii_radix.rs b/clippy_lints/src/methods/is_digit_ascii_radix.rs
index 22d896433f07a..40b48ccca5d5d 100644
--- a/clippy_lints/src/methods/is_digit_ascii_radix.rs
+++ b/clippy_lints/src/methods/is_digit_ascii_radix.rs
@@ -1,5 +1,3 @@
-//! Lint for `c.is_digit(10)`
-
 use super::IS_DIGIT_ASCII_RADIX;
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{ConstEvalCtxt, FullInt};
diff --git a/clippy_lints/src/mutex_atomic.rs b/clippy_lints/src/mutex_atomic.rs
index 853e476a006c6..3884149645883 100644
--- a/clippy_lints/src/mutex_atomic.rs
+++ b/clippy_lints/src/mutex_atomic.rs
@@ -1,7 +1,3 @@
-//! Checks for usage of mutex where an atomic value could be used
-//!
-//! This lint is **allow** by default
-
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_hir::Expr;
diff --git a/clippy_lints/src/needless_bool.rs b/clippy_lints/src/needless_bool.rs
index 9cb4fa41c73f1..df155a7a41257 100644
--- a/clippy_lints/src/needless_bool.rs
+++ b/clippy_lints/src/needless_bool.rs
@@ -1,7 +1,3 @@
-//! Checks for needless boolean results of if-else expressions
-//!
-//! This lint is **warn** by default
-
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
diff --git a/clippy_lints/src/needless_continue.rs b/clippy_lints/src/needless_continue.rs
index b97cb4579ca7b..ce97370d4d9fa 100644
--- a/clippy_lints/src/needless_continue.rs
+++ b/clippy_lints/src/needless_continue.rs
@@ -1,38 +1,3 @@
-//! Checks for continue statements in loops that are redundant.
-//!
-//! For example, the lint would catch
-//!
-//! ```rust
-//! let mut a = 1;
-//! let x = true;
-//!
-//! while a < 5 {
-//!     a = 6;
-//!     if x {
-//!         // ...
-//!     } else {
-//!         continue;
-//!     }
-//!     println!("Hello, world");
-//! }
-//! ```
-//!
-//! And suggest something like this:
-//!
-//! ```rust
-//! let mut a = 1;
-//! let x = true;
-//!
-//! while a < 5 {
-//!     a = 6;
-//!     if x {
-//!         // ...
-//!         println!("Hello, world");
-//!     }
-//! }
-//! ```
-//!
-//! This lint is **warn** by default.
 use clippy_utils::diagnostics::span_lint_and_help;
 use clippy_utils::source::{indent_of, snippet, snippet_block};
 use rustc_ast::ast;
diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs
index 6915cd4061551..e50562d8e4973 100644
--- a/clippy_lints/src/non_copy_const.rs
+++ b/clippy_lints/src/non_copy_const.rs
@@ -1,7 +1,3 @@
-//! Checks for usage of const which the type is not `Freeze` (`Cell`-free).
-//!
-//! This lint is **warn** by default.
-
 use std::ptr;
 
 use clippy_config::Conf;
diff --git a/clippy_lints/src/ptr.rs b/clippy_lints/src/ptr.rs
index 1e824681fa12f..125f694996c1c 100644
--- a/clippy_lints/src/ptr.rs
+++ b/clippy_lints/src/ptr.rs
@@ -1,5 +1,3 @@
-//! Checks for usage of  `&Vec[_]` and `&String`.
-
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::expr_sig;
diff --git a/clippy_lints/src/size_of_in_element_count.rs b/clippy_lints/src/size_of_in_element_count.rs
index 01f0e3cfadbd4..7750d8909d362 100644
--- a/clippy_lints/src/size_of_in_element_count.rs
+++ b/clippy_lints/src/size_of_in_element_count.rs
@@ -1,6 +1,3 @@
-//! Lint on use of `size_of` or `size_of_val` of T in an expression
-//! expecting a count of T
-
 use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
diff --git a/clippy_lints/src/utils/author.rs b/clippy_lints/src/utils/author.rs
index 316c1f32d3a2a..0cce45290cfe7 100644
--- a/clippy_lints/src/utils/author.rs
+++ b/clippy_lints/src/utils/author.rs
@@ -1,6 +1,3 @@
-//! A group of attributes that can be attached to Rust code in order
-//! to generate a clippy lint detecting said code automatically.
-
 use clippy_utils::{get_attr, higher};
 use rustc_ast::ast::{LitFloatType, LitKind};
 use rustc_ast::LitIntType;

From 6993752607f75165f6b2fd41e7d622648e1a313c Mon Sep 17 00:00:00 2001
From: Alex Macleod <alex@macleod.io>
Date: Sat, 17 Aug 2024 18:42:21 +0000
Subject: [PATCH 42/54] Replace some iteration with
 `get_[type_]diagnostic_name`

---
 clippy_lints/src/collection_is_never_read.rs | 37 +++++++++-----------
 clippy_lints/src/functions/must_use.rs       |  8 ++---
 clippy_lints/src/infinite_iter.rs            | 33 ++++++++---------
 clippy_lints/src/manual_retain.rs            | 34 ++++++++----------
 clippy_lints/src/methods/needless_collect.rs |  9 ++---
 clippy_lints/src/mut_key.rs                  | 14 ++++----
 6 files changed, 60 insertions(+), 75 deletions(-)

diff --git a/clippy_lints/src/collection_is_never_read.rs b/clippy_lints/src/collection_is_never_read.rs
index eebda3ff76fd9..c6847411c75ab 100644
--- a/clippy_lints/src/collection_is_never_read.rs
+++ b/clippy_lints/src/collection_is_never_read.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
+use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item};
 use clippy_utils::visitors::{for_each_expr, Visitable};
 use clippy_utils::{get_enclosing_block, path_to_local_id};
 use core::ops::ControlFlow;
@@ -7,7 +7,6 @@ use rustc_hir::{Body, ExprKind, HirId, LangItem, LetStmt, Node, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::symbol::sym;
-use rustc_span::Symbol;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -44,24 +43,11 @@ declare_clippy_lint! {
 }
 declare_lint_pass!(CollectionIsNeverRead => [COLLECTION_IS_NEVER_READ]);
 
-// Add `String` here when it is added to diagnostic items
-static COLLECTIONS: [Symbol; 9] = [
-    sym::BTreeMap,
-    sym::BTreeSet,
-    sym::BinaryHeap,
-    sym::HashMap,
-    sym::HashSet,
-    sym::LinkedList,
-    sym::Option,
-    sym::Vec,
-    sym::VecDeque,
-];
-
 impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'tcx>) {
         // Look for local variables whose type is a container. Search surrounding block for read access.
         if let PatKind::Binding(_, local_id, _, _) = local.pat.kind
-            && match_acceptable_type(cx, local, &COLLECTIONS)
+            && match_acceptable_type(cx, local)
             && let Some(enclosing_block) = get_enclosing_block(cx, local.hir_id)
             && has_no_read_access(cx, local_id, enclosing_block)
         {
@@ -70,11 +56,22 @@ impl<'tcx> LateLintPass<'tcx> for CollectionIsNeverRead {
     }
 }
 
-fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>, collections: &[Symbol]) -> bool {
+fn match_acceptable_type(cx: &LateContext<'_>, local: &LetStmt<'_>) -> bool {
     let ty = cx.typeck_results().pat_ty(local.pat);
-    collections.iter().any(|&sym| is_type_diagnostic_item(cx, ty, sym))
-    // String type is a lang item but not a diagnostic item for now so we need a separate check
-        || is_type_lang_item(cx, ty, LangItem::String)
+    matches!(
+        get_type_diagnostic_name(cx, ty),
+        Some(
+            sym::BTreeMap
+                | sym::BTreeSet
+                | sym::BinaryHeap
+                | sym::HashMap
+                | sym::HashSet
+                | sym::LinkedList
+                | sym::Option
+                | sym::Vec
+                | sym::VecDeque
+        )
+    ) || is_type_lang_item(cx, ty, LangItem::String)
 }
 
 fn has_no_read_access<'tcx, T: Visitable<'tcx>>(cx: &LateContext<'tcx>, id: HirId, block: T) -> bool {
diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs
index b179d7b52492c..b008ce6892100 100644
--- a/clippy_lints/src/functions/must_use.rs
+++ b/clippy_lints/src/functions/must_use.rs
@@ -8,7 +8,7 @@ use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::{self, Ty};
-use rustc_span::{sym, Span, Symbol};
+use rustc_span::{sym, Span};
 
 use clippy_utils::attrs::is_proc_macro;
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
@@ -193,17 +193,13 @@ fn is_mutable_pat(cx: &LateContext<'_>, pat: &hir::Pat<'_>, tys: &mut DefIdSet)
     }
 }
 
-static KNOWN_WRAPPER_TYS: &[Symbol] = &[sym::Rc, sym::Arc];
-
 fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, tys: &mut DefIdSet) -> bool {
     match *ty.kind() {
         // primitive types are never mutable
         ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
         ty::Adt(adt, args) => {
             tys.insert(adt.did()) && !ty.is_freeze(cx.tcx, cx.param_env)
-                || KNOWN_WRAPPER_TYS
-                    .iter()
-                    .any(|&sym| cx.tcx.is_diagnostic_item(sym, adt.did()))
+                || matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Rc | sym::Arc))
                     && args.types().any(|ty| is_mutable_ty(cx, ty, tys))
         },
         ty::Tuple(args) => args.iter().any(|ty| is_mutable_ty(cx, ty, tys)),
diff --git a/clippy_lints/src/infinite_iter.rs b/clippy_lints/src/infinite_iter.rs
index 676d50c4951b1..71c317552ee1a 100644
--- a/clippy_lints/src/infinite_iter.rs
+++ b/clippy_lints/src/infinite_iter.rs
@@ -1,10 +1,10 @@
 use clippy_utils::diagnostics::span_lint;
 use clippy_utils::higher;
-use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
+use clippy_utils::ty::{get_type_diagnostic_name, implements_trait};
 use rustc_hir::{BorrowKind, Closure, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::symbol::{sym, Symbol};
+use rustc_span::symbol::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -210,18 +210,6 @@ const COMPLETING_METHODS: [(&str, usize); 12] = [
     ("product", 0),
 ];
 
-/// the paths of types that are known to be infinitely allocating
-const INFINITE_COLLECTORS: &[Symbol] = &[
-    sym::BinaryHeap,
-    sym::BTreeMap,
-    sym::BTreeSet,
-    sym::HashMap,
-    sym::HashSet,
-    sym::LinkedList,
-    sym::Vec,
-    sym::VecDeque,
-];
-
 fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
     match expr.kind {
         ExprKind::MethodCall(method, receiver, args, _) => {
@@ -248,10 +236,19 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
                 }
             } else if method.ident.name == sym!(collect) {
                 let ty = cx.typeck_results().expr_ty(expr);
-                if INFINITE_COLLECTORS
-                    .iter()
-                    .any(|diag_item| is_type_diagnostic_item(cx, ty, *diag_item))
-                {
+                if matches!(
+                    get_type_diagnostic_name(cx, ty),
+                    Some(
+                        sym::BinaryHeap
+                            | sym::BTreeMap
+                            | sym::BTreeSet
+                            | sym::HashMap
+                            | sym::HashSet
+                            | sym::LinkedList
+                            | sym::Vec
+                            | sym::VecDeque,
+                    )
+                ) {
                     return is_infinite(cx, receiver);
                 }
             }
diff --git a/clippy_lints/src/manual_retain.rs b/clippy_lints/src/manual_retain.rs
index d549470ed47e1..d4e53f8f74bd6 100644
--- a/clippy_lints/src/manual_retain.rs
+++ b/clippy_lints/src/manual_retain.rs
@@ -2,14 +2,14 @@ use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
-use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
+use clippy_utils::ty::{get_type_diagnostic_name, is_type_lang_item};
 use clippy_utils::{match_def_path, paths, SpanlessEq};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::def_id::DefId;
 use rustc_hir::ExprKind::Assign;
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{impl_lint_pass, RustcVersion};
+use rustc_session::impl_lint_pass;
 use rustc_span::symbol::sym;
 use rustc_span::Span;
 
@@ -20,16 +20,6 @@ const ACCEPTABLE_METHODS: [&[&str]; 5] = [
     &paths::SLICE_INTO,
     &paths::VEC_DEQUE_ITER,
 ];
-const ACCEPTABLE_TYPES: [(rustc_span::Symbol, Option<RustcVersion>); 7] = [
-    (sym::BinaryHeap, Some(msrvs::BINARY_HEAP_RETAIN)),
-    (sym::BTreeSet, Some(msrvs::BTREE_SET_RETAIN)),
-    (sym::BTreeMap, Some(msrvs::BTREE_MAP_RETAIN)),
-    (sym::HashSet, Some(msrvs::HASH_SET_RETAIN)),
-    (sym::HashMap, Some(msrvs::HASH_MAP_RETAIN)),
-    (sym::Vec, None),
-    (sym::VecDeque, None),
-];
-const MAP_TYPES: [rustc_span::Symbol; 2] = [sym::BTreeMap, sym::HashMap];
 
 declare_clippy_lint! {
     /// ### What it does
@@ -264,16 +254,22 @@ fn match_acceptable_def_path(cx: &LateContext<'_>, collect_def_id: DefId) -> boo
 }
 
 fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: &Msrv) -> bool {
-    let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs();
-    ACCEPTABLE_TYPES.iter().any(|(ty, acceptable_msrv)| {
-        is_type_diagnostic_item(cx, expr_ty, *ty)
-            && acceptable_msrv.map_or(true, |acceptable_msrv| msrv.meets(acceptable_msrv))
-    })
+    let ty = cx.typeck_results().expr_ty(expr).peel_refs();
+    let required = match get_type_diagnostic_name(cx, ty) {
+        Some(sym::BinaryHeap) => msrvs::BINARY_HEAP_RETAIN,
+        Some(sym::BTreeSet) => msrvs::BTREE_SET_RETAIN,
+        Some(sym::BTreeMap) => msrvs::BTREE_MAP_RETAIN,
+        Some(sym::HashSet) => msrvs::HASH_SET_RETAIN,
+        Some(sym::HashMap) => msrvs::HASH_MAP_RETAIN,
+        Some(sym::Vec | sym::VecDeque) => return true,
+        _ => return false,
+    };
+    msrv.meets(required)
 }
 
 fn match_map_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
-    let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs();
-    MAP_TYPES.iter().any(|ty| is_type_diagnostic_item(cx, expr_ty, *ty))
+    let ty = cx.typeck_results().expr_ty(expr).peel_refs();
+    matches!(get_type_diagnostic_name(cx, ty), Some(sym::BTreeMap | sym::HashMap))
 }
 
 fn make_span_lint_and_sugg(cx: &LateContext<'_>, span: Span, sugg: String) {
diff --git a/clippy_lints/src/methods/needless_collect.rs b/clippy_lints/src/methods/needless_collect.rs
index 46b457daf7074..f61923e5bf569 100644
--- a/clippy_lints/src/methods/needless_collect.rs
+++ b/clippy_lints/src/methods/needless_collect.rs
@@ -2,7 +2,7 @@ use super::NEEDLESS_COLLECT;
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::ty::{is_type_diagnostic_item, make_normalized_projection, make_projection};
+use clippy_utils::ty::{get_type_diagnostic_name, make_normalized_projection, make_projection};
 use clippy_utils::{
     can_move_expr_to_closure, fn_def_id, get_enclosing_block, higher, is_trait_method, path_to_local, path_to_local_id,
     CaptureKind,
@@ -88,9 +88,10 @@ pub(super) fn check<'tcx>(
         Node::LetStmt(l) => {
             if let PatKind::Binding(BindingMode::NONE | BindingMode::MUT, id, _, None) = l.pat.kind
                 && let ty = cx.typeck_results().expr_ty(collect_expr)
-                && [sym::Vec, sym::VecDeque, sym::BinaryHeap, sym::LinkedList]
-                    .into_iter()
-                    .any(|item| is_type_diagnostic_item(cx, ty, item))
+                && matches!(
+                    get_type_diagnostic_name(cx, ty),
+                    Some(sym::Vec | sym::VecDeque | sym::BinaryHeap | sym::LinkedList)
+                )
                 && let iter_ty = cx.typeck_results().expr_ty(iter_expr)
                 && let Some(block) = get_enclosing_block(cx, l.hir_id)
                 && let Some(iter_calls) = detect_iter_and_into_iters(block, id, cx, get_captured_ids(cx, iter_ty))
diff --git a/clippy_lints/src/mut_key.rs b/clippy_lints/src/mut_key.rs
index 83af9979b9e36..f52b3a6a5a160 100644
--- a/clippy_lints/src/mut_key.rs
+++ b/clippy_lints/src/mut_key.rs
@@ -125,14 +125,12 @@ impl<'tcx> MutableKeyType<'tcx> {
     // generics (because the compiler cannot ensure immutability for unknown types).
     fn check_ty_(&mut self, cx: &LateContext<'tcx>, span: Span, ty: Ty<'tcx>) {
         let ty = ty.peel_refs();
-        if let ty::Adt(def, args) = ty.kind() {
-            let is_keyed_type = [sym::HashMap, sym::BTreeMap, sym::HashSet, sym::BTreeSet]
-                .iter()
-                .any(|diag_item| cx.tcx.is_diagnostic_item(*diag_item, def.did()));
-            if !is_keyed_type {
-                return;
-            }
-
+        if let ty::Adt(def, args) = ty.kind()
+            && matches!(
+                cx.tcx.get_diagnostic_name(def.did()),
+                Some(sym::HashMap | sym::BTreeMap | sym::HashSet | sym::BTreeSet)
+            )
+        {
             let subst_ty = args.type_at(0);
             if self.interior_mut.is_interior_mut_ty(cx, subst_ty) {
                 span_lint(cx, MUTABLE_KEY_TYPE, span, "mutable key type");

From a2033428c4ca9883a2b7cbbd1f167d481b0d240c Mon Sep 17 00:00:00 2001
From: Guillaume Gomez <guillaume1.gomez@gmail.com>
Date: Sun, 18 Aug 2024 14:13:45 +0200
Subject: [PATCH 43/54] Do not emit `TOO_LONG_FIRST_DOC_PARAGRAPH` lint if item
 is generated from proc-macro and simplify code to emit lint

---
 .../src/doc/too_long_first_doc_paragraph.rs   | 39 +++++++++----------
 1 file changed, 18 insertions(+), 21 deletions(-)

diff --git a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
index a47cba64b28e5..7bb3bb12f2ca9 100644
--- a/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
+++ b/clippy_lints/src/doc/too_long_first_doc_paragraph.rs
@@ -3,7 +3,8 @@ use rustc_errors::Applicability;
 use rustc_hir::{Item, ItemKind};
 use rustc_lint::LateContext;
 
-use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::is_from_proc_macro;
 use clippy_utils::source::snippet_opt;
 
 use super::TOO_LONG_FIRST_DOC_PARAGRAPH;
@@ -63,32 +64,28 @@ pub(super) fn check(
     let &[first_span, .., last_span] = spans.as_slice() else {
         return;
     };
+    if is_from_proc_macro(cx, item) {
+        return;
+    }
 
-    if should_suggest_empty_doc
-        && let Some(second_span) = spans.get(1)
-        && let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi())
-        && let Some(snippet) = snippet_opt(cx, new_span)
-    {
-        span_lint_and_then(
-            cx,
-            TOO_LONG_FIRST_DOC_PARAGRAPH,
-            first_span.with_hi(last_span.lo()),
-            "first doc comment paragraph is too long",
-            |diag| {
+    span_lint_and_then(
+        cx,
+        TOO_LONG_FIRST_DOC_PARAGRAPH,
+        first_span.with_hi(last_span.lo()),
+        "first doc comment paragraph is too long",
+        |diag| {
+            if should_suggest_empty_doc
+                && let Some(second_span) = spans.get(1)
+                && let new_span = first_span.with_hi(second_span.lo()).with_lo(first_span.hi())
+                && let Some(snippet) = snippet_opt(cx, new_span)
+            {
                 diag.span_suggestion(
                     new_span,
                     "add an empty line",
                     format!("{snippet}///\n"),
                     Applicability::MachineApplicable,
                 );
-            },
-        );
-        return;
-    }
-    span_lint(
-        cx,
-        TOO_LONG_FIRST_DOC_PARAGRAPH,
-        first_span.with_hi(last_span.lo()),
-        "first doc comment paragraph is too long",
+            }
+        },
     );
 }

From 4e6fc6fa052d3278a5bf11ba805d292f5a5ecb4a Mon Sep 17 00:00:00 2001
From: sobolevn <mail@sobolevn.me>
Date: Sun, 18 Aug 2024 19:40:56 +0300
Subject: [PATCH 44/54] Improve `collapsible_match` error message syntax

---
 clippy_lints/src/matches/collapsible_match.rs | 2 +-
 tests/ui/collapsible_match.stderr             | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clippy_lints/src/matches/collapsible_match.rs b/clippy_lints/src/matches/collapsible_match.rs
index 90cfdecc19936..b666e77c09ecb 100644
--- a/clippy_lints/src/matches/collapsible_match.rs
+++ b/clippy_lints/src/matches/collapsible_match.rs
@@ -96,7 +96,7 @@ fn check_arm<'tcx>(
         // collapsing patterns need an explicit field name in struct pattern matching
         // ex: Struct {x: Some(1)}
         let replace_msg = if is_innermost_parent_pat_struct {
-            format!(", prefixed by {}:", snippet(cx, binding_span, "their field name"))
+            format!(", prefixed by `{}`:", snippet(cx, binding_span, "their field name"))
         } else {
             String::new()
         };
diff --git a/tests/ui/collapsible_match.stderr b/tests/ui/collapsible_match.stderr
index 01944baee79af..1da78b56239c7 100644
--- a/tests/ui/collapsible_match.stderr
+++ b/tests/ui/collapsible_match.stderr
@@ -223,7 +223,7 @@ help: the outer pattern can be modified to include the inner pattern
 LL |     if let Issue9647::A { a, .. } = x {
    |                           ^ replace this binding
 LL |         if let Some(u) = a {
-   |                ^^^^^^^ with this pattern, prefixed by a:
+   |                ^^^^^^^ with this pattern, prefixed by `a`:
 
 error: this `if let` can be collapsed into the outer `if let`
   --> tests/ui/collapsible_match.rs:292:9

From 43e1145c80d1c2f4e159885d382659ace3a1bc5d Mon Sep 17 00:00:00 2001
From: Ralf Jung <post@ralfj.de>
Date: Mon, 12 Aug 2024 10:57:57 +0200
Subject: [PATCH 45/54] rename AddressOf -> RawBorrow inside the compiler

---
 clippy_utils/src/qualify_min_const_fn.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clippy_utils/src/qualify_min_const_fn.rs b/clippy_utils/src/qualify_min_const_fn.rs
index 553af913ef9df..95fbf0b2ea20a 100644
--- a/clippy_utils/src/qualify_min_const_fn.rs
+++ b/clippy_utils/src/qualify_min_const_fn.rs
@@ -110,7 +110,7 @@ fn check_rvalue<'tcx>(
 ) -> McfResult {
     match rvalue {
         Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
-        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
+        Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::RawPtr(_, place) => {
             check_place(tcx, *place, span, body, msrv)
         },
         Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body, msrv),

From 1958e1acd46e791382219429cb7c38c97bef677d Mon Sep 17 00:00:00 2001
From: kyoto7250 <50972773+kyoto7250@users.noreply.github.com>
Date: Tue, 20 Aug 2024 01:51:32 +0900
Subject: [PATCH 46/54] fix false position in explicit_iter_loop with msrv

---
 clippy_config/src/msrvs.rs                   |  1 +
 clippy_lints/src/loops/explicit_iter_loop.rs | 12 ++++++++++--
 tests/ui/explicit_iter_loop.fixed            | 12 ++++++++++++
 tests/ui/explicit_iter_loop.rs               | 12 ++++++++++++
 4 files changed, 35 insertions(+), 2 deletions(-)

diff --git a/clippy_config/src/msrvs.rs b/clippy_config/src/msrvs.rs
index 5b9707efd2634..0e8215aa8e3cd 100644
--- a/clippy_config/src/msrvs.rs
+++ b/clippy_config/src/msrvs.rs
@@ -18,6 +18,7 @@ macro_rules! msrv_aliases {
 // names may refer to stabilized feature flags or library items
 msrv_aliases! {
     1,81,0  { LINT_REASONS_STABILIZATION }
+    1,80,0 { BOX_INTO_ITER}
     1,77,0 { C_STR_LITERALS }
     1,76,0 { PTR_FROM_REF, OPTION_RESULT_INSPECT }
     1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE }
diff --git a/clippy_lints/src/loops/explicit_iter_loop.rs b/clippy_lints/src/loops/explicit_iter_loop.rs
index eea5f2a94ea60..b134af500f540 100644
--- a/clippy_lints/src/loops/explicit_iter_loop.rs
+++ b/clippy_lints/src/loops/explicit_iter_loop.rs
@@ -3,7 +3,7 @@ use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{
-    implements_trait, implements_trait_with_env, is_copy, make_normalized_projection,
+    implements_trait, implements_trait_with_env, is_copy, is_type_lang_item, make_normalized_projection,
     make_normalized_projection_with_regions, normalize_with_regions,
 };
 use rustc_errors::Applicability;
@@ -20,9 +20,10 @@ pub(super) fn check(
     msrv: &Msrv,
     enforce_iter_loop_reborrow: bool,
 ) {
-    let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr, enforce_iter_loop_reborrow) else {
+    let Some((adjust, ty)) = is_ref_iterable(cx, self_arg, call_expr, enforce_iter_loop_reborrow, msrv) else {
         return;
     };
+
     if let ty::Array(_, count) = *ty.peel_refs().kind() {
         if !ty.is_ref() {
             if !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) {
@@ -109,6 +110,7 @@ fn is_ref_iterable<'tcx>(
     self_arg: &Expr<'_>,
     call_expr: &Expr<'_>,
     enforce_iter_loop_reborrow: bool,
+    msrv: &Msrv,
 ) -> Option<(AdjustKind, Ty<'tcx>)> {
     let typeck = cx.typeck_results();
     if let Some(trait_id) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
@@ -128,6 +130,12 @@ fn is_ref_iterable<'tcx>(
         let self_ty = typeck.expr_ty(self_arg);
         let self_is_copy = is_copy(cx, self_ty);
 
+        if !msrv.meets(msrvs::BOX_INTO_ITER)
+            && is_type_lang_item(cx, self_ty.peel_refs(), rustc_hir::LangItem::OwnedBox)
+        {
+            return None;
+        }
+
         if adjustments.is_empty() && self_is_copy {
             // Exact type match, already checked earlier
             return Some((AdjustKind::None, self_ty));
diff --git a/tests/ui/explicit_iter_loop.fixed b/tests/ui/explicit_iter_loop.fixed
index f38b34c5a7c47..1148f0f6c6a63 100644
--- a/tests/ui/explicit_iter_loop.fixed
+++ b/tests/ui/explicit_iter_loop.fixed
@@ -153,3 +153,15 @@ fn main() {
     let r = &x;
     for _ in r {}
 }
+
+#[clippy::msrv = "1.79"]
+pub fn issue_13184() {
+    // https://github.com/rust-lang/rust-clippy/issues/13184
+    // No need to fix, as IntoIterator for Box is valid starting from 1.80
+    let mut values: Box<[u32]> = Box::new([1, 2]);
+    for _ in values.iter() {}
+    for _ in values.iter_mut() {}
+
+    let rvalues = &values;
+    for _ in rvalues.iter() {}
+}
diff --git a/tests/ui/explicit_iter_loop.rs b/tests/ui/explicit_iter_loop.rs
index 2e701ada5ac6d..4dda2f13e5b83 100644
--- a/tests/ui/explicit_iter_loop.rs
+++ b/tests/ui/explicit_iter_loop.rs
@@ -153,3 +153,15 @@ fn main() {
     let r = &x;
     for _ in r.iter() {}
 }
+
+#[clippy::msrv = "1.79"]
+pub fn issue_13184() {
+    // https://github.com/rust-lang/rust-clippy/issues/13184
+    // No need to fix, as IntoIterator for Box is valid starting from 1.80
+    let mut values: Box<[u32]> = Box::new([1, 2]);
+    for _ in values.iter() {}
+    for _ in values.iter_mut() {}
+
+    let rvalues = &values;
+    for _ in rvalues.iter() {}
+}

From 7b7cf440cb5bc8733de38aeca8d42f3747bd45e1 Mon Sep 17 00:00:00 2001
From: Alexey Semenyuk <alexsemenyuk88@gmail.com>
Date: Sun, 18 Aug 2024 17:50:29 +0500
Subject: [PATCH 47/54] string_slice should detect on Cow

---
 clippy_lints/src/strings.rs  |  2 +-
 tests/ui/string_slice.rs     |  5 +++++
 tests/ui/string_slice.stderr | 14 ++++++++++----
 3 files changed, 16 insertions(+), 5 deletions(-)

diff --git a/clippy_lints/src/strings.rs b/clippy_lints/src/strings.rs
index cfc387886dc01..6ca4ca000e9b4 100644
--- a/clippy_lints/src/strings.rs
+++ b/clippy_lints/src/strings.rs
@@ -190,7 +190,7 @@ impl<'tcx> LateLintPass<'tcx> for StringAdd {
                 }
             },
             ExprKind::Index(target, _idx, _) => {
-                let e_ty = cx.typeck_results().expr_ty(target).peel_refs();
+                let e_ty = cx.typeck_results().expr_ty_adjusted(target).peel_refs();
                 if e_ty.is_str() || is_type_lang_item(cx, e_ty, LangItem::String) {
                     span_lint(
                         cx,
diff --git a/tests/ui/string_slice.rs b/tests/ui/string_slice.rs
index 440a86b104a3d..1d1911aaa1d91 100644
--- a/tests/ui/string_slice.rs
+++ b/tests/ui/string_slice.rs
@@ -1,3 +1,5 @@
+use std::borrow::Cow;
+
 #[warn(clippy::string_slice)]
 #[allow(clippy::no_effect)]
 
@@ -11,4 +13,7 @@ fn main() {
     let s = String::from(m);
     &s[0..2];
     //~^ ERROR: indexing into a string may panic if the index is within a UTF-8 character
+    let a = Cow::Borrowed("foo");
+    &a[0..3];
+    //~^ ERROR: indexing into a string may panic if the index is within a UTF-8 character
 }
diff --git a/tests/ui/string_slice.stderr b/tests/ui/string_slice.stderr
index 7a4596b5f2d25..bc0fcde34b826 100644
--- a/tests/ui/string_slice.stderr
+++ b/tests/ui/string_slice.stderr
@@ -1,5 +1,5 @@
 error: indexing into a string may panic if the index is within a UTF-8 character
-  --> tests/ui/string_slice.rs:5:6
+  --> tests/ui/string_slice.rs:7:6
    |
 LL |     &"Ölkanne"[1..];
    |      ^^^^^^^^^^^^^^
@@ -8,16 +8,22 @@ LL |     &"Ölkanne"[1..];
    = help: to override `-D warnings` add `#[allow(clippy::string_slice)]`
 
 error: indexing into a string may panic if the index is within a UTF-8 character
-  --> tests/ui/string_slice.rs:9:6
+  --> tests/ui/string_slice.rs:11:6
    |
 LL |     &m[2..5];
    |      ^^^^^^^
 
 error: indexing into a string may panic if the index is within a UTF-8 character
-  --> tests/ui/string_slice.rs:12:6
+  --> tests/ui/string_slice.rs:14:6
    |
 LL |     &s[0..2];
    |      ^^^^^^^
 
-error: aborting due to 3 previous errors
+error: indexing into a string may panic if the index is within a UTF-8 character
+  --> tests/ui/string_slice.rs:17:6
+   |
+LL |     &a[0..3];
+   |      ^^^^^^^
+
+error: aborting due to 4 previous errors
 

From fc8a025f171a4cf8e5839c2bd5fa33c25357e2a0 Mon Sep 17 00:00:00 2001
From: Milo Moisson <milomoisson@gmail.com>
Date: Tue, 20 Aug 2024 12:04:28 +0200
Subject: [PATCH 48/54] fix(cfg_not_test): lint description typo

---
 clippy_lints/src/cfg_not_test.rs | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/clippy_lints/src/cfg_not_test.rs b/clippy_lints/src/cfg_not_test.rs
index b54f392bf2fad..d820c1e0720b8 100644
--- a/clippy_lints/src/cfg_not_test.rs
+++ b/clippy_lints/src/cfg_not_test.rs
@@ -5,7 +5,7 @@ use rustc_session::declare_lint_pass;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for usage of `cfg` that excludes code from `test` builds. (i.e., `#{cfg(not(test))]`)
+    /// Checks for usage of `cfg` that excludes code from `test` builds. (i.e., `#[cfg(not(test))]`)
     ///
     /// ### Why is this bad?
     /// This may give the false impression that a codebase has 100% coverage, yet actually has untested code.

From 687d4e3d16d946a7b2a721db18a357cfc877422e Mon Sep 17 00:00:00 2001
From: Jason Newcomb <jsnewcomb@pm.me>
Date: Tue, 20 Aug 2024 11:52:47 -0400
Subject: [PATCH 49/54] `declare_interior_mutable_const`: Ignore pointer types.

---
 clippy_lints/src/non_copy_const.rs              |  2 +-
 .../ui/declare_interior_mutable_const/others.rs | 17 +++++++++++++++++
 .../others.stderr                               | 10 +++++-----
 3 files changed, 23 insertions(+), 6 deletions(-)

diff --git a/clippy_lints/src/non_copy_const.rs b/clippy_lints/src/non_copy_const.rs
index e50562d8e4973..25f547f1a43c0 100644
--- a/clippy_lints/src/non_copy_const.rs
+++ b/clippy_lints/src/non_copy_const.rs
@@ -184,7 +184,7 @@ impl_lint_pass!(NonCopyConst<'_> => [DECLARE_INTERIOR_MUTABLE_CONST, BORROW_INTE
 impl<'tcx> NonCopyConst<'tcx> {
     pub fn new(tcx: TyCtxt<'tcx>, conf: &'static Conf) -> Self {
         Self {
-            interior_mut: InteriorMut::new(tcx, &conf.ignore_interior_mutability),
+            interior_mut: InteriorMut::without_pointers(tcx, &conf.ignore_interior_mutability),
         }
     }
 
diff --git a/tests/ui/declare_interior_mutable_const/others.rs b/tests/ui/declare_interior_mutable_const/others.rs
index 56a8d22cb1c81..9dafad8b784b9 100644
--- a/tests/ui/declare_interior_mutable_const/others.rs
+++ b/tests/ui/declare_interior_mutable_const/others.rs
@@ -3,6 +3,7 @@
 use std::borrow::Cow;
 use std::cell::Cell;
 use std::fmt::Display;
+use std::ptr;
 use std::sync::atomic::AtomicUsize;
 use std::sync::Once;
 
@@ -53,4 +54,20 @@ mod issue_8493 {
     issue_8493!();
 }
 
+#[repr(C, align(8))]
+struct NoAtomic(usize);
+#[repr(C, align(8))]
+struct WithAtomic(AtomicUsize);
+
+const fn with_non_null() -> *const WithAtomic {
+    const NO_ATOMIC: NoAtomic = NoAtomic(0);
+    (&NO_ATOMIC as *const NoAtomic).cast()
+}
+const WITH_ATOMIC: *const WithAtomic = with_non_null();
+
+struct Generic<T>(T);
+impl<T> Generic<T> {
+    const RAW_POINTER: *const Cell<T> = ptr::null();
+}
+
 fn main() {}
diff --git a/tests/ui/declare_interior_mutable_const/others.stderr b/tests/ui/declare_interior_mutable_const/others.stderr
index 1f2b9561ce509..4a7251471424e 100644
--- a/tests/ui/declare_interior_mutable_const/others.stderr
+++ b/tests/ui/declare_interior_mutable_const/others.stderr
@@ -1,5 +1,5 @@
 error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/others.rs:9:1
+  --> tests/ui/declare_interior_mutable_const/others.rs:10:1
    |
 LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5);
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -9,7 +9,7 @@ LL | const ATOMIC: AtomicUsize = AtomicUsize::new(5);
    = help: to override `-D warnings` add `#[allow(clippy::declare_interior_mutable_const)]`
 
 error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/others.rs:10:1
+  --> tests/ui/declare_interior_mutable_const/others.rs:11:1
    |
 LL | const CELL: Cell<usize> = Cell::new(6);
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -17,7 +17,7 @@ LL | const CELL: Cell<usize> = Cell::new(6);
    = help: consider making this `Sync` so that it can go in a static item or using a `thread_local`
 
 error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/others.rs:11:1
+  --> tests/ui/declare_interior_mutable_const/others.rs:12:1
    |
 LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], Vec::new(), 7);
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -25,7 +25,7 @@ LL | const ATOMIC_TUPLE: ([AtomicUsize; 1], Vec<AtomicUsize>, u8) = ([ATOMIC], V
    = help: consider making this a static item
 
 error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/others.rs:16:9
+  --> tests/ui/declare_interior_mutable_const/others.rs:17:9
    |
 LL |         const $name: $ty = $e;
    |         ^^^^^^^^^^^^^^^^^^^^^^
@@ -36,7 +36,7 @@ LL | declare_const!(_ONCE: Once = Once::new());
    = note: this error originates in the macro `declare_const` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: a `const` item should not be interior mutable
-  --> tests/ui/declare_interior_mutable_const/others.rs:44:13
+  --> tests/ui/declare_interior_mutable_const/others.rs:45:13
    |
 LL |             const _BAZ: Cell<usize> = Cell::new(0);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

From 3f97261fbaff449c86c1ff97d0c342a567527c98 Mon Sep 17 00:00:00 2001
From: Bruce Mitchener <bruce.mitchener@gmail.com>
Date: Wed, 21 Aug 2024 21:42:57 +0700
Subject: [PATCH 50/54] Fix a couple of typos.

---
 clippy_lints/src/returns.rs  | 2 +-
 clippy_lints/src/use_self.rs | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/clippy_lints/src/returns.rs b/clippy_lints/src/returns.rs
index ffc9475cf9cdf..5ce091b7a7a30 100644
--- a/clippy_lints/src/returns.rs
+++ b/clippy_lints/src/returns.rs
@@ -399,7 +399,7 @@ fn check_final_expr<'tcx>(
 
             // Returns may be used to turn an expression into a statement in rustc's AST.
             // This allows the addition of attributes, like `#[allow]` (See: clippy#9361)
-            // `#[expect(clippy::needless_return)]` needs to be handled separatly to
+            // `#[expect(clippy::needless_return)]` needs to be handled separately to
             // actually fulfill the expectation (clippy::#12998)
             match cx.tcx.hir().attrs(expr.hir_id) {
                 [] => {},
diff --git a/clippy_lints/src/use_self.rs b/clippy_lints/src/use_self.rs
index 93785b45c27d4..5da48f4f7fbd7 100644
--- a/clippy_lints/src/use_self.rs
+++ b/clippy_lints/src/use_self.rs
@@ -229,7 +229,7 @@ impl<'tcx> LateLintPass<'tcx> for UseSelf {
             && let impl_ty = cx.tcx.type_of(impl_id).instantiate_identity()
             && same_type_and_consts(ty, impl_ty)
             // Ensure the type we encounter and the one from the impl have the same lifetime parameters. It may be that
-            // the lifetime parameters of `ty` are ellided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`, in
+            // the lifetime parameters of `ty` are elided (`impl<'a> Foo<'a> { fn new() -> Self { Foo{..} } }`, in
             // which case we must still trigger the lint.
             && (has_no_lifetime(ty) || same_lifetimes(ty, impl_ty))
         {

From 8a4c34a5b7f084f64ab20a4120c61521a1a2b662 Mon Sep 17 00:00:00 2001
From: Jason Newcomb <jsnewcomb@pm.me>
Date: Fri, 9 Aug 2024 09:04:18 -0400
Subject: [PATCH 51/54] Start removing `snippet_opt`

---
 clippy_lints/src/booleans.rs                  | 33 +++++++------
 clippy_lints/src/borrow_deref_ref.rs          |  7 +--
 clippy_lints/src/format.rs                    |  4 +-
 clippy_lints/src/format_args.rs               |  4 +-
 clippy_lints/src/from_over_into.rs            | 12 ++---
 clippy_lints/src/int_plus_one.rs              |  6 +--
 clippy_lints/src/items_after_test_module.rs   |  9 ++--
 clippy_lints/src/large_stack_frames.rs        |  4 +-
 clippy_lints/src/len_zero.rs                  |  4 +-
 clippy_lints/src/literal_representation.rs    |  6 +--
 clippy_lints/src/manual_float_methods.rs      |  4 +-
 clippy_lints/src/manual_hash_one.rs           |  6 +--
 clippy_lints/src/manual_non_exhaustive.rs     |  6 +--
 clippy_lints/src/manual_range_patterns.rs     |  6 +--
 .../src/methods/unnecessary_iter_cloned.rs    |  8 +--
 .../src/misc_early/unneeded_field_pattern.rs  | 27 +++++-----
 .../src/missing_enforced_import_rename.rs     |  4 +-
 clippy_lints/src/needless_if.rs               |  4 +-
 clippy_lints/src/needless_late_init.rs        | 11 +++--
 clippy_lints/src/no_effect.rs                 | 47 +++++++++---------
 clippy_lints/src/nonstandard_macro_braces.rs  |  6 +--
 .../src/operators/assign_op_pattern.rs        |  6 +--
 .../src/operators/misrefactored_assign_op.rs  |  6 ++-
 .../src/operators/needless_bitwise_bool.rs    |  6 +--
 clippy_lints/src/operators/ptr_eq.rs          |  6 +--
 clippy_lints/src/unit_types/unit_arg.rs       | 20 ++++----
 .../src/utils/format_args_collector.rs        | 49 ++++++-------------
 clippy_utils/src/attrs.rs                     | 38 +++++++-------
 clippy_utils/src/hir_utils.rs                 | 18 +++----
 29 files changed, 177 insertions(+), 190 deletions(-)

diff --git a/clippy_lints/src/booleans.rs b/clippy_lints/src/booleans.rs
index a2f48c18170a6..e933fdf1d6e6e 100644
--- a/clippy_lints/src/booleans.rs
+++ b/clippy_lints/src/booleans.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::eq_expr_value;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -134,28 +134,30 @@ fn check_inverted_bool_in_condition(
 
     let suggestion = match (left.kind, right.kind) {
         (ExprKind::Unary(UnOp::Not, left_sub), ExprKind::Unary(UnOp::Not, right_sub)) => {
-            let Some(left) = snippet_opt(cx, left_sub.span) else {
+            let Some(left) = left_sub.span.get_source_text(cx) else {
                 return;
             };
-            let Some(right) = snippet_opt(cx, right_sub.span) else {
+            let Some(right) = right_sub.span.get_source_text(cx) else {
                 return;
             };
             let Some(op) = bin_op_eq_str(op) else { return };
             format!("{left} {op} {right}")
         },
         (ExprKind::Unary(UnOp::Not, left_sub), _) => {
-            let Some(left) = snippet_opt(cx, left_sub.span) else {
+            let Some(left) = left_sub.span.get_source_text(cx) else {
                 return;
             };
-            let Some(right) = snippet_opt(cx, right.span) else {
+            let Some(right) = right.span.get_source_text(cx) else {
                 return;
             };
             let Some(op) = inverted_bin_op_eq_str(op) else { return };
             format!("{left} {op} {right}")
         },
         (_, ExprKind::Unary(UnOp::Not, right_sub)) => {
-            let Some(left) = snippet_opt(cx, left.span) else { return };
-            let Some(right) = snippet_opt(cx, right_sub.span) else {
+            let Some(left) = left.span.get_source_text(cx) else {
+                return;
+            };
+            let Some(right) = right_sub.span.get_source_text(cx) else {
                 return;
             };
             let Some(op) = inverted_bin_op_eq_str(op) else { return };
@@ -313,8 +315,7 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
                         self.output.push_str(&str);
                     } else {
                         self.output.push('!');
-                        let snip = snippet_opt(self.cx, terminal.span)?;
-                        self.output.push_str(&snip);
+                        self.output.push_str(&terminal.span.get_source_text(self.cx)?);
                     }
                 },
                 True | False | Not(_) => {
@@ -345,8 +346,12 @@ impl<'a, 'tcx, 'v> SuggestContext<'a, 'tcx, 'v> {
                 }
             },
             &Term(n) => {
-                let snip = snippet_opt(self.cx, self.terminals[n as usize].span.source_callsite())?;
-                self.output.push_str(&snip);
+                self.output.push_str(
+                    &self.terminals[n as usize]
+                        .span
+                        .source_callsite()
+                        .get_source_text(self.cx)?,
+                );
             },
         }
         Some(())
@@ -370,8 +375,8 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
                 _ => None,
             }
             .and_then(|op| {
-                let lhs_snippet = snippet_opt(cx, lhs.span)?;
-                let rhs_snippet = snippet_opt(cx, rhs.span)?;
+                let lhs_snippet = lhs.span.get_source_text(cx)?;
+                let rhs_snippet = rhs.span.get_source_text(cx)?;
 
                 if !(lhs_snippet.starts_with('(') && lhs_snippet.ends_with(')')) {
                     if let (ExprKind::Cast(..), BinOpKind::Ge) = (&lhs.kind, binop.node) {
@@ -399,7 +404,7 @@ fn simplify_not(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
                     let path: &str = path.ident.name.as_str();
                     a == path
                 })
-                .and_then(|(_, neg_method)| Some(format!("{}.{neg_method}()", snippet_opt(cx, receiver.span)?)))
+                .and_then(|(_, neg_method)| Some(format!("{}.{neg_method}()", receiver.span.get_source_text(cx)?)))
         },
         _ => None,
     }
diff --git a/clippy_lints/src/borrow_deref_ref.rs b/clippy_lints/src/borrow_deref_ref.rs
index bd123a725a736..cba8224b84c9e 100644
--- a/clippy_lints/src/borrow_deref_ref.rs
+++ b/clippy_lints/src/borrow_deref_ref.rs
@@ -1,6 +1,6 @@
 use crate::reference::DEREF_ADDROF;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::implements_trait;
 use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed};
 use rustc_errors::Applicability;
@@ -73,6 +73,7 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
                 }
             })
             && !is_from_proc_macro(cx, e)
+            && let Some(deref_text) = deref_target.span.get_source_text(cx)
         {
             span_lint_and_then(
                 cx,
@@ -83,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
                     diag.span_suggestion(
                         e.span,
                         "if you would like to reborrow, try removing `&*`",
-                        snippet_opt(cx, deref_target.span).unwrap(),
+                        deref_text.as_str(),
                         Applicability::MachineApplicable,
                     );
 
@@ -98,7 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
                     diag.span_suggestion(
                         e.span,
                         "if you would like to deref, try using `&**`",
-                        format!("&**{}", &snippet_opt(cx, deref_target.span).unwrap()),
+                        format!("&**{deref_text}"),
                         Applicability::MaybeIncorrect,
                     );
                 },
diff --git a/clippy_lints/src/format.rs b/clippy_lints/src/format.rs
index c14bd38a1f21c..4dedff69b9abd 100644
--- a/clippy_lints/src/format.rs
+++ b/clippy_lints/src/format.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::macros::{find_format_arg_expr, root_macro_call_first_node, FormatArgsStorage};
-use clippy_utils::source::{snippet_opt, snippet_with_context};
+use clippy_utils::source::{snippet_with_context, SpanRangeExt};
 use clippy_utils::sugg::Sugg;
 use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait};
 use rustc_errors::Applicability;
@@ -65,7 +65,7 @@ impl<'tcx> LateLintPass<'tcx> for UselessFormat {
                 ([], []) => span_useless_format_empty(cx, call_site, "String::new()".to_owned(), applicability),
                 ([], [_]) => {
                     // Simulate macro expansion, converting {{ and }} to { and }.
-                    let Some(snippet) = snippet_opt(cx, format_args.span) else {
+                    let Some(snippet) = format_args.span.get_source_text(cx) else {
                         return;
                     };
                     let s_expand = snippet.replace("{{", "{").replace("}}", "}");
diff --git a/clippy_lints/src/format_args.rs b/clippy_lints/src/format_args.rs
index 823992767e508..020c0c5440d93 100644
--- a/clippy_lints/src/format_args.rs
+++ b/clippy_lints/src/format_args.rs
@@ -7,7 +7,7 @@ use clippy_utils::macros::{
     find_format_arg_expr, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, is_format_macro,
     is_panic, matching_root_macro_call, root_macro_call_first_node, FormatArgsStorage, FormatParamUsage, MacroCall,
 };
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::{implements_trait, is_type_lang_item};
 use itertools::Itertools;
 use rustc_ast::{
@@ -407,7 +407,7 @@ impl<'a, 'tcx> FormatArgsExpr<'a, 'tcx> {
                 count_needed_derefs(receiver_ty, cx.typeck_results().expr_adjustments(receiver).iter())
             && implements_trait(cx, target, display_trait_id, &[])
             && let Some(sized_trait_id) = cx.tcx.lang_items().sized_trait()
-            && let Some(receiver_snippet) = snippet_opt(cx, receiver.span.source_callsite())
+            && let Some(receiver_snippet) = receiver.span.source_callsite().get_source_text(cx)
         {
             let needs_ref = !implements_trait(cx, receiver_ty, sized_trait_id, &[]);
             if n_needed_derefs == 0 && !needs_ref {
diff --git a/clippy_lints/src/from_over_into.rs b/clippy_lints/src/from_over_into.rs
index b6c8437fb8f73..5e9098474dc02 100644
--- a/clippy_lints/src/from_over_into.rs
+++ b/clippy_lints/src/from_over_into.rs
@@ -3,7 +3,7 @@ use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::span_is_local;
 use clippy_utils::path_def_id;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::{walk_path, Visitor};
 use rustc_hir::{
@@ -178,8 +178,8 @@ fn convert_to_from(
         return None;
     };
 
-    let from = snippet_opt(cx, self_ty.span)?;
-    let into = snippet_opt(cx, target_ty.span)?;
+    let from = self_ty.span.get_source_text(cx)?;
+    let into = target_ty.span.get_source_text(cx)?;
 
     let return_type = matches!(sig.decl.output, FnRetTy::Return(_))
         .then_some(String::from("Self"))
@@ -190,10 +190,10 @@ fn convert_to_from(
         (into_trait_seg.ident.span, String::from("From")),
         // impl Into<T> for U  ->  impl Into<U> for U
         //           ~                       ~
-        (target_ty.span, from.clone()),
+        (target_ty.span, from.to_owned()),
         // impl Into<T> for U  ->  impl Into<T> for T
         //                  ~                       ~
-        (self_ty.span, into),
+        (self_ty.span, into.to_owned()),
         // fn into(self) -> T  ->  fn from(self) -> T
         //    ~~~~                    ~~~~
         (impl_item.ident.span, String::from("from")),
@@ -223,7 +223,7 @@ fn convert_to_from(
     }
 
     for span in finder.upper {
-        suggestions.push((span, from.clone()));
+        suggestions.push((span, from.to_owned()));
     }
     for span in finder.lower {
         suggestions.push((span, String::from("val")));
diff --git a/clippy_lints/src/int_plus_one.rs b/clippy_lints/src/int_plus_one.rs
index 941caaab1f1bf..fc575bff7e63f 100644
--- a/clippy_lints/src/int_plus_one.rs
+++ b/clippy_lints/src/int_plus_one.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_ast::ast::{BinOpKind, Expr, ExprKind, LitKind};
 use rustc_ast::token;
 use rustc_errors::Applicability;
@@ -130,8 +130,8 @@ impl IntPlusOne {
             BinOpKind::Le => "<",
             _ => return None,
         };
-        if let Some(snippet) = snippet_opt(cx, node.span) {
-            if let Some(other_side_snippet) = snippet_opt(cx, other_side.span) {
+        if let Some(snippet) = node.span.get_source_text(cx) {
+            if let Some(other_side_snippet) = other_side.span.get_source_text(cx) {
                 let rec = match side {
                     Side::Lhs => Some(format!("{snippet} {binop_string} {other_side_snippet}")),
                     Side::Rhs => Some(format!("{other_side_snippet} {binop_string} {snippet}")),
diff --git a/clippy_lints/src/items_after_test_module.rs b/clippy_lints/src/items_after_test_module.rs
index 3614fb8cc963b..8caa484bb9934 100644
--- a/clippy_lints/src/items_after_test_module.rs
+++ b/clippy_lints/src/items_after_test_module.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{fulfill_or_allowed, is_cfg_test, is_from_proc_macro};
 use rustc_errors::{Applicability, SuggestionStyle};
 use rustc_hir::{HirId, Item, ItemKind, Mod};
@@ -93,11 +93,14 @@ impl LateLintPass<'_> for ItemsAfterTestModule {
                     if let Some(prev) = mod_pos.checked_sub(1)
                         && let prev = cx.tcx.hir().item(module.item_ids[prev])
                         && let items_span = last.span.with_lo(test_mod.span.hi())
-                        && let Some(items) = snippet_opt(cx, items_span)
+                        && let Some(items) = items_span.get_source_text(cx)
                     {
                         diag.multipart_suggestion_with_style(
                             "move the items to before the test module was defined",
-                            vec![(prev.span.shrink_to_hi(), items), (items_span, String::new())],
+                            vec![
+                                (prev.span.shrink_to_hi(), items.to_owned()),
+                                (items_span, String::new()),
+                            ],
                             Applicability::MachineApplicable,
                             SuggestionStyle::HideCodeAlways,
                         );
diff --git a/clippy_lints/src/large_stack_frames.rs b/clippy_lints/src/large_stack_frames.rs
index 4abf7edc9b4e5..d2bdf194adad9 100644
--- a/clippy_lints/src/large_stack_frames.rs
+++ b/clippy_lints/src/large_stack_frames.rs
@@ -3,7 +3,7 @@ use std::{fmt, ops};
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::fn_has_unsatisfiable_preds;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Body, FnDecl};
@@ -186,7 +186,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackFrames {
                         // TODO: Is there a cleaner, robust way to ask this question?
                         // The obvious `LocalDecl::is_user_variable()` panics on "unwrapping cross-crate data",
                         // and that doesn't get us the true name in scope rather than the span text either.
-                        if let Some(name) = snippet_opt(cx, local_span)
+                        if let Some(name) = local_span.get_source_text(cx)
                             && is_ident(&name)
                         {
                             // If the local is an ordinary named variable,
diff --git a/clippy_lints/src/len_zero.rs b/clippy_lints/src/len_zero.rs
index 4c737371bd238..0bc7fc2dd9d1e 100644
--- a/clippy_lints/src/len_zero.rs
+++ b/clippy_lints/src/len_zero.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::{snippet_opt, snippet_with_context};
+use clippy_utils::source::{snippet_with_context, SpanRangeExt};
 use clippy_utils::sugg::{has_enclosing_paren, Sugg};
 use clippy_utils::{get_item_name, get_parent_as_impl, is_lint_allowed, peel_ref_operators};
 use rustc_ast::ast::LitKind;
@@ -216,7 +216,7 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
 }
 
 fn span_without_enclosing_paren(cx: &LateContext<'_>, span: Span) -> Span {
-    let Some(snippet) = snippet_opt(cx, span) else {
+    let Some(snippet) = span.get_source_text(cx) else {
         return span;
     };
     if has_enclosing_paren(snippet) {
diff --git a/clippy_lints/src/literal_representation.rs b/clippy_lints/src/literal_representation.rs
index f51c593b2de9e..81f2a03fb55fb 100644
--- a/clippy_lints/src/literal_representation.rs
+++ b/clippy_lints/src/literal_representation.rs
@@ -1,7 +1,7 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::numeric_literal::{NumericLiteral, Radix};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_ast::ast::{Expr, ExprKind, LitKind};
 use rustc_ast::token;
 use rustc_errors::Applicability;
@@ -225,7 +225,7 @@ impl LiteralDigitGrouping {
     }
 
     fn check_lit(&self, cx: &EarlyContext<'_>, lit: token::Lit, span: Span) {
-        if let Some(src) = snippet_opt(cx, span)
+        if let Some(src) = span.get_source_text(cx)
             && let Ok(lit_kind) = LitKind::from_token_lit(lit)
             && let Some(mut num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind)
         {
@@ -439,7 +439,7 @@ impl DecimalLiteralRepresentation {
         // Lint integral literals.
         if let Ok(lit_kind) = LitKind::from_token_lit(lit)
             && let LitKind::Int(val, _) = lit_kind
-            && let Some(src) = snippet_opt(cx, span)
+            && let Some(src) = span.get_source_text(cx)
             && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit_kind)
             && num_lit.radix == Radix::Decimal
             && val >= u128::from(self.threshold)
diff --git a/clippy_lints/src/manual_float_methods.rs b/clippy_lints/src/manual_float_methods.rs
index 6bdc79129a9bc..9d3ddab60bb3a 100644
--- a/clippy_lints/src/manual_float_methods.rs
+++ b/clippy_lints/src/manual_float_methods.rs
@@ -1,6 +1,6 @@
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{is_from_proc_macro, path_to_local};
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Constness, Expr, ExprKind};
@@ -103,7 +103,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
             // case somebody does that for some reason
             && (is_infinity(&const_1) && is_neg_infinity(&const_2)
                 || is_neg_infinity(&const_1) && is_infinity(&const_2))
-            && let Some(local_snippet) = snippet_opt(cx, first.span)
+            && let Some(local_snippet) = first.span.get_source_text(cx)
         {
             let variant = match (kind.node, lhs_kind.node, rhs_kind.node) {
                 (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Eq) => Variant::ManualIsInfinite,
diff --git a/clippy_lints/src/manual_hash_one.rs b/clippy_lints/src/manual_hash_one.rs
index 1c568b1b74f32..11716a539ab5c 100644
--- a/clippy_lints/src/manual_hash_one.rs
+++ b/clippy_lints/src/manual_hash_one.rs
@@ -1,7 +1,7 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_hir_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::visitors::{is_local_used, local_used_once};
 use clippy_utils::{is_trait_method, path_to_local_id};
 use rustc_errors::Applicability;
@@ -107,8 +107,8 @@ impl LateLintPass<'_> for ManualHashOne {
                 finish_expr.span,
                 "manual implementation of `BuildHasher::hash_one`",
                 |diag| {
-                    if let Some(build_hasher) = snippet_opt(cx, build_hasher.span)
-                        && let Some(hashed_value) = snippet_opt(cx, hashed_value.span)
+                    if let Some(build_hasher) = build_hasher.span.get_source_text(cx)
+                        && let Some(hashed_value) = hashed_value.span.get_source_text(cx)
                     {
                         diag.multipart_suggestion(
                             "try",
diff --git a/clippy_lints/src/manual_non_exhaustive.rs b/clippy_lints/src/manual_non_exhaustive.rs
index 28cfe22835af6..6b1d90483ccd1 100644
--- a/clippy_lints/src/manual_non_exhaustive.rs
+++ b/clippy_lints/src/manual_non_exhaustive.rs
@@ -2,7 +2,7 @@ use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
 use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::is_doc_hidden;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_ast::ast::{self, VisibilityKind};
 use rustc_ast::attr;
 use rustc_data_structures::fx::FxHashSet;
@@ -124,7 +124,7 @@ impl EarlyLintPass for ManualNonExhaustiveStruct {
                     |diag| {
                         if !item.attrs.iter().any(|attr| attr.has_name(sym::non_exhaustive))
                             && let header_span = cx.sess().source_map().span_until_char(item.span, delimiter)
-                            && let Some(snippet) = snippet_opt(cx, header_span)
+                            && let Some(snippet) = header_span.get_source_text(cx)
                         {
                             diag.span_suggestion(
                                 header_span,
@@ -194,7 +194,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
                 "this seems like a manual implementation of the non-exhaustive pattern",
                 |diag| {
                     let header_span = cx.sess().source_map().span_until_char(enum_span, '{');
-                    if let Some(snippet) = snippet_opt(cx, header_span) {
+                    if let Some(snippet) = header_span.get_source_text(cx) {
                         diag.span_suggestion(
                             header_span,
                             "add the attribute",
diff --git a/clippy_lints/src/manual_range_patterns.rs b/clippy_lints/src/manual_range_patterns.rs
index 07d4abbf5cd11..9afb2b5bc84cd 100644
--- a/clippy_lints/src/manual_range_patterns.rs
+++ b/clippy_lints/src/manual_range_patterns.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_ast::LitKind;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -143,8 +143,8 @@ impl LateLintPass<'_> for ManualRangePatterns {
                 pat.span,
                 "this OR pattern can be rewritten using a range",
                 |diag| {
-                    if let Some(min) = snippet_opt(cx, min.span)
-                        && let Some(max) = snippet_opt(cx, max.span)
+                    if let Some(min) = min.span.get_source_text(cx)
+                        && let Some(max) = max.span.get_source_text(cx)
                     {
                         diag.span_suggestion(
                             pat.span,
diff --git a/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/clippy_lints/src/methods/unnecessary_iter_cloned.rs
index 70885e46e955e..4d18bc7ac7777 100644
--- a/clippy_lints/src/methods/unnecessary_iter_cloned.rs
+++ b/clippy_lints/src/methods/unnecessary_iter_cloned.rs
@@ -1,7 +1,7 @@
 use super::utils::clone_or_copy_needed;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::ForLoop;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::{get_iterator_item_ty, implements_trait};
 use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local};
@@ -40,7 +40,7 @@ pub fn check_for_loop_iter(
         && let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent)
         && let (clone_or_copy_needed, references_to_binding) = clone_or_copy_needed(cx, pat, body)
         && !clone_or_copy_needed
-        && let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
+        && let Some(receiver_snippet) = receiver.span.get_source_text(cx)
     {
         // Issue 12098
         // https://github.com/rust-lang/rust-clippy/issues/12098
@@ -100,7 +100,7 @@ pub fn check_for_loop_iter(
             && implements_trait(cx, collection_ty, into_iterator_trait_id, &[])
             && let Some(into_iter_item_ty) = cx.get_associated_type(collection_ty, into_iterator_trait_id, "Item")
             && iter_item_ty == into_iter_item_ty
-            && let Some(collection_snippet) = snippet_opt(cx, collection.span)
+            && let Some(collection_snippet) = collection.span.get_source_text(cx)
         {
             collection_snippet
         } else {
@@ -122,7 +122,7 @@ pub fn check_for_loop_iter(
                 } else {
                     Applicability::MachineApplicable
                 };
-                diag.span_suggestion(expr.span, "use", snippet, applicability);
+                diag.span_suggestion(expr.span, "use", snippet.to_owned(), applicability);
                 if !references_to_binding.is_empty() {
                     diag.multipart_suggestion(
                         "remove any references to the binding",
diff --git a/clippy_lints/src/misc_early/unneeded_field_pattern.rs b/clippy_lints/src/misc_early/unneeded_field_pattern.rs
index 6db03adf44ac9..33ab94e00a5c6 100644
--- a/clippy_lints/src/misc_early/unneeded_field_pattern.rs
+++ b/clippy_lints/src/misc_early/unneeded_field_pattern.rs
@@ -1,5 +1,6 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
+use itertools::Itertools;
 use rustc_ast::ast::{Pat, PatKind};
 use rustc_lint::EarlyContext;
 
@@ -45,19 +46,6 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) {
                             "you matched a field with a wildcard pattern, consider using `..` instead",
                         );
                     } else {
-                        let mut normal = vec![];
-
-                        for field in pfields {
-                            match field.pat.kind {
-                                PatKind::Wild => {},
-                                _ => {
-                                    if let Some(n) = snippet_opt(cx, field.span) {
-                                        normal.push(n);
-                                    }
-                                },
-                            }
-                        }
-
                         #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
                         span_lint_and_then(
                             cx,
@@ -65,7 +53,16 @@ pub(super) fn check(cx: &EarlyContext<'_>, pat: &Pat) {
                             field.span,
                             "you matched a field with a wildcard pattern, consider using `..` instead",
                             |diag| {
-                                diag.help(format!("try with `{type_name} {{ {}, .. }}`", normal[..].join(", ")));
+                                diag.help(format!(
+                                    "try with `{type_name} {{ {}, .. }}`",
+                                    pfields
+                                        .iter()
+                                        .filter_map(|f| match f.pat.kind {
+                                            PatKind::Wild => None,
+                                            _ => f.span.get_source_text(cx),
+                                        })
+                                        .format(", "),
+                                ));
                             },
                         );
                     }
diff --git a/clippy_lints/src/missing_enforced_import_rename.rs b/clippy_lints/src/missing_enforced_import_rename.rs
index cdf6645b3df77..59f29d242abf3 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::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::def_id::DefIdMap;
@@ -73,7 +73,7 @@ impl LateLintPass<'_> for ImportRename {
                     && let Some(name) = self.renames.get(&id)
                     // Remove semicolon since it is not present for nested imports
                     && let span_without_semi = cx.sess().source_map().span_until_char(item.span, ';')
-                    && let Some(snip) = snippet_opt(cx, span_without_semi)
+                    && let Some(snip) = span_without_semi.get_source_text(cx)
                     && let Some(import) = match snip.split_once(" as ") {
                         None => Some(snip.as_str()),
                         Some((import, rename)) => {
diff --git a/clippy_lints/src/needless_if.rs b/clippy_lints/src/needless_if.rs
index 1d6233d432a75..8e14fbf2f8061 100644
--- a/clippy_lints/src/needless_if.rs
+++ b/clippy_lints/src/needless_if.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher::If;
 use clippy_utils::is_from_proc_macro;
-use clippy_utils::source::{snippet_opt, SpanRangeExt};
+use clippy_utils::source::SpanRangeExt;
 use rustc_errors::Applicability;
 use rustc_hir::{ExprKind, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass, LintContext};
@@ -57,7 +57,7 @@ impl LateLintPass<'_> for NeedlessIf {
                 src.bytes()
                     .all(|ch| matches!(ch, b'{' | b'}') || ch.is_ascii_whitespace())
             })
-            && let Some(cond_snippet) = snippet_opt(cx, cond.span)
+            && let Some(cond_snippet) = cond.span.get_source_text(cx)
             && !is_from_proc_macro(cx, expr)
         {
             span_lint_and_sugg(
diff --git a/clippy_lints/src/needless_late_init.rs b/clippy_lints/src/needless_late_init.rs
index 4bfc30fa5cf80..46cdb82130f97 100644
--- a/clippy_lints/src/needless_late_init.rs
+++ b/clippy_lints/src/needless_late_init.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::path_to_local;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::{SourceText, SpanRangeExt};
 use clippy_utils::ty::needs_ordered_drop;
 use clippy_utils::visitors::{for_each_expr, for_each_expr_without_closures, is_local_used};
 use core::ops::ControlFlow;
@@ -236,7 +236,7 @@ fn first_usage<'tcx>(
         })
 }
 
-fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) -> Option<String> {
+fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) -> Option<SourceText> {
     let span = local.span.with_hi(match local.ty {
         // let <pat>: <ty>;
         // ~~~~~~~~~~~~~~~
@@ -246,7 +246,7 @@ fn local_snippet_without_semicolon(cx: &LateContext<'_>, local: &LetStmt<'_>) ->
         None => local.pat.span.hi(),
     });
 
-    snippet_opt(cx, span)
+    span.get_source_text(cx)
 }
 
 fn check<'tcx>(
@@ -275,7 +275,10 @@ fn check<'tcx>(
                 |diag| {
                     diag.multipart_suggestion(
                         format!("move the declaration `{binding_name}` here"),
-                        vec![(local_stmt.span, String::new()), (assign.lhs_span, let_snippet)],
+                        vec![
+                            (local_stmt.span, String::new()),
+                            (assign.lhs_span, let_snippet.to_owned()),
+                        ],
                         Applicability::MachineApplicable,
                     );
                 },
diff --git a/clippy_lints/src/no_effect.rs b/clippy_lints/src/no_effect.rs
index 8232e69db3917..b181791699a5b 100644
--- a/clippy_lints/src/no_effect.rs
+++ b/clippy_lints/src/no_effect.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::has_drop;
 use clippy_utils::{
     in_automatically_derived, is_inside_always_const_context, is_lint_allowed, path_to_local, peel_blocks,
@@ -268,34 +268,31 @@ fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
         && reduced.iter().all(|e| e.span.ctxt() == ctxt)
     {
         if let ExprKind::Index(..) = &expr.kind {
-            if is_inside_always_const_context(cx.tcx, expr.hir_id) {
-                return;
+            if !is_inside_always_const_context(cx.tcx, expr.hir_id)
+                && let [arr, func] = &*reduced
+                && let Some(arr) = arr.span.get_source_text(cx)
+                && let Some(func) = func.span.get_source_text(cx)
+            {
+                span_lint_hir_and_then(
+                    cx,
+                    UNNECESSARY_OPERATION,
+                    expr.hir_id,
+                    stmt.span,
+                    "unnecessary operation",
+                    |diag| {
+                        diag.span_suggestion(
+                            stmt.span,
+                            "statement can be written as",
+                            format!("assert!({arr}.len() > {func});"),
+                            Applicability::MaybeIncorrect,
+                        );
+                    },
+                );
             }
-            let snippet =
-                if let (Some(arr), Some(func)) = (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span)) {
-                    format!("assert!({arr}.len() > {func});")
-                } else {
-                    return;
-                };
-            span_lint_hir_and_then(
-                cx,
-                UNNECESSARY_OPERATION,
-                expr.hir_id,
-                stmt.span,
-                "unnecessary operation",
-                |diag| {
-                    diag.span_suggestion(
-                        stmt.span,
-                        "statement can be written as",
-                        snippet,
-                        Applicability::MaybeIncorrect,
-                    );
-                },
-            );
         } else {
             let mut snippet = String::new();
             for e in reduced {
-                if let Some(snip) = snippet_opt(cx, e.span) {
+                if let Some(snip) = e.span.get_source_text(cx) {
                     snippet.push_str(&snip);
                     snippet.push(';');
                 } else {
diff --git a/clippy_lints/src/nonstandard_macro_braces.rs b/clippy_lints/src/nonstandard_macro_braces.rs
index 2aaf1e6ff46da..51ba29d7389b9 100644
--- a/clippy_lints/src/nonstandard_macro_braces.rs
+++ b/clippy_lints/src/nonstandard_macro_braces.rs
@@ -1,7 +1,7 @@
 use clippy_config::types::MacroMatcher;
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::{SourceText, SpanRangeExt};
 use rustc_ast::ast;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::Applicability;
@@ -34,7 +34,7 @@ declare_clippy_lint! {
 }
 
 /// The (callsite span, (open brace, close brace), source snippet)
-type MacroInfo = (Span, (char, char), String);
+type MacroInfo = (Span, (char, char), SourceText);
 
 pub struct MacroBraces {
     macro_braces: FxHashMap<String, (char, char)>,
@@ -94,7 +94,7 @@ fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBrace
     if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind
         && let name = mac_name.as_str()
         && let Some(&braces) = mac_braces.macro_braces.get(name)
-        && let Some(snip) = snippet_opt(cx, span_call_site)
+        && let Some(snip) = span_call_site.get_source_text(cx)
         // we must check only invocation sites
         // https://github.com/rust-lang/rust-clippy/issues/7422
         && snip.starts_with(&format!("{name}!"))
diff --git a/clippy_lints/src/operators/assign_op_pattern.rs b/clippy_lints/src/operators/assign_op_pattern.rs
index 641d881d974b8..11c97b4ef0073 100644
--- a/clippy_lints/src/operators/assign_op_pattern.rs
+++ b/clippy_lints/src/operators/assign_op_pattern.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::implements_trait;
 use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{binop_traits, eq_expr_value, trait_ref_of_method};
@@ -46,8 +46,8 @@ pub(super) fn check<'tcx>(
                     expr.span,
                     "manual implementation of an assign operation",
                     |diag| {
-                        if let (Some(snip_a), Some(snip_r)) =
-                            (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span))
+                        if let Some(snip_a) = assignee.span.get_source_text(cx)
+                            && let Some(snip_r) = rhs.span.get_source_text(cx)
                         {
                             diag.span_suggestion(
                                 expr.span,
diff --git a/clippy_lints/src/operators/misrefactored_assign_op.rs b/clippy_lints/src/operators/misrefactored_assign_op.rs
index 311cbd050a10c..8daedd1c90140 100644
--- a/clippy_lints/src/operators/misrefactored_assign_op.rs
+++ b/clippy_lints/src/operators/misrefactored_assign_op.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{eq_expr_value, sugg};
 use rustc_errors::Applicability;
 use rustc_hir as hir;
@@ -42,7 +42,9 @@ fn lint_misrefactored_assign_op(
         expr.span,
         "variable appears on both sides of an assignment operation",
         |diag| {
-            if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) {
+            if let Some(snip_a) = assignee.span.get_source_text(cx)
+                && let Some(snip_r) = rhs_other.span.get_source_text(cx)
+            {
                 let a = &sugg::Sugg::hir(cx, assignee, "..");
                 let r = &sugg::Sugg::hir(cx, rhs, "..");
                 let long = format!("{snip_a} = {}", sugg::make_binop(op, a, r));
diff --git a/clippy_lints/src/operators/needless_bitwise_bool.rs b/clippy_lints/src/operators/needless_bitwise_bool.rs
index ab5fb1787004e..9d8e833ef6d75 100644
--- a/clippy_lints/src/operators/needless_bitwise_bool.rs
+++ b/clippy_lints/src/operators/needless_bitwise_bool.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
 use rustc_lint::LateContext;
@@ -24,8 +24,8 @@ pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Exp
             e.span,
             "use of bitwise operator instead of lazy operator between booleans",
             |diag| {
-                if let Some(lhs_snip) = snippet_opt(cx, lhs.span)
-                    && let Some(rhs_snip) = snippet_opt(cx, rhs.span)
+                if let Some(lhs_snip) = lhs.span.get_source_text(cx)
+                    && let Some(rhs_snip) = rhs.span.get_source_text(cx)
                 {
                     let sugg = format!("{lhs_snip} {op_str} {rhs_snip}");
                     diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable);
diff --git a/clippy_lints/src/operators/ptr_eq.rs b/clippy_lints/src/operators/ptr_eq.rs
index 607930561e0fc..861564d54569e 100644
--- a/clippy_lints/src/operators/ptr_eq.rs
+++ b/clippy_lints/src/operators/ptr_eq.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::std_or_core;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
@@ -22,8 +22,8 @@ pub(super) fn check<'tcx>(
 
         if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left)
             && let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right)
-            && let Some(left_snip) = snippet_opt(cx, left_var.span)
-            && let Some(right_snip) = snippet_opt(cx, right_var.span)
+            && let Some(left_snip) = left_var.span.get_source_text(cx)
+            && let Some(right_snip) = right_var.span.get_source_text(cx)
         {
             let Some(top_crate) = std_or_core(cx) else { return };
             span_lint_and_sugg(
diff --git a/clippy_lints/src/unit_types/unit_arg.rs b/clippy_lints/src/unit_types/unit_arg.rs
index afc53e6f32d96..978d49f09c3a8 100644
--- a/clippy_lints/src/unit_types/unit_arg.rs
+++ b/clippy_lints/src/unit_types/unit_arg.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_from_proc_macro;
-use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
+use clippy_utils::source::{indent_of, reindent_multiline, SourceText, SpanRangeExt};
 use rustc_errors::Applicability;
 use rustc_hir::{Block, Expr, ExprKind, MatchSource, Node, StmtKind};
 use rustc_lint::LateContext;
@@ -79,7 +79,7 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp
                         && block.expr.is_none()
                         && let Some(last_stmt) = block.stmts.iter().last()
                         && let StmtKind::Semi(last_expr) = last_stmt.kind
-                        && let Some(snip) = snippet_opt(cx, last_expr.span)
+                        && let Some(snip) = last_expr.span.get_source_text(cx)
                     {
                         Some((last_stmt.span, snip))
                     } else {
@@ -90,24 +90,24 @@ fn lint_unit_args(cx: &LateContext<'_>, expr: &Expr<'_>, args_to_recover: &[&Exp
                     db.span_suggestion(
                         span,
                         "remove the semicolon from the last statement in the block",
-                        sugg,
+                        sugg.as_str(),
                         Applicability::MaybeIncorrect,
                     );
                     or = "or ";
                     applicability = Applicability::MaybeIncorrect;
                 });
 
-            let arg_snippets: Vec<String> = args_to_recover
+            let arg_snippets: Vec<_> = args_to_recover
                 .iter()
-                .filter_map(|arg| snippet_opt(cx, arg.span))
+                .filter_map(|arg| arg.span.get_source_text(cx))
                 .collect();
-            let arg_snippets_without_empty_blocks: Vec<String> = args_to_recover
+            let arg_snippets_without_empty_blocks: Vec<_> = args_to_recover
                 .iter()
                 .filter(|arg| !is_empty_block(arg))
-                .filter_map(|arg| snippet_opt(cx, arg.span))
+                .filter_map(|arg| arg.span.get_source_text(cx))
                 .collect();
 
-            if let Some(call_snippet) = snippet_opt(cx, expr.span) {
+            if let Some(call_snippet) = expr.span.get_source_text(cx) {
                 let sugg = fmt_stmts_and_call(
                     cx,
                     expr,
@@ -161,8 +161,8 @@ fn fmt_stmts_and_call(
     cx: &LateContext<'_>,
     call_expr: &Expr<'_>,
     call_snippet: &str,
-    args_snippets: &[impl AsRef<str>],
-    non_empty_block_args_snippets: &[impl AsRef<str>],
+    args_snippets: &[SourceText],
+    non_empty_block_args_snippets: &[SourceText],
 ) -> String {
     let call_expr_indent = indent_of(cx, call_expr.span).unwrap_or(0);
     let call_snippet_with_replacements = args_snippets
diff --git a/clippy_lints/src/utils/format_args_collector.rs b/clippy_lints/src/utils/format_args_collector.rs
index 5acfd35fd6ae6..f50ce6c99deac 100644
--- a/clippy_lints/src/utils/format_args_collector.rs
+++ b/clippy_lints/src/utils/format_args_collector.rs
@@ -1,5 +1,5 @@
 use clippy_utils::macros::FormatArgsStorage;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use itertools::Itertools;
 use rustc_ast::{Crate, Expr, ExprKind, FormatArgs};
 use rustc_data_structures::fx::FxHashMap;
@@ -75,38 +75,21 @@ fn has_span_from_proc_macro(cx: &EarlyContext<'_>, args: &FormatArgs) -> bool {
 
     // `format!("{} {} {c}", "one", "two", c = "three")`
     //                     ^^     ^^     ^^^^^^
-    let between_spans = once(args.span)
+    !once(args.span)
         .chain(argument_span)
         .tuple_windows()
-        .map(|(start, end)| start.between(end));
-
-    for between_span in between_spans {
-        let mut seen_comma = false;
-
-        let Some(snippet) = snippet_opt(cx, between_span) else {
-            return true;
-        };
-        for token in tokenize(&snippet) {
-            match token.kind {
-                TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace => {},
-                TokenKind::Comma if !seen_comma => seen_comma = true,
-                // named arguments, `start_val, name = end_val`
-                //                            ^^^^^^^^^ between_span
-                TokenKind::Ident | TokenKind::Eq if seen_comma => {},
-                // An unexpected token usually indicates that we crossed a macro boundary
-                //
-                // `println!(some_proc_macro!("input {}"), a)`
-                //                                      ^^^ between_span
-                // `println!("{}", val!(x))`
-                //               ^^^^^^^ between_span
-                _ => return true,
-            }
-        }
-
-        if !seen_comma {
-            return true;
-        }
-    }
-
-    false
+        .map(|(start, end)| start.between(end))
+        .all(|sp| {
+            sp.check_source_text(cx, |src| {
+                // text should be either `, name` or `, name =`
+                let mut iter = tokenize(src).filter(|t| {
+                    !matches!(
+                        t.kind,
+                        TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
+                    )
+                });
+                iter.next().is_some_and(|t| matches!(t.kind, TokenKind::Comma))
+                    && iter.all(|t| matches!(t.kind, TokenKind::Ident | TokenKind::Eq))
+            })
+        })
 }
diff --git a/clippy_utils/src/attrs.rs b/clippy_utils/src/attrs.rs
index d2200bcf7103b..42c8b218d14e8 100644
--- a/clippy_utils/src/attrs.rs
+++ b/clippy_utils/src/attrs.rs
@@ -7,7 +7,7 @@ use rustc_session::Session;
 use rustc_span::{sym, Span};
 use std::str::FromStr;
 
-use crate::source::snippet_opt;
+use crate::source::SpanRangeExt;
 use crate::tokenize_with_text;
 
 /// Deprecation status of attributes known by Clippy.
@@ -179,25 +179,23 @@ pub fn has_non_exhaustive_attr(tcx: TyCtxt<'_>, adt: AdtDef<'_>) -> bool {
 
 /// Checks if the given span contains a `#[cfg(..)]` attribute
 pub fn span_contains_cfg(cx: &LateContext<'_>, s: Span) -> bool {
-    let Some(snip) = snippet_opt(cx, s) else {
-        // Assume true. This would require either an invalid span, or one which crosses file boundaries.
-        return true;
-    };
-    let mut iter = tokenize_with_text(&snip);
+    s.check_source_text(cx, |src| {
+        let mut iter = tokenize_with_text(src);
 
-    // Search for the token sequence [`#`, `[`, `cfg`]
-    while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) {
-        let mut iter = iter.by_ref().skip_while(|(t, _)| {
-            matches!(
-                t,
-                TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. }
-            )
-        });
-        if matches!(iter.next(), Some((TokenKind::OpenBracket, _)))
-            && matches!(iter.next(), Some((TokenKind::Ident, "cfg")))
-        {
-            return true;
+        // Search for the token sequence [`#`, `[`, `cfg`]
+        while iter.any(|(t, _)| matches!(t, TokenKind::Pound)) {
+            let mut iter = iter.by_ref().skip_while(|(t, _)| {
+                matches!(
+                    t,
+                    TokenKind::Whitespace | TokenKind::LineComment { .. } | TokenKind::BlockComment { .. }
+                )
+            });
+            if matches!(iter.next(), Some((TokenKind::OpenBracket, _)))
+                && matches!(iter.next(), Some((TokenKind::Ident, "cfg")))
+            {
+                return true;
+            }
         }
-    }
-    false
+        false
+    })
 }
diff --git a/clippy_utils/src/hir_utils.rs b/clippy_utils/src/hir_utils.rs
index b0a668329e920..f61ef9ac1b05e 100644
--- a/clippy_utils/src/hir_utils.rs
+++ b/clippy_utils/src/hir_utils.rs
@@ -1,6 +1,6 @@
 use crate::consts::ConstEvalCtxt;
 use crate::macros::macro_backtrace;
-use crate::source::{snippet_opt, walk_span_to_context, SpanRange, SpanRangeExt};
+use crate::source::{walk_span_to_context, SpanRange, SpanRangeExt};
 use crate::tokenize_with_text;
 use rustc_ast::ast::InlineAsmTemplatePiece;
 use rustc_data_structures::fx::FxHasher;
@@ -588,10 +588,9 @@ fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'
             // block with an empty span.
             ([], None) if block.span.is_empty() => &ExprKind::Tup(&[]),
             // `{}` => `()`
-            ([], None) => match snippet_opt(cx, block.span) {
-                // Don't reduce if there are any tokens contained in the braces
-                Some(snip)
-                    if tokenize(&snip)
+            ([], None)
+                if block.span.check_source_text(cx, |src| {
+                    tokenize(src)
                         .map(|t| t.kind)
                         .filter(|t| {
                             !matches!(
@@ -599,11 +598,10 @@ fn reduce_exprkind<'hir>(cx: &LateContext<'_>, kind: &'hir ExprKind<'hir>) -> &'
                                 TokenKind::LineComment { .. } | TokenKind::BlockComment { .. } | TokenKind::Whitespace
                             )
                         })
-                        .ne([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied()) =>
-                {
-                    kind
-                },
-                _ => &ExprKind::Tup(&[]),
+                        .eq([TokenKind::OpenBrace, TokenKind::CloseBrace].iter().copied())
+                }) =>
+            {
+                &ExprKind::Tup(&[])
             },
             ([], Some(expr)) => match expr.kind {
                 // `{ return .. }` => `return ..`

From 9450b4af483b4b10fe427c1436e75acc9ba68158 Mon Sep 17 00:00:00 2001
From: Jason Newcomb <jsnewcomb@pm.me>
Date: Wed, 21 Aug 2024 14:04:04 -0400
Subject: [PATCH 52/54] Replace more uses of `snippet_opt`.

---
 clippy_lints/src/casts/cast_lossless.rs       |  4 +-
 clippy_lints/src/casts/unnecessary_cast.rs    |  8 +-
 clippy_lints/src/casts/zero_ptr.rs            |  4 +-
 clippy_lints/src/functions/must_use.rs        |  4 +-
 clippy_lints/src/functions/too_many_lines.rs  | 91 +++++++++----------
 clippy_lints/src/matches/manual_unwrap_or.rs  |  8 +-
 ...se_sensitive_file_extension_comparisons.rs | 12 ++-
 .../src/methods/filter_map_bool_then.rs       |  8 +-
 .../methods/from_iter_instead_of_collect.rs   |  4 +-
 .../src/methods/join_absolute_paths.rs        |  4 +-
 clippy_lints/src/methods/manual_ok_or.rs      |  8 +-
 clippy_lints/src/methods/manual_try_fold.rs   | 10 +-
 .../methods/needless_character_iteration.rs   |  6 +-
 .../src/methods/needless_option_as_deref.rs   |  4 +-
 .../src/methods/string_lit_chars_any.rs       |  4 +-
 .../src/methods/unnecessary_get_then_check.rs | 10 +-
 .../src/methods/unnecessary_to_owned.rs       | 18 ++--
 .../src/methods/unused_enumerate_index.rs     | 12 ++-
 clippy_lints/src/vec.rs                       |  8 +-
 clippy_utils/src/source.rs                    |  7 +-
 20 files changed, 125 insertions(+), 109 deletions(-)

diff --git a/clippy_lints/src/casts/cast_lossless.rs b/clippy_lints/src/casts/cast_lossless.rs
index bd3acc06f4b2b..346aed7e9f117 100644
--- a/clippy_lints/src/casts/cast_lossless.rs
+++ b/clippy_lints/src/casts/cast_lossless.rs
@@ -1,7 +1,7 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_in_const_context;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::is_isize_or_usize;
 use rustc_errors::Applicability;
@@ -34,7 +34,7 @@ pub(super) fn check(
             diag.help("an `as` cast can become silently lossy if the types change in the future");
             let mut applicability = Applicability::MachineApplicable;
             let from_sugg = Sugg::hir_with_context(cx, cast_from_expr, expr.span.ctxt(), "<from>", &mut applicability);
-            let Some(ty) = snippet_opt(cx, hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt())) else {
+            let Some(ty) = hygiene::walk_chain(cast_to_hir.span, expr.span.ctxt()).get_source_text(cx) else {
                 return;
             };
             match cast_to_hir.kind {
diff --git a/clippy_lints/src/casts/unnecessary_cast.rs b/clippy_lints/src/casts/unnecessary_cast.rs
index fb0b0cba6a662..566adc83d6903 100644
--- a/clippy_lints/src/casts/unnecessary_cast.rs
+++ b/clippy_lints/src/casts/unnecessary_cast.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::numeric_literal::NumericLiteral;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::{snippet_opt, SpanRangeExt};
 use clippy_utils::visitors::{for_each_expr_without_closures, Visitable};
 use clippy_utils::{get_parent_expr, is_hir_ty_cfg_dependant, is_ty_alias, path_to_local};
 use rustc_ast::{LitFloatType, LitIntType, LitKind};
@@ -104,7 +104,7 @@ pub(super) fn check<'tcx>(
         let literal_str = &cast_str;
 
         if let LitKind::Int(n, _) = lit.node
-            && let Some(src) = snippet_opt(cx, cast_expr.span)
+            && let Some(src) = cast_expr.span.get_source_text(cx)
             && cast_to.is_floating_point()
             && let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node)
             && let from_nbits = 128 - n.get().leading_zeros()
@@ -131,7 +131,7 @@ pub(super) fn check<'tcx>(
             | LitKind::Float(_, LitFloatType::Suffixed(_))
                 if cast_from.kind() == cast_to.kind() =>
             {
-                if let Some(src) = snippet_opt(cx, cast_expr.span) {
+                if let Some(src) = cast_expr.span.get_source_text(cx) {
                     if let Some(num_lit) = NumericLiteral::from_lit_kind(&src, &lit.node) {
                         lint_unnecessary_cast(cx, expr, num_lit.integer, cast_from, cast_to);
                         return true;
@@ -253,7 +253,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx
             let res = cx.qpath_res(&qpath, expr.hir_id);
             // Function call
             if let Res::Def(DefKind::Fn, def_id) = res {
-                let Some(snippet) = snippet_opt(cx, cx.tcx.def_span(def_id)) else {
+                let Some(snippet) = cx.tcx.def_span(def_id).get_source_text(cx) else {
                     return ControlFlow::Continue(());
                 };
                 // This is the worst part of this entire function. This is the only way I know of to
diff --git a/clippy_lints/src/casts/zero_ptr.rs b/clippy_lints/src/casts/zero_ptr.rs
index 3c1c7d2dc3a54..c5c4a28646d27 100644
--- a/clippy_lints/src/casts/zero_ptr.rs
+++ b/clippy_lints/src/casts/zero_ptr.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{is_in_const_context, is_integer_literal, std_or_core};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, Mutability, Ty, TyKind};
@@ -20,7 +20,7 @@ pub fn check(cx: &LateContext<'_>, expr: &Expr<'_>, from: &Expr<'_>, to: &Ty<'_>
 
         let sugg = if let TyKind::Infer = mut_ty.ty.kind {
             format!("{std_or_core}::{sugg_fn}()")
-        } else if let Some(mut_ty_snip) = snippet_opt(cx, mut_ty.ty.span) {
+        } else if let Some(mut_ty_snip) = mut_ty.ty.span.get_source_text(cx) {
             format!("{std_or_core}::{sugg_fn}::<{mut_ty_snip}>()")
         } else {
             return;
diff --git a/clippy_lints/src/functions/must_use.rs b/clippy_lints/src/functions/must_use.rs
index a08415aeb98a1..9382812104706 100644
--- a/clippy_lints/src/functions/must_use.rs
+++ b/clippy_lints/src/functions/must_use.rs
@@ -12,7 +12,7 @@ use rustc_span::{sym, Span};
 
 use clippy_utils::attrs::is_proc_macro;
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::is_must_use_ty;
 use clippy_utils::visitors::for_each_expr_without_closures;
 use clippy_utils::{return_ty, trait_ref_of_method};
@@ -155,7 +155,7 @@ fn check_must_use_candidate<'tcx>(
         return;
     }
     span_lint_and_then(cx, MUST_USE_CANDIDATE, fn_span, msg, |diag| {
-        if let Some(snippet) = snippet_opt(cx, fn_span) {
+        if let Some(snippet) = fn_span.get_source_text(cx) {
             diag.span_suggestion(
                 fn_span,
                 "add the attribute",
diff --git a/clippy_lints/src/functions/too_many_lines.rs b/clippy_lints/src/functions/too_many_lines.rs
index 586ca58d60dd6..0f5ce340c44dd 100644
--- a/clippy_lints/src/functions/too_many_lines.rs
+++ b/clippy_lints/src/functions/too_many_lines.rs
@@ -1,12 +1,11 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::source::SpanRangeExt;
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_span::Span;
 
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::source::snippet_opt;
-
 use super::TOO_MANY_LINES;
 
 pub(super) fn check_fn(
@@ -22,57 +21,57 @@ pub(super) fn check_fn(
         return;
     }
 
-    let Some(code_snippet) = snippet_opt(cx, body.value.span) else {
-        return;
-    };
     let mut line_count: u64 = 0;
-    let mut in_comment = false;
-    let mut code_in_line;
+    let too_many = body.value.span.check_source_text(cx, |src| {
+        let mut in_comment = false;
+        let mut code_in_line;
 
-    let function_lines = if matches!(body.value.kind, hir::ExprKind::Block(..))
-        && code_snippet.as_bytes().first().copied() == Some(b'{')
-        && code_snippet.as_bytes().last().copied() == Some(b'}')
-    {
-        // Removing the braces from the enclosing block
-        &code_snippet[1..code_snippet.len() - 1]
-    } else {
-        &code_snippet
-    }
-    .trim() // Remove leading and trailing blank lines
-    .lines();
+        let function_lines = if matches!(body.value.kind, hir::ExprKind::Block(..))
+            && src.as_bytes().first().copied() == Some(b'{')
+            && src.as_bytes().last().copied() == Some(b'}')
+        {
+            // Removing the braces from the enclosing block
+            &src[1..src.len() - 1]
+        } else {
+            src
+        }
+        .trim() // Remove leading and trailing blank lines
+        .lines();
 
-    for mut line in function_lines {
-        code_in_line = false;
-        loop {
-            line = line.trim_start();
-            if line.is_empty() {
-                break;
-            }
-            if in_comment {
-                if let Some(i) = line.find("*/") {
-                    line = &line[i + 2..];
-                    in_comment = false;
-                    continue;
+        for mut line in function_lines {
+            code_in_line = false;
+            loop {
+                line = line.trim_start();
+                if line.is_empty() {
+                    break;
                 }
-            } else {
-                let multi_idx = line.find("/*").unwrap_or(line.len());
-                let single_idx = line.find("//").unwrap_or(line.len());
-                code_in_line |= multi_idx > 0 && single_idx > 0;
-                // Implies multi_idx is below line.len()
-                if multi_idx < single_idx {
-                    line = &line[multi_idx + 2..];
-                    in_comment = true;
-                    continue;
+                if in_comment {
+                    if let Some(i) = line.find("*/") {
+                        line = &line[i + 2..];
+                        in_comment = false;
+                        continue;
+                    }
+                } else {
+                    let multi_idx = line.find("/*").unwrap_or(line.len());
+                    let single_idx = line.find("//").unwrap_or(line.len());
+                    code_in_line |= multi_idx > 0 && single_idx > 0;
+                    // Implies multi_idx is below line.len()
+                    if multi_idx < single_idx {
+                        line = &line[multi_idx + 2..];
+                        in_comment = true;
+                        continue;
+                    }
                 }
+                break;
+            }
+            if code_in_line {
+                line_count += 1;
             }
-            break;
-        }
-        if code_in_line {
-            line_count += 1;
         }
-    }
+        line_count > too_many_lines_threshold
+    });
 
-    if line_count > too_many_lines_threshold {
+    if too_many {
         span_lint(
             cx,
             TOO_MANY_LINES,
diff --git a/clippy_lints/src/matches/manual_unwrap_or.rs b/clippy_lints/src/matches/manual_unwrap_or.rs
index 2d4c8daf5cb66..d5d83df93471c 100644
--- a/clippy_lints/src/matches/manual_unwrap_or.rs
+++ b/clippy_lints/src/matches/manual_unwrap_or.rs
@@ -1,6 +1,6 @@
 use clippy_utils::consts::ConstEvalCtxt;
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
+use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::contains_return_break_continue_macro;
 use clippy_utils::{is_res_lang_ctor, path_to_local_id, peel_blocks, sugg};
@@ -67,11 +67,11 @@ fn check_and_lint<'tcx>(
         && path_to_local_id(peel_blocks(then_expr), binding_hir_id)
         && cx.typeck_results().expr_adjustments(then_expr).is_empty()
         && let Some(ty_name) = find_type_name(cx, ty)
-        && let Some(or_body_snippet) = snippet_opt(cx, else_expr.span)
+        && let Some(or_body_snippet) = else_expr.span.get_source_text(cx)
         && let Some(indent) = indent_of(cx, expr.span)
         && ConstEvalCtxt::new(cx).eval_simple(else_expr).is_some()
     {
-        lint(cx, expr, let_expr, ty_name, or_body_snippet, indent);
+        lint(cx, expr, let_expr, ty_name, &or_body_snippet, indent);
     }
 }
 
@@ -110,7 +110,7 @@ fn lint<'tcx>(
     expr: &Expr<'tcx>,
     scrutinee: &'tcx Expr<'_>,
     ty_name: &str,
-    or_body_snippet: String,
+    or_body_snippet: &str,
     indent: usize,
 ) {
     let reindented_or_body = reindent_multiline(or_body_snippet.into(), true, Some(indent));
diff --git a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
index a5df863d800bb..740cce0a6c235 100644
--- a/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
+++ b/clippy_lints/src/methods/case_sensitive_file_extension_comparisons.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
+use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt};
 use clippy_utils::ty::is_type_lang_item;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -49,10 +49,12 @@ pub(super) fn check<'tcx>(
             "case-sensitive file extension comparison",
             |diag| {
                 diag.help("consider using a case-insensitive comparison instead");
-                if let Some(mut recv_source) = snippet_opt(cx, recv.span) {
-                    if !cx.typeck_results().expr_ty(recv).is_ref() {
-                        recv_source = format!("&{recv_source}");
-                    }
+                if let Some(recv_source) = recv.span.get_source_text(cx) {
+                    let recv_source = if cx.typeck_results().expr_ty(recv).is_ref() {
+                        recv_source.to_owned()
+                    } else {
+                        format!("&{recv_source}")
+                    };
 
                     let suggestion_source = reindent_multiline(
                         format!(
diff --git a/clippy_lints/src/methods/filter_map_bool_then.rs b/clippy_lints/src/methods/filter_map_bool_then.rs
index 2e43d19a69911..4fbf661727daa 100644
--- a/clippy_lints/src/methods/filter_map_bool_then.rs
+++ b/clippy_lints/src/methods/filter_map_bool_then.rs
@@ -1,7 +1,7 @@
 use super::FILTER_MAP_BOOL_THEN;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::paths::BOOL_THEN;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::is_copy;
 use clippy_utils::{is_from_proc_macro, is_trait_method, match_def_path, peel_blocks};
 use rustc_errors::Applicability;
@@ -42,9 +42,9 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, arg: &
             .iter()
             .filter(|adj| matches!(adj.kind, Adjust::Deref(_)))
             .count()
-        && let Some(param_snippet) = snippet_opt(cx, param.span)
-        && let Some(filter) = snippet_opt(cx, recv.span)
-        && let Some(map) = snippet_opt(cx, then_body.span)
+        && let Some(param_snippet) = param.span.get_source_text(cx)
+        && let Some(filter) = recv.span.get_source_text(cx)
+        && let Some(map) = then_body.span.get_source_text(cx)
     {
         span_lint_and_sugg(
             cx,
diff --git a/clippy_lints/src/methods/from_iter_instead_of_collect.rs b/clippy_lints/src/methods/from_iter_instead_of_collect.rs
index 917a8e33eb9fb..f4840785584ef 100644
--- a/clippy_lints/src/methods/from_iter_instead_of_collect.rs
+++ b/clippy_lints/src/methods/from_iter_instead_of_collect.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::implements_trait;
 use clippy_utils::{is_path_diagnostic_item, sugg};
 use rustc_errors::Applicability;
@@ -39,7 +39,7 @@ fn extract_turbofish(cx: &LateContext<'_>, expr: &hir::Expr<'_>, ty: Ty<'_>) ->
     }
 
     let call_site = expr.span.source_callsite();
-    if let Some(snippet) = snippet_opt(cx, call_site)
+    if let Some(snippet) = call_site.get_source_text(cx)
         && let snippet_split = snippet.split("::").collect::<Vec<_>>()
         && let Some((_, elements)) = snippet_split.split_last()
     {
diff --git a/clippy_lints/src/methods/join_absolute_paths.rs b/clippy_lints/src/methods/join_absolute_paths.rs
index aa1ec60d434a5..2dad7fcf3c129 100644
--- a/clippy_lints/src/methods/join_absolute_paths.rs
+++ b/clippy_lints/src/methods/join_absolute_paths.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::expr_or_init;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::snippet;
 use clippy_utils::ty::is_type_diagnostic_item;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -25,7 +25,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, recv: &'tcx Expr<'tcx>, join_a
             join_arg.span,
             "argument to `Path::join` starts with a path separator",
             |diag| {
-                let arg_str = snippet_opt(cx, spanned.span).unwrap_or_else(|| "..".to_string());
+                let arg_str = snippet(cx, spanned.span, "..");
 
                 let no_separator = if sym_str.starts_with('/') {
                     arg_str.replacen('/', "", 1)
diff --git a/clippy_lints/src/methods/manual_ok_or.rs b/clippy_lints/src/methods/manual_ok_or.rs
index b1af0083e65a9..b1a8e1e5e4709 100644
--- a/clippy_lints/src/methods/manual_ok_or.rs
+++ b/clippy_lints/src/methods/manual_ok_or.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::{indent_of, reindent_multiline, snippet_opt};
+use clippy_utils::source::{indent_of, reindent_multiline, SpanRangeExt};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{is_res_lang_ctor, path_res, path_to_local_id};
 use rustc_errors::Applicability;
@@ -23,11 +23,11 @@ pub(super) fn check<'tcx>(
         && let ExprKind::Call(err_path, [err_arg]) = or_expr.kind
         && is_res_lang_ctor(cx, path_res(cx, err_path), ResultErr)
         && is_ok_wrapping(cx, map_expr)
-        && let Some(recv_snippet) = snippet_opt(cx, recv.span)
-        && let Some(err_arg_snippet) = snippet_opt(cx, err_arg.span)
+        && let Some(recv_snippet) = recv.span.get_source_text(cx)
+        && let Some(err_arg_snippet) = err_arg.span.get_source_text(cx)
         && let Some(indent) = indent_of(cx, expr.span)
     {
-        let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.into(), true, Some(indent + 4));
+        let reindented_err_arg_snippet = reindent_multiline(err_arg_snippet.as_str().into(), true, Some(indent + 4));
         span_lint_and_sugg(
             cx,
             MANUAL_OK_OR,
diff --git a/clippy_lints/src/methods/manual_try_fold.rs b/clippy_lints/src/methods/manual_try_fold.rs
index f93edded729fc..11fba35c16ea3 100644
--- a/clippy_lints/src/methods/manual_try_fold.rs
+++ b/clippy_lints/src/methods/manual_try_fold.rs
@@ -1,6 +1,6 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::implements_trait;
 use clippy_utils::{is_from_proc_macro, is_trait_method};
 use rustc_errors::Applicability;
@@ -31,13 +31,15 @@ pub(super) fn check<'tcx>(
         && let Res::Def(DefKind::Ctor(_, _), _) = cx.qpath_res(&qpath, path.hir_id)
         && let ExprKind::Closure(closure) = acc.kind
         && !is_from_proc_macro(cx, expr)
-        && let Some(args_snip) = closure.fn_arg_span.and_then(|fn_arg_span| snippet_opt(cx, fn_arg_span))
+        && let Some(args_snip) = closure
+            .fn_arg_span
+            .and_then(|fn_arg_span| fn_arg_span.get_source_text(cx))
     {
         let init_snip = rest
             .is_empty()
             .then_some(first.span)
-            .and_then(|span| snippet_opt(cx, span))
-            .unwrap_or("...".to_owned());
+            .and_then(|span| span.get_source_text(cx))
+            .map_or_else(|| "...".to_owned(), |src| src.to_owned());
 
         span_lint_and_sugg(
             cx,
diff --git a/clippy_lints/src/methods/needless_character_iteration.rs b/clippy_lints/src/methods/needless_character_iteration.rs
index e3d7820771547..332da722a37dc 100644
--- a/clippy_lints/src/methods/needless_character_iteration.rs
+++ b/clippy_lints/src/methods/needless_character_iteration.rs
@@ -7,7 +7,7 @@ use rustc_span::Span;
 use super::utils::get_last_chain_binding_hir_id;
 use super::NEEDLESS_CHARACTER_ITERATION;
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{match_def_path, path_to_local_id, peel_blocks};
 
 fn peels_expr_ref<'a, 'tcx>(mut expr: &'a Expr<'tcx>) -> &'a Expr<'tcx> {
@@ -35,7 +35,7 @@ fn handle_expr(
                 && path_to_local_id(receiver, first_param)
                 && let char_arg_ty = cx.typeck_results().expr_ty_adjusted(receiver).peel_refs()
                 && *char_arg_ty.kind() == ty::Char
-                && let Some(snippet) = snippet_opt(cx, before_chars)
+                && let Some(snippet) = before_chars.get_source_text(cx)
             {
                 span_lint_and_sugg(
                     cx,
@@ -79,7 +79,7 @@ fn handle_expr(
                 && let Some(fn_def_id) = cx.qpath_res(&path, fn_path.hir_id).opt_def_id()
                 && match_def_path(cx, fn_def_id, &["core", "char", "methods", "<impl char>", "is_ascii"])
                 && path_to_local_id(peels_expr_ref(arg), first_param)
-                && let Some(snippet) = snippet_opt(cx, before_chars)
+                && let Some(snippet) = before_chars.get_source_text(cx)
             {
                 span_lint_and_sugg(
                     cx,
diff --git a/clippy_lints/src/methods/needless_option_as_deref.rs b/clippy_lints/src/methods/needless_option_as_deref.rs
index eaae8613d44d9..9f714fdd47bb3 100644
--- a/clippy_lints/src/methods/needless_option_as_deref.rs
+++ b/clippy_lints/src/methods/needless_option_as_deref.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::path_res;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::usage::local_used_after_expr;
 use rustc_errors::Applicability;
@@ -32,7 +32,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, name
             expr.span,
             "derefed type is same as origin",
             "try",
-            snippet_opt(cx, recv.span).unwrap(),
+            recv.span.get_source_text(cx).unwrap().to_owned(),
             Applicability::MachineApplicable,
         );
     }
diff --git a/clippy_lints/src/methods/string_lit_chars_any.rs b/clippy_lints/src/methods/string_lit_chars_any.rs
index 5f6f027a3b59e..cc0d432b799d6 100644
--- a/clippy_lints/src/methods/string_lit_chars_any.rs
+++ b/clippy_lints/src/methods/string_lit_chars_any.rs
@@ -1,6 +1,6 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::{is_from_proc_macro, is_trait_method, path_to_local};
 use itertools::Itertools;
 use rustc_ast::LitKind;
@@ -34,7 +34,7 @@ pub(super) fn check<'tcx>(
             _ => return,
         }
         && !is_from_proc_macro(cx, expr)
-        && let Some(scrutinee_snip) = snippet_opt(cx, scrutinee.span)
+        && let Some(scrutinee_snip) = scrutinee.span.get_source_text(cx)
     {
         // Normalize the char using `map` so `join` doesn't use `Display`, if we don't then
         // something like `r"\"` will become `'\'`, which is of course invalid
diff --git a/clippy_lints/src/methods/unnecessary_get_then_check.rs b/clippy_lints/src/methods/unnecessary_get_then_check.rs
index f6184222d8e96..64eb84117954c 100644
--- a/clippy_lints/src/methods/unnecessary_get_then_check.rs
+++ b/clippy_lints/src/methods/unnecessary_get_then_check.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::is_type_diagnostic_item;
 
 use rustc_errors::Applicability;
@@ -38,11 +38,11 @@ pub(super) fn check(
         return;
     };
     let both_calls_span = get_call_span.with_hi(call_span.hi());
-    if let Some(snippet) = snippet_opt(cx, both_calls_span)
-        && let Some(arg_snippet) = snippet_opt(cx, arg.span)
+    if let Some(snippet) = both_calls_span.get_source_text(cx)
+        && let Some(arg_snippet) = arg.span.get_source_text(cx)
     {
         let generics_snippet = if let Some(generics) = path.args
-            && let Some(generics_snippet) = snippet_opt(cx, generics.span_ext)
+            && let Some(generics_snippet) = generics.span_ext.get_source_text(cx)
         {
             format!("::{generics_snippet}")
         } else {
@@ -63,7 +63,7 @@ pub(super) fn check(
                 suggestion,
                 Applicability::MaybeIncorrect,
             );
-        } else if let Some(caller_snippet) = snippet_opt(cx, get_caller.span) {
+        } else if let Some(caller_snippet) = get_caller.span.get_source_text(cx) {
             let full_span = get_caller.span.with_hi(call_span.hi());
 
             span_lint_and_then(
diff --git a/clippy_lints/src/methods/unnecessary_to_owned.rs b/clippy_lints/src/methods/unnecessary_to_owned.rs
index fed2b128dcf30..69c5bc57e2996 100644
--- a/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -2,7 +2,7 @@ use super::implicit_clone::is_clone_like;
 use super::unnecessary_iter_cloned::{self, is_into_iter};
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
-use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::source::{snippet, SpanRangeExt};
 use clippy_utils::ty::{get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item};
 use clippy_utils::visitors::find_all_ret_expressions;
 use clippy_utils::{
@@ -133,7 +133,7 @@ fn check_addr_of_expr(
         && (*referent_ty != receiver_ty
             || (matches!(referent_ty.kind(), ty::Array(..)) && is_copy(cx, *referent_ty))
             || is_cow_into_owned(cx, method_name, method_def_id))
-        && let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
+        && let Some(receiver_snippet) = receiver.span.get_source_text(cx)
     {
         if receiver_ty == target_ty && n_target_refs >= n_receiver_refs {
             span_lint_and_sugg(
@@ -167,7 +167,7 @@ fn check_addr_of_expr(
                     parent.span,
                     format!("unnecessary use of `{method_name}`"),
                     "use",
-                    receiver_snippet,
+                    receiver_snippet.to_owned(),
                     Applicability::MachineApplicable,
                 );
             } else {
@@ -217,7 +217,7 @@ fn check_into_iter_call_arg(
         && let parent_ty = cx.typeck_results().expr_ty(parent)
         && implements_trait(cx, parent_ty, iterator_trait_id, &[])
         && let Some(item_ty) = get_iterator_item_ty(cx, parent_ty)
-        && let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
+        && let Some(receiver_snippet) = receiver.span.get_source_text(cx)
     {
         if unnecessary_iter_cloned::check_for_loop_iter(cx, parent, method_name, receiver, true) {
             return true;
@@ -309,8 +309,8 @@ fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symb
     if let Some(parent) = get_parent_expr(cx, expr)
         && let Some((fn_name, argument_expr)) = get_fn_name_and_arg(cx, parent)
         && fn_name.as_str() == "split"
-        && let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
-        && let Some(arg_snippet) = snippet_opt(cx, argument_expr.span)
+        && let Some(receiver_snippet) = receiver.span.get_source_text(cx)
+        && let Some(arg_snippet) = argument_expr.span.get_source_text(cx)
     {
         // We may end-up here because of an expression like `x.to_string().split(…)` where the type of `x`
         // implements `AsRef<str>` but does not implement `Deref<Target = str>`. In this case, we have to
@@ -405,7 +405,7 @@ fn check_other_call_arg<'tcx>(
             None
         }
         && can_change_type(cx, maybe_arg, receiver_ty)
-        && let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
+        && let Some(receiver_snippet) = receiver.span.get_source_text(cx)
     {
         span_lint_and_sugg(
             cx,
@@ -695,7 +695,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx
         && let arg_ty = arg_ty.peel_refs()
         // For now we limit this lint to `String` and `Vec`.
         && (is_str_and_string(cx, arg_ty, original_arg_ty) || is_slice_and_vec(cx, arg_ty, original_arg_ty))
-        && let Some(snippet) = snippet_opt(cx, caller.span)
+        && let Some(snippet) = caller.span.get_source_text(cx)
     {
         span_lint_and_sugg(
             cx,
@@ -706,7 +706,7 @@ fn check_if_applicable_to_argument<'tcx>(cx: &LateContext<'tcx>, arg: &Expr<'tcx
             if original_arg_ty.is_array() {
                 format!("{snippet}.as_slice()")
             } else {
-                snippet
+                snippet.to_owned()
             },
             Applicability::MaybeIncorrect,
         );
diff --git a/clippy_lints/src/methods/unused_enumerate_index.rs b/clippy_lints/src/methods/unused_enumerate_index.rs
index 3004d9c4233a7..ee5177d1ffaa1 100644
--- a/clippy_lints/src/methods/unused_enumerate_index.rs
+++ b/clippy_lints/src/methods/unused_enumerate_index.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
-use clippy_utils::source::{snippet, snippet_opt};
+use clippy_utils::source::{snippet, SpanRangeExt};
 use clippy_utils::{expr_or_init, is_trait_method, pat_is_wild};
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind, FnDecl, PatKind, TyKind};
@@ -81,8 +81,14 @@ pub(super) fn check(cx: &LateContext<'_>, call_expr: &Expr<'_>, recv: &Expr<'_>,
         let new_closure_param = match find_elem_explicit_type_span(closure.fn_decl) {
             // We have an explicit type. Get its snippet, that of the binding name, and do `binding: ty`.
             // Fallback to `..` if we fail getting either snippet.
-            Some(ty_span) => snippet_opt(cx, elem.span)
-                .and_then(|binding_name| snippet_opt(cx, ty_span).map(|ty_name| format!("{binding_name}: {ty_name}")))
+            Some(ty_span) => elem
+                .span
+                .get_source_text(cx)
+                .and_then(|binding_name| {
+                    ty_span
+                        .get_source_text(cx)
+                        .map(|ty_name| format!("{binding_name}: {ty_name}"))
+                })
                 .unwrap_or_else(|| "..".to_string()),
             // Otherwise, we have no explicit type. We can replace with the binding name of the element.
             None => snippet(cx, elem.span, "..").into_owned(),
diff --git a/clippy_lints/src/vec.rs b/clippy_lints/src/vec.rs
index 228db14d1b7c3..d3e49bff422ca 100644
--- a/clippy_lints/src/vec.rs
+++ b/clippy_lints/src/vec.rs
@@ -5,7 +5,7 @@ use clippy_config::msrvs::{self, Msrv};
 use clippy_config::Conf;
 use clippy_utils::consts::{ConstEvalCtxt, Constant};
 use clippy_utils::diagnostics::span_lint_hir_and_then;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::is_copy;
 use clippy_utils::visitors::for_each_local_use_after_expr;
 use clippy_utils::{get_parent_expr, higher, is_in_test, is_trait_method};
@@ -214,9 +214,11 @@ impl SuggestedType {
     }
 
     fn snippet(self, cx: &LateContext<'_>, args_span: Option<Span>, len_span: Option<Span>) -> String {
-        let maybe_args = args_span.and_then(|sp| snippet_opt(cx, sp)).unwrap_or_default();
+        let maybe_args = args_span
+            .and_then(|sp| sp.get_source_text(cx))
+            .map_or(String::new(), |x| x.to_owned());
         let maybe_len = len_span
-            .and_then(|sp| snippet_opt(cx, sp).map(|s| format!("; {s}")))
+            .and_then(|sp| sp.get_source_text(cx).map(|s| format!("; {s}")))
             .unwrap_or_default();
 
         match self {
diff --git a/clippy_utils/src/source.rs b/clippy_utils/src/source.rs
index 8b49b013e0313..d6830b53b1571 100644
--- a/clippy_utils/src/source.rs
+++ b/clippy_utils/src/source.rs
@@ -207,6 +207,7 @@ fn get_source_range(sm: &SourceMap, sp: Range<BytePos>) -> Option<SourceFileRang
     if !Lrc::ptr_eq(&start.sf, &end.sf) || start.pos > end.pos {
         return None;
     }
+    sm.ensure_source_file_source_present(&start.sf);
     let range = start.pos.to_usize()..end.pos.to_usize();
     Some(SourceFileRange { sf: start.sf, range })
 }
@@ -283,7 +284,11 @@ impl SourceFileRange {
     /// Attempts to get the text from the source file. This can fail if the source text isn't
     /// loaded.
     pub fn as_str(&self) -> Option<&str> {
-        self.sf.src.as_ref().and_then(|x| x.get(self.range.clone()))
+        self.sf
+            .src
+            .as_ref()
+            .or_else(|| self.sf.external_src.get().and_then(|src| src.get_source()))
+            .and_then(|x| x.get(self.range.clone()))
     }
 }
 

From f4fc3858bc58d2e51c545ba5b3cda592225e51fc Mon Sep 17 00:00:00 2001
From: Alexey Semenyuk <alexsemenyuk88@gmail.com>
Date: Sat, 24 Aug 2024 15:43:00 +0500
Subject: [PATCH 53/54] Fix suggestion unwrap_or_else

---
 .../src/methods/unnecessary_lazy_eval.rs      |   4 +-
 tests/ui/unnecessary_lazy_eval.stderr         | 566 ++++++++++++------
 .../ui/unnecessary_lazy_eval_unfixable.stderr |  35 +-
 3 files changed, 402 insertions(+), 203 deletions(-)

diff --git a/clippy_lints/src/methods/unnecessary_lazy_eval.rs b/clippy_lints/src/methods/unnecessary_lazy_eval.rs
index 4429f0326058a..b84594c0da19f 100644
--- a/clippy_lints/src/methods/unnecessary_lazy_eval.rs
+++ b/clippy_lints/src/methods/unnecessary_lazy_eval.rs
@@ -64,9 +64,9 @@ pub(super) fn check<'tcx>(
                 // but prefer to avoid changing the signature of the function itself.
                 if let hir::ExprKind::MethodCall(.., span) = expr.kind {
                     span_lint_and_then(cx, UNNECESSARY_LAZY_EVALUATIONS, expr.span, msg, |diag| {
-                        diag.span_suggestion(
+                        diag.span_suggestion_verbose(
                             span,
-                            format!("use `{simplify_using}(..)` instead"),
+                            format!("use `{simplify_using}` instead"),
                             format!("{simplify_using}({})", snippet(cx, body_expr.span, "..")),
                             applicability,
                         );
diff --git a/tests/ui/unnecessary_lazy_eval.stderr b/tests/ui/unnecessary_lazy_eval.stderr
index fcd60f48bccdd..bcdf65b217e51 100644
--- a/tests/ui/unnecessary_lazy_eval.stderr
+++ b/tests/ui/unnecessary_lazy_eval.stderr
@@ -2,316 +2,432 @@ error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:83:13
    |
 LL |     let _ = opt.unwrap_or_else(|| 2);
-   |             ^^^^--------------------
-   |                 |
-   |                 help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_lazy_evaluations)]`
+help: use `unwrap_or` instead
+   |
+LL |     let _ = opt.unwrap_or(2);
+   |                 ~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:84:13
    |
 LL |     let _ = opt.unwrap_or_else(|| astronomers_pi);
-   |             ^^^^---------------------------------
-   |                 |
-   |                 help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = opt.unwrap_or(astronomers_pi);
+   |                 ~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:85:13
    |
 LL |     let _ = opt.unwrap_or_else(|| ext_str.some_field);
-   |             ^^^^-------------------------------------
-   |                 |
-   |                 help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = opt.unwrap_or(ext_str.some_field);
+   |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:87:13
    |
 LL |     let _ = opt.and_then(|_| ext_opt);
-   |             ^^^^---------------------
-   |                 |
-   |                 help: use `and(..)` instead: `and(ext_opt)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `and` instead
+   |
+LL |     let _ = opt.and(ext_opt);
+   |                 ~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:88:13
    |
 LL |     let _ = opt.or_else(|| ext_opt);
-   |             ^^^^-------------------
-   |                 |
-   |                 help: use `or(..)` instead: `or(ext_opt)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _ = opt.or(ext_opt);
+   |                 ~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:89:13
    |
 LL |     let _ = opt.or_else(|| None);
-   |             ^^^^----------------
-   |                 |
-   |                 help: use `or(..)` instead: `or(None)`
+   |             ^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _ = opt.or(None);
+   |                 ~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:90:13
    |
 LL |     let _ = opt.get_or_insert_with(|| 2);
-   |             ^^^^------------------------
-   |                 |
-   |                 help: use `get_or_insert(..)` instead: `get_or_insert(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `get_or_insert` instead
+   |
+LL |     let _ = opt.get_or_insert(2);
+   |                 ~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:91:13
    |
 LL |     let _ = opt.ok_or_else(|| 2);
-   |             ^^^^----------------
-   |                 |
-   |                 help: use `ok_or(..)` instead: `ok_or(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `ok_or` instead
+   |
+LL |     let _ = opt.ok_or(2);
+   |                 ~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:92:13
    |
 LL |     let _ = nested_tuple_opt.unwrap_or_else(|| Some((1, 2)));
-   |             ^^^^^^^^^^^^^^^^^-------------------------------
-   |                              |
-   |                              help: use `unwrap_or(..)` instead: `unwrap_or(Some((1, 2)))`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = nested_tuple_opt.unwrap_or(Some((1, 2)));
+   |                              ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:93:13
    |
 LL |     let _ = cond.then(|| astronomers_pi);
-   |             ^^^^^-----------------------
-   |                  |
-   |                  help: use `then_some(..)` instead: `then_some(astronomers_pi)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _ = cond.then_some(astronomers_pi);
+   |                  ~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:94:13
    |
 LL |     let _ = true.then(|| -> _ {});
-   |             ^^^^^----------------
-   |                  |
-   |                  help: use `then_some(..)` instead: `then_some({})`
+   |             ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _ = true.then_some({});
+   |                  ~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:95:13
    |
 LL |     let _ = true.then(|| {});
-   |             ^^^^^-----------
-   |                  |
-   |                  help: use `then_some(..)` instead: `then_some({})`
+   |             ^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _ = true.then_some({});
+   |                  ~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:99:13
    |
 LL |     let _ = Some(1).unwrap_or_else(|| *r);
-   |             ^^^^^^^^---------------------
-   |                     |
-   |                     help: use `unwrap_or(..)` instead: `unwrap_or(*r)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = Some(1).unwrap_or(*r);
+   |                     ~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:101:13
    |
 LL |     let _ = Some(1).unwrap_or_else(|| *b);
-   |             ^^^^^^^^---------------------
-   |                     |
-   |                     help: use `unwrap_or(..)` instead: `unwrap_or(*b)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = Some(1).unwrap_or(*b);
+   |                     ~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:103:13
    |
 LL |     let _ = Some(1).as_ref().unwrap_or_else(|| &r);
-   |             ^^^^^^^^^^^^^^^^^---------------------
-   |                              |
-   |                              help: use `unwrap_or(..)` instead: `unwrap_or(&r)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = Some(1).as_ref().unwrap_or(&r);
+   |                              ~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:104:13
    |
 LL |     let _ = Some(1).as_ref().unwrap_or_else(|| &b);
-   |             ^^^^^^^^^^^^^^^^^---------------------
-   |                              |
-   |                              help: use `unwrap_or(..)` instead: `unwrap_or(&b)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = Some(1).as_ref().unwrap_or(&b);
+   |                              ~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:107:13
    |
 LL |     let _ = Some(10).unwrap_or_else(|| 2);
-   |             ^^^^^^^^^--------------------
-   |                      |
-   |                      help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = Some(10).unwrap_or(2);
+   |                      ~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:108:13
    |
 LL |     let _ = Some(10).and_then(|_| ext_opt);
-   |             ^^^^^^^^^---------------------
-   |                      |
-   |                      help: use `and(..)` instead: `and(ext_opt)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `and` instead
+   |
+LL |     let _ = Some(10).and(ext_opt);
+   |                      ~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:109:28
    |
 LL |     let _: Option<usize> = None.or_else(|| ext_opt);
-   |                            ^^^^^-------------------
-   |                                 |
-   |                                 help: use `or(..)` instead: `or(ext_opt)`
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _: Option<usize> = None.or(ext_opt);
+   |                                 ~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:110:13
    |
 LL |     let _ = None.get_or_insert_with(|| 2);
-   |             ^^^^^------------------------
-   |                  |
-   |                  help: use `get_or_insert(..)` instead: `get_or_insert(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `get_or_insert` instead
+   |
+LL |     let _ = None.get_or_insert(2);
+   |                  ~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:111:35
    |
 LL |     let _: Result<usize, usize> = None.ok_or_else(|| 2);
-   |                                   ^^^^^----------------
-   |                                        |
-   |                                        help: use `ok_or(..)` instead: `ok_or(2)`
+   |                                   ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `ok_or` instead
+   |
+LL |     let _: Result<usize, usize> = None.ok_or(2);
+   |                                        ~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:112:28
    |
 LL |     let _: Option<usize> = None.or_else(|| None);
-   |                            ^^^^^----------------
-   |                                 |
-   |                                 help: use `or(..)` instead: `or(None)`
+   |                            ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _: Option<usize> = None.or(None);
+   |                                 ~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:115:13
    |
 LL |     let _ = deep.0.unwrap_or_else(|| 2);
-   |             ^^^^^^^--------------------
-   |                    |
-   |                    help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = deep.0.unwrap_or(2);
+   |                    ~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:116:13
    |
 LL |     let _ = deep.0.and_then(|_| ext_opt);
-   |             ^^^^^^^---------------------
-   |                    |
-   |                    help: use `and(..)` instead: `and(ext_opt)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `and` instead
+   |
+LL |     let _ = deep.0.and(ext_opt);
+   |                    ~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:117:13
    |
 LL |     let _ = deep.0.or_else(|| None);
-   |             ^^^^^^^----------------
-   |                    |
-   |                    help: use `or(..)` instead: `or(None)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _ = deep.0.or(None);
+   |                    ~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:118:13
    |
 LL |     let _ = deep.0.get_or_insert_with(|| 2);
-   |             ^^^^^^^------------------------
-   |                    |
-   |                    help: use `get_or_insert(..)` instead: `get_or_insert(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `get_or_insert` instead
+   |
+LL |     let _ = deep.0.get_or_insert(2);
+   |                    ~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:119:13
    |
 LL |     let _ = deep.0.ok_or_else(|| 2);
-   |             ^^^^^^^----------------
-   |                    |
-   |                    help: use `ok_or(..)` instead: `ok_or(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `ok_or` instead
+   |
+LL |     let _ = deep.0.ok_or(2);
+   |                    ~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:150:28
    |
 LL |     let _: Option<usize> = None.or_else(|| Some(3));
-   |                            ^^^^^-------------------
-   |                                 |
-   |                                 help: use `or(..)` instead: `or(Some(3))`
+   |                            ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _: Option<usize> = None.or(Some(3));
+   |                                 ~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:151:13
    |
 LL |     let _ = deep.0.or_else(|| Some(3));
-   |             ^^^^^^^-------------------
-   |                    |
-   |                    help: use `or(..)` instead: `or(Some(3))`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _ = deep.0.or(Some(3));
+   |                    ~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Option::None`
   --> tests/ui/unnecessary_lazy_eval.rs:152:13
    |
 LL |     let _ = opt.or_else(|| Some(3));
-   |             ^^^^-------------------
-   |                 |
-   |                 help: use `or(..)` instead: `or(Some(3))`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _ = opt.or(Some(3));
+   |                 ~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:158:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| 2);
-   |             ^^^^^---------------------
-   |                  |
-   |                  help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = res2.unwrap_or(2);
+   |                  ~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:159:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| astronomers_pi);
-   |             ^^^^^----------------------------------
-   |                  |
-   |                  help: use `unwrap_or(..)` instead: `unwrap_or(astronomers_pi)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = res2.unwrap_or(astronomers_pi);
+   |                  ~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:160:13
    |
 LL |     let _ = res2.unwrap_or_else(|_| ext_str.some_field);
-   |             ^^^^^--------------------------------------
-   |                  |
-   |                  help: use `unwrap_or(..)` instead: `unwrap_or(ext_str.some_field)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = res2.unwrap_or(ext_str.some_field);
+   |                  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:182:35
    |
 LL |     let _: Result<usize, usize> = res.and_then(|_| Err(2));
-   |                                   ^^^^--------------------
-   |                                       |
-   |                                       help: use `and(..)` instead: `and(Err(2))`
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `and` instead
+   |
+LL |     let _: Result<usize, usize> = res.and(Err(2));
+   |                                       ~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:183:35
    |
 LL |     let _: Result<usize, usize> = res.and_then(|_| Err(astronomers_pi));
-   |                                   ^^^^---------------------------------
-   |                                       |
-   |                                       help: use `and(..)` instead: `and(Err(astronomers_pi))`
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `and` instead
+   |
+LL |     let _: Result<usize, usize> = res.and(Err(astronomers_pi));
+   |                                       ~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:184:35
    |
 LL |     let _: Result<usize, usize> = res.and_then(|_| Err(ext_str.some_field));
-   |                                   ^^^^-------------------------------------
-   |                                       |
-   |                                       help: use `and(..)` instead: `and(Err(ext_str.some_field))`
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `and` instead
+   |
+LL |     let _: Result<usize, usize> = res.and(Err(ext_str.some_field));
+   |                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:186:35
    |
 LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(2));
-   |                                   ^^^^------------------
-   |                                       |
-   |                                       help: use `or(..)` instead: `or(Ok(2))`
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _: Result<usize, usize> = res.or(Ok(2));
+   |                                       ~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:187:35
    |
 LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(astronomers_pi));
-   |                                   ^^^^-------------------------------
-   |                                       |
-   |                                       help: use `or(..)` instead: `or(Ok(astronomers_pi))`
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _: Result<usize, usize> = res.or(Ok(astronomers_pi));
+   |                                       ~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:188:35
    |
 LL |     let _: Result<usize, usize> = res.or_else(|_| Ok(ext_str.some_field));
-   |                                   ^^^^-----------------------------------
-   |                                       |
-   |                                       help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
+   |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `or` instead
+   |
+LL |     let _: Result<usize, usize> = res.or(Ok(ext_str.some_field));
+   |                                       ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval.rs:189:35
@@ -324,193 +440,265 @@ LL | |     // some lines
 ...  |
 LL | |     // some lines
 LL | |     or_else(|_| Ok(ext_str.some_field));
-   | |_____----------------------------------^
-   |       |
-   |       help: use `or(..)` instead: `or(Ok(ext_str.some_field))`
+   | |_______________________________________^
+   |
+help: use `or` instead
+   |
+LL |     or(Ok(ext_str.some_field));
+   |     ~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:219:14
    |
 LL |     let _x = false.then(|| i32::MAX + 1);
-   |              ^^^^^^---------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(i32::MAX + 1)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(i32::MAX + 1);
+   |                    ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:221:14
    |
 LL |     let _x = false.then(|| i32::MAX * 2);
-   |              ^^^^^^---------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(i32::MAX * 2)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(i32::MAX * 2);
+   |                    ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:223:14
    |
 LL |     let _x = false.then(|| i32::MAX - 1);
-   |              ^^^^^^---------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(i32::MAX - 1)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(i32::MAX - 1);
+   |                    ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:225:14
    |
 LL |     let _x = false.then(|| i32::MIN - 1);
-   |              ^^^^^^---------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(i32::MIN - 1)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(i32::MIN - 1);
+   |                    ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:227:14
    |
 LL |     let _x = false.then(|| (1 + 2 * 3 - 2 / 3 + 9) << 2);
-   |              ^^^^^^-------------------------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some((1 + 2 * 3 - 2 / 3 + 9) << 2)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some((1 + 2 * 3 - 2 / 3 + 9) << 2);
+   |                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:229:14
    |
 LL |     let _x = false.then(|| 255u8 << 7);
-   |              ^^^^^^-------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(255u8 << 7)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(255u8 << 7);
+   |                    ~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:231:14
    |
 LL |     let _x = false.then(|| 255u8 << 8);
-   |              ^^^^^^-------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(255u8 << 8)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(255u8 << 8);
+   |                    ~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:233:14
    |
 LL |     let _x = false.then(|| 255u8 >> 8);
-   |              ^^^^^^-------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(255u8 >> 8)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(255u8 >> 8);
+   |                    ~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:236:14
    |
 LL |     let _x = false.then(|| i32::MAX + -1);
-   |              ^^^^^^----------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(i32::MAX + -1)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(i32::MAX + -1);
+   |                    ~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:238:14
    |
 LL |     let _x = false.then(|| -i32::MAX);
-   |              ^^^^^^------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(-i32::MAX)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(-i32::MAX);
+   |                    ~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:240:14
    |
 LL |     let _x = false.then(|| -i32::MIN);
-   |              ^^^^^^------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(-i32::MIN)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(-i32::MIN);
+   |                    ~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:243:14
    |
 LL |     let _x = false.then(|| 255 >> -7);
-   |              ^^^^^^------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(255 >> -7)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(255 >> -7);
+   |                    ~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:245:14
    |
 LL |     let _x = false.then(|| 255 << -1);
-   |              ^^^^^^------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(255 << -1)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(255 << -1);
+   |                    ~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:247:14
    |
 LL |     let _x = false.then(|| 1 / 0);
-   |              ^^^^^^--------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(1 / 0)`
+   |              ^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(1 / 0);
+   |                    ~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:249:14
    |
 LL |     let _x = false.then(|| x << -1);
-   |              ^^^^^^----------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(x << -1)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(x << -1);
+   |                    ~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:251:14
    |
 LL |     let _x = false.then(|| x << 2);
-   |              ^^^^^^---------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(x << 2)`
+   |              ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(x << 2);
+   |                    ~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:261:14
    |
 LL |     let _x = false.then(|| x / 0);
-   |              ^^^^^^--------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(x / 0)`
+   |              ^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(x / 0);
+   |                    ~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:263:14
    |
 LL |     let _x = false.then(|| x % 0);
-   |              ^^^^^^--------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(x % 0)`
+   |              ^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(x % 0);
+   |                    ~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:266:14
    |
 LL |     let _x = false.then(|| 1 / -1);
-   |              ^^^^^^---------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(1 / -1)`
+   |              ^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(1 / -1);
+   |                    ~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:268:14
    |
 LL |     let _x = false.then(|| i32::MIN / -1);
-   |              ^^^^^^----------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(i32::MIN / -1)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(i32::MIN / -1);
+   |                    ~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:271:14
    |
 LL |     let _x = false.then(|| i32::MIN / 0);
-   |              ^^^^^^---------------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(i32::MIN / 0)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(i32::MIN / 0);
+   |                    ~~~~~~~~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:273:14
    |
 LL |     let _x = false.then(|| 4 / 2);
-   |              ^^^^^^--------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(4 / 2)`
+   |              ^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(4 / 2);
+   |                    ~~~~~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval.rs:281:14
    |
 LL |     let _x = false.then(|| f1 + f2);
-   |              ^^^^^^----------------
-   |                    |
-   |                    help: use `then_some(..)` instead: `then_some(f1 + f2)`
+   |              ^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _x = false.then_some(f1 + f2);
+   |                    ~~~~~~~~~~~~~~~~~~
 
 error: aborting due to 63 previous errors
 
diff --git a/tests/ui/unnecessary_lazy_eval_unfixable.stderr b/tests/ui/unnecessary_lazy_eval_unfixable.stderr
index b566b119571cb..390235b212474 100644
--- a/tests/ui/unnecessary_lazy_eval_unfixable.stderr
+++ b/tests/ui/unnecessary_lazy_eval_unfixable.stderr
@@ -2,36 +2,47 @@ error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval_unfixable.rs:13:13
    |
 LL |     let _ = Ok(1).unwrap_or_else(|()| 2);
-   |             ^^^^^^----------------------
-   |                   |
-   |                   help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::unnecessary-lazy-evaluations` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_lazy_evaluations)]`
+help: use `unwrap_or` instead
+   |
+LL |     let _ = Ok(1).unwrap_or(2);
+   |                   ~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval_unfixable.rs:19:13
    |
 LL |     let _ = Ok(1).unwrap_or_else(|e::E| 2);
-   |             ^^^^^^------------------------
-   |                   |
-   |                   help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = Ok(1).unwrap_or(2);
+   |                   ~~~~~~~~~~~~
 
 error: unnecessary closure used to substitute value for `Result::Err`
   --> tests/ui/unnecessary_lazy_eval_unfixable.rs:21:13
    |
 LL |     let _ = Ok(1).unwrap_or_else(|SomeStruct { .. }| 2);
-   |             ^^^^^^-------------------------------------
-   |                   |
-   |                   help: use `unwrap_or(..)` instead: `unwrap_or(2)`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `unwrap_or` instead
+   |
+LL |     let _ = Ok(1).unwrap_or(2);
+   |                   ~~~~~~~~~~~~
 
 error: unnecessary closure used with `bool::then`
   --> tests/ui/unnecessary_lazy_eval_unfixable.rs:31:13
    |
 LL |     let _ = true.then(|| -> &[u8] { &[] });
-   |             ^^^^^-------------------------
-   |                  |
-   |                  help: use `then_some(..)` instead: `then_some({ &[] })`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `then_some` instead
+   |
+LL |     let _ = true.then_some({ &[] });
+   |                  ~~~~~~~~~~~~~~~~~~
 
 error: aborting due to 4 previous errors
 

From 0196e2d1dc50e43c6ae6132690e30d355ca56d67 Mon Sep 17 00:00:00 2001
From: Jason Newcomb <jsnewcomb@pm.me>
Date: Sat, 24 Aug 2024 16:31:55 -0400
Subject: [PATCH 54/54] Bump nightly version -> 2024-08-23

---
 rust-toolchain | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/rust-toolchain b/rust-toolchain
index 5fbe4e544a9e4..0be2e81810ebe 100644
--- a/rust-toolchain
+++ b/rust-toolchain
@@ -1,4 +1,4 @@
 [toolchain]
-channel = "nightly-2024-08-08"
+channel = "nightly-2024-08-23"
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
 profile = "minimal"