about summary refs log tree commit diff
diff options
context:
space:
mode:
authorPhilipp Krones <hello@philkrones.com>2024-05-30 10:49:05 +0200
committerPhilipp Krones <hello@philkrones.com>2024-05-30 10:49:05 +0200
commit9b320af78708ed83f560a87cd1debb2844460287 (patch)
treeada1e8c9c066615c757bad46cc8b54f6485275d7
parentcaa187f3bc9604c78dfbc3ffabbe1372cb528639 (diff)
parentc9139bd546d9cd69df817faeab62c5f9b1a51337 (diff)
downloadrust-9b320af78708ed83f560a87cd1debb2844460287.tar.gz
rust-9b320af78708ed83f560a87cd1debb2844460287.zip
Merge commit 'c9139bd546d9cd69df817faeab62c5f9b1a51337' into clippy-subtree-update
-rw-r--r--src/tools/clippy/book/src/configuration.md2
-rw-r--r--src/tools/clippy/book/src/development/adding_lints.md5
-rw-r--r--src/tools/clippy/clippy_dev/src/new_lint.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/absolute_paths.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/allow_attributes.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/as_conversions.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/asm_syntax.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/mod.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/mod.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/create_dir.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/dbg_macro.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/default_union_representation.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/derive.rs31
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/markdown.rs28
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/drop_forget_ref.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/else_if_without_else.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/empty_drop.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/empty_enum.rs45
-rw-r--r--src/tools/clippy/clippy_lints/src/empty_with_brackets.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/endian_bytes.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/error_impl_error.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/exhaustive_items.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/exit.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/float_literal.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/format_push_string.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/mod.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_return.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/indexing_slicing.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/inherent_impl.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/init_numbered_fields.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/large_include_file.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/let_underscore.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/literal_representation.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/mod.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/mod.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs418
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs47
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/utils.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/min_ident_chars.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/misc_early/mod.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_assert_message.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_doc.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_inline.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_trait_methods.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/module_style.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/mutex_atomic.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_bool.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/non_expressive_names.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/mod.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/panic_unimplemented.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/partial_pub_fields.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/pub_use.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/question_mark_used.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/raw_strings.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_slicing.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/ref_patterns.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/returns.rs34
-rw-r--r--src/tools/clippy/clippy_lints/src/same_name_method.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/semicolon_block.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/shadow.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/single_call_fn.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/std_instead_of_core.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/strings.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/types/mod.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/unicode.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unwrap_in_result.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/visibility.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs13
-rw-r--r--src/tools/clippy/clippy_utils/src/consts.rs6
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs22
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs6
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/tests/ui-internal/disallow_span_lint.rs8
-rw-r--r--src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr14
-rw-r--r--src/tools/clippy/tests/ui/derive_partial_eq_without_eq.fixed17
-rw-r--r--src/tools/clippy/tests/ui/derive_partial_eq_without_eq.rs17
-rw-r--r--src/tools/clippy/tests/ui/derive_partial_eq_without_eq.stderr26
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.fixed3
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.rs3
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.stderr8
-rw-r--r--src/tools/clippy/tests/ui/doc/issue_12795.fixed9
-rw-r--r--src/tools/clippy/tests/ui/doc/issue_12795.rs9
-rw-r--r--src/tools/clippy/tests/ui/doc/issue_12795.stderr48
-rw-r--r--src/tools/clippy/tests/ui/iter_on_empty_collections.fixed22
-rw-r--r--src/tools/clippy/tests/ui/iter_on_empty_collections.rs22
-rw-r--r--src/tools/clippy/tests/ui/iter_on_empty_collections.stderr8
-rw-r--r--src/tools/clippy/tests/ui/let_and_return.fixed34
-rw-r--r--src/tools/clippy/tests/ui/let_and_return.rs34
-rw-r--r--src/tools/clippy/tests/ui/let_and_return.stderr72
-rw-r--r--src/tools/clippy/tests/ui/many_single_char_names.rs2
-rw-r--r--src/tools/clippy/tests/ui/many_single_char_names.stderr10
-rw-r--r--src/tools/clippy/tests/ui/needless_return.fixed4
-rw-r--r--src/tools/clippy/tests/ui/needless_return.rs4
-rw-r--r--src/tools/clippy/tests/ui/needless_return.stderr14
-rw-r--r--src/tools/clippy/tests/ui/numbered_fields.fixed5
-rw-r--r--src/tools/clippy/tests/ui/numbered_fields.rs5
-rw-r--r--src/tools/clippy/tests/ui/numbered_fields.stderr8
-rw-r--r--src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs98
-rw-r--r--src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr206
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed29
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs29
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr44
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.stderr2
-rw-r--r--src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs11
-rw-r--r--src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr8
128 files changed, 1422 insertions, 676 deletions
diff --git a/src/tools/clippy/book/src/configuration.md b/src/tools/clippy/book/src/configuration.md
index 9eb067abd91..ea549e4df4a 100644
--- a/src/tools/clippy/book/src/configuration.md
+++ b/src/tools/clippy/book/src/configuration.md
@@ -133,7 +133,7 @@ Very rarely, you may wish to prevent Clippy from evaluating certain sections of
 `clippy` cfg is not set. You may need to provide a stub so that the code compiles:
 
 ```rust
-#[cfg(not(clippy)]
+#[cfg(not(clippy))]
 include!(concat!(env!("OUT_DIR"), "/my_big_function-generated.rs"));
 
 #[cfg(clippy)]
diff --git a/src/tools/clippy/book/src/development/adding_lints.md b/src/tools/clippy/book/src/development/adding_lints.md
index 415022612ca..48c00bcbf34 100644
--- a/src/tools/clippy/book/src/development/adding_lints.md
+++ b/src/tools/clippy/book/src/development/adding_lints.md
@@ -587,6 +587,11 @@ declare_clippy_lint! {
 }
 ```
 
+If the lint is in the `restriction` group because it lints things that are not
+necessarily “bad” but are more of a style choice, then replace the
+“Why is this bad?” section heading with “Why restrict this?”, to avoid writing
+“Why is this bad? It isn't, but ...”.
+
 Once your lint is merged, this documentation will show up in the [lint
 list][lint_list].
 
diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs
index b6481dde4dd..2e56eb8ec15 100644
--- a/src/tools/clippy/clippy_dev/src/new_lint.rs
+++ b/src/tools/clippy/clippy_dev/src/new_lint.rs
@@ -331,12 +331,17 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
 }
 
 fn get_lint_declaration(name_upper: &str, category: &str) -> String {
+    let justification_heading = if category == "restriction" {
+        "Why restrict this?"
+    } else {
+        "Why is this bad?"
+    };
     formatdoc!(
         r#"
             declare_clippy_lint! {{
                 /// ### What it does
                 ///
-                /// ### Why is this bad?
+                /// ### {justification_heading}
                 ///
                 /// ### Example
                 /// ```no_run
diff --git a/src/tools/clippy/clippy_lints/src/absolute_paths.rs b/src/tools/clippy/clippy_lints/src/absolute_paths.rs
index 8ba661afeeb..461117cf965 100644
--- a/src/tools/clippy/clippy_lints/src/absolute_paths.rs
+++ b/src/tools/clippy/clippy_lints/src/absolute_paths.rs
@@ -12,7 +12,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for usage of items through absolute paths, like `std::env::current_dir`.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Many codebases have their own style when it comes to importing, but one that is seldom used
     /// is using absolute paths *everywhere*. This is generally considered unidiomatic, and you
     /// should add a `use` statement.
diff --git a/src/tools/clippy/clippy_lints/src/allow_attributes.rs b/src/tools/clippy/clippy_lints/src/allow_attributes.rs
index 39fc49dee37..123d0e51eee 100644
--- a/src/tools/clippy/clippy_lints/src/allow_attributes.rs
+++ b/src/tools/clippy/clippy_lints/src/allow_attributes.rs
@@ -19,10 +19,11 @@ declare_clippy_lint! {
     /// This lint only warns outer attributes (`#[allow]`), as inner attributes
     /// (`#![allow]`) are usually used to enable or disable lints on a global scale.
     ///
-    /// ### Why is this bad?
-    /// `#[expect]` attributes suppress the lint emission, but emit a warning, if
+    /// ### Why restrict this?
+    /// `#[allow]` attributes can linger after their reason for existence is gone.
+    /// `#[expect]` attributes suppress the lint emission, but emit a warning if
     /// the expectation is unfulfilled. This can be useful to be notified when the
-    /// lint is no longer triggered.
+    /// lint is no longer triggered, which may indicate the attribute can be removed.
     ///
     /// ### Example
     /// ```rust,ignore
diff --git a/src/tools/clippy/clippy_lints/src/as_conversions.rs b/src/tools/clippy/clippy_lints/src/as_conversions.rs
index e3daf75c3eb..cfa25005a05 100644
--- a/src/tools/clippy/clippy_lints/src/as_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/as_conversions.rs
@@ -17,7 +17,7 @@ declare_clippy_lint! {
     /// There is a good explanation the reason why this lint should work in this way and how it is useful
     /// [in this issue](https://github.com/rust-lang/rust-clippy/issues/5122).
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// `as` conversions will perform many kinds of
     /// conversions, including silently lossy conversions and dangerous coercions.
     /// There are cases when it makes sense to use `as`, so the lint is
diff --git a/src/tools/clippy/clippy_lints/src/asm_syntax.rs b/src/tools/clippy/clippy_lints/src/asm_syntax.rs
index 7c88bfc97ca..0db1456d40b 100644
--- a/src/tools/clippy/clippy_lints/src/asm_syntax.rs
+++ b/src/tools/clippy/clippy_lints/src/asm_syntax.rs
@@ -65,9 +65,8 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for usage of Intel x86 assembly syntax.
     ///
-    /// ### Why is this bad?
-    /// The lint has been enabled to indicate a preference
-    /// for AT&T x86 assembly syntax.
+    /// ### Why restrict this?
+    /// To enforce consistent use of AT&T x86 assembly syntax.
     ///
     /// ### Example
     ///
@@ -114,9 +113,8 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for usage of AT&T x86 assembly syntax.
     ///
-    /// ### Why is this bad?
-    /// The lint has been enabled to indicate a preference
-    /// for Intel x86 assembly syntax.
+    /// ### Why restrict this?
+    /// To enforce consistent use of Intel x86 assembly syntax.
     ///
     /// ### Example
     ///
diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs
index aec22965b1b..7217686dcca 100644
--- a/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs
+++ b/src/tools/clippy/clippy_lints/src/assertions_on_result_states.rs
@@ -16,23 +16,33 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for `assert!(r.is_ok())` or `assert!(r.is_err())` calls.
     ///
-    /// ### Why is this bad?
-    /// An assertion failure cannot output an useful message of the error.
+    /// ### Why restrict this?
+    /// This form of assertion does not show any of the information present in the `Result`
+    /// other than which variant it isn’t.
     ///
     /// ### Known problems
     /// The suggested replacement decreases the readability of code and log output.
     ///
     /// ### Example
-    /// ```rust,ignore
+    /// ```rust,no_run
     /// # let r = Ok::<_, ()>(());
     /// assert!(r.is_ok());
-    /// # let r = Err::<_, ()>(());
+    /// # let r = Err::<(), _>(());
     /// assert!(r.is_err());
     /// ```
+    ///
+    /// Use instead:
+    ///
+    /// ```rust,no_run
+    /// # let r = Ok::<_, ()>(());
+    /// r.unwrap();
+    /// # let r = Err::<(), _>(());
+    /// r.unwrap_err();
+    /// ```
     #[clippy::version = "1.64.0"]
     pub ASSERTIONS_ON_RESULT_STATES,
     restriction,
-    "`assert!(r.is_ok())`/`assert!(r.is_err())` gives worse error message than directly calling `r.unwrap()`/`r.unwrap_err()`"
+    "`assert!(r.is_ok())` or `assert!(r.is_err())` gives worse panic messages than directly calling `r.unwrap()` or `r.unwrap_err()`"
 }
 
 declare_lint_pass!(AssertionsOnResultStates => [ASSERTIONS_ON_RESULT_STATES]);
diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs
index 39f40607799..a24bd5ed44b 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs
@@ -309,9 +309,9 @@ declare_clippy_lint! {
     ///
     /// (This requires the `lint_reasons` feature)
     ///
-    /// ### Why is this bad?
-    /// Allowing a lint should always have a reason. This reason should be documented to
-    /// ensure that others understand the reasoning
+    /// ### Why restrict this?
+    /// Justifying each `allow` helps readers understand the reasoning,
+    /// and may allow removing `allow` attributes if their purpose is obsolete.
     ///
     /// ### Example
     /// ```no_run
diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs
index bd2c96f01f6..e60c36ced75 100644
--- a/src/tools/clippy/clippy_lints/src/casts/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs
@@ -303,7 +303,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for casts of a function pointer to any integer type.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Casting a function pointer to an integer can have surprising results and can occur
     /// accidentally if parentheses are omitted from a function call. If you aren't doing anything
     /// low-level with function pointers then you can opt-out of casting functions to integers in
@@ -535,8 +535,8 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for the usage of `as _` conversion using inferred type.
     ///
-    /// ### Why is this bad?
-    /// The conversion might include lossy conversion and dangerous cast that might go
+    /// ### Why restrict this?
+    /// The conversion might include lossy conversion or a dangerous cast that might go
     /// undetected due to the type being inferred.
     ///
     /// The lint is allowed by default as using `_` is less wordy than always specifying the type.
diff --git a/src/tools/clippy/clippy_lints/src/create_dir.rs b/src/tools/clippy/clippy_lints/src/create_dir.rs
index 7a3d5a07091..27c00948a8f 100644
--- a/src/tools/clippy/clippy_lints/src/create_dir.rs
+++ b/src/tools/clippy/clippy_lints/src/create_dir.rs
@@ -10,8 +10,10 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks usage of `std::fs::create_dir` and suggest using `std::fs::create_dir_all` instead.
     ///
-    /// ### Why is this bad?
-    /// Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`.
+    /// ### Why restrict this?
+    /// Sometimes `std::fs::create_dir` is mistakenly chosen over `std::fs::create_dir_all`,
+    /// resulting in failure when more than one directory needs to be created or when the directory already exists.
+    /// Crates which never need to specifically create a single directory may wish to prevent this mistake.
     ///
     /// ### Example
     /// ```rust,ignore
diff --git a/src/tools/clippy/clippy_lints/src/dbg_macro.rs b/src/tools/clippy/clippy_lints/src/dbg_macro.rs
index db593726604..b0590b0a71c 100644
--- a/src/tools/clippy/clippy_lints/src/dbg_macro.rs
+++ b/src/tools/clippy/clippy_lints/src/dbg_macro.rs
@@ -14,7 +14,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for usage of the [`dbg!`](https://doc.rust-lang.org/std/macro.dbg.html) macro.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// The `dbg!` macro is intended as a debugging tool. It should not be present in released
     /// software or committed to a version control system.
     ///
diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
index 1d6c4ce72e1..fbc4ede37b1 100644
--- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
+++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
@@ -22,9 +22,8 @@ declare_clippy_lint! {
     ///
     /// See [RFC0212](https://github.com/rust-lang/rfcs/blob/master/text/0212-restore-int-fallback.md) for more information about the fallback.
     ///
-    /// ### Why is this bad?
-    /// For those who are very careful about types, default numeric fallback
-    /// can be a pitfall that cause unexpected runtime behavior.
+    /// ### Why restrict this?
+    /// To ensure that every numeric type is chosen explicitly rather than implicitly.
     ///
     /// ### Known problems
     /// This lint can only be allowed at the function level or above.
diff --git a/src/tools/clippy/clippy_lints/src/default_union_representation.rs b/src/tools/clippy/clippy_lints/src/default_union_representation.rs
index b4290b6437f..3fa9bad0d03 100644
--- a/src/tools/clippy/clippy_lints/src/default_union_representation.rs
+++ b/src/tools/clippy/clippy_lints/src/default_union_representation.rs
@@ -10,7 +10,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Displays a warning when a union is declared with the default representation (without a `#[repr(C)]` attribute).
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Unions in Rust have unspecified layout by default, despite many people thinking that they
     /// lay out each field at the start of the union (like C does). That is, there are no guarantees
     /// about the offset of the fields for unions with multiple non-ZST fields without an explicitly
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index c6aef9ac2d6..0276c64ef9b 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -2,7 +2,9 @@ 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::{expr_use_ctxt, get_parent_expr, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode};
+use clippy_utils::{
+    expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode,
+};
 use core::mem;
 use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
 use rustc_data_structures::fx::FxIndexMap;
@@ -1038,14 +1040,8 @@ fn report<'tcx>(
             );
         },
         State::ExplicitDeref { mutability } => {
-            if matches!(
-                expr.kind,
-                ExprKind::Block(..)
-                    | ExprKind::ConstBlock(_)
-                    | ExprKind::If(..)
-                    | ExprKind::Loop(..)
-                    | ExprKind::Match(..)
-            ) && let ty::Ref(_, ty, _) = data.adjusted_ty.kind()
+            if is_block_like(expr)
+                && let ty::Ref(_, ty, _) = data.adjusted_ty.kind()
                 && ty.is_sized(cx.tcx, cx.param_env)
             {
                 // Rustc bug: auto deref doesn't work on block expression when targeting sized types.
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
index 2a644922fb1..636698e96f6 100644
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ b/src/tools/clippy/clippy_lints/src/derive.rs
@@ -1,17 +1,17 @@
-use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_note, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
 use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, paths};
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
 use rustc_hir::intravisit::{walk_expr, walk_fn, walk_item, FnKind, Visitor};
 use rustc_hir::{
-    self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Safety, Impl, Item, ItemKind, UnsafeSource,
+    self as hir, BlockCheckMode, BodyId, Expr, ExprKind, FnDecl, Impl, Item, ItemKind, Safety, UnsafeSource,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
 use rustc_middle::traits::Reveal;
 use rustc_middle::ty::{
-    self, ClauseKind, GenericArgKind, GenericParamDefKind, ParamEnv, Upcast, TraitPredicate, Ty, TyCtxt,
+    self, ClauseKind, GenericArgKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast,
 };
 use rustc_session::declare_lint_pass;
 use rustc_span::def_id::LocalDefId;
@@ -390,13 +390,17 @@ fn check_unsafe_derive_deserialize<'tcx>(
             .map(|imp_did| cx.tcx.hir().expect_item(imp_did.expect_local()))
             .any(|imp| has_unsafe(cx, imp))
     {
-        span_lint_and_help(
+        span_lint_hir_and_then(
             cx,
             UNSAFE_DERIVE_DESERIALIZE,
+            adt_hir_id,
             item.span,
             "you are deriving `serde::Deserialize` on a type that has methods using `unsafe`",
-            None,
-            "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html",
+            |diag| {
+                diag.help(
+                    "consider implementing `serde::Deserialize` manually. See https://serde.rs/impl-deserialize.html",
+                );
+            },
         );
     }
 }
@@ -452,20 +456,27 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r
         && !has_non_exhaustive_attr(cx.tcx, *adt)
         && !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id)
         && let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id)
+        && let Some(local_def_id) = adt.did().as_local()
         // If all of our fields implement `Eq`, we can implement `Eq` too
         && adt
             .all_fields()
             .map(|f| f.ty(cx.tcx, args))
             .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, None, &[]))
     {
-        span_lint_and_sugg(
+        span_lint_hir_and_then(
             cx,
             DERIVE_PARTIAL_EQ_WITHOUT_EQ,
+            cx.tcx.local_def_id_to_hir_id(local_def_id),
             span.ctxt().outer_expn_data().call_site,
             "you are deriving `PartialEq` and can implement `Eq`",
-            "consider deriving `Eq` as well",
-            "PartialEq, Eq".to_string(),
-            Applicability::MachineApplicable,
+            |diag| {
+                diag.span_suggestion(
+                    span.ctxt().outer_expn_data().call_site,
+                    "consider deriving `Eq` as well",
+                    "PartialEq, Eq",
+                    Applicability::MachineApplicable,
+                );
+            },
         );
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
index def4b5932b4..a995f06fb73 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
@@ -20,11 +20,11 @@ declare_clippy_lint! {
     /// [aliases]: http://www.unicode.org/reports/tr24/tr24-31.html#Script_Value_Aliases
     /// [supported_scripts]: https://www.unicode.org/iso15924/iso15924-codes.html
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// It may be not desired to have many different scripts for
     /// identifiers in the codebase.
     ///
-    /// Note that if you only want to allow plain English, you might want to use
+    /// Note that if you only want to allow typical English, you might want to use
     /// built-in [`non_ascii_idents`] lint instead.
     ///
     /// [`non_ascii_idents`]: https://doc.rust-lang.org/rustc/lints/listing/allowed-by-default.html#non-ascii-idents
diff --git a/src/tools/clippy/clippy_lints/src/doc/markdown.rs b/src/tools/clippy/clippy_lints/src/doc/markdown.rs
index 1add02af310..41c0bcd55ad 100644
--- a/src/tools/clippy/clippy_lints/src/doc/markdown.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/markdown.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
+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};
@@ -30,6 +30,7 @@ pub fn check(
             word = tmp_word;
         }
 
+        let original_len = word.len();
         word = word.trim_start_matches(trim_pattern);
 
         // Remove leading or trailing single `:` which may be part of a sentence.
@@ -44,6 +45,25 @@ pub fn check(
             continue;
         }
 
+        // Ensure that all reachable matching closing parens are included as well.
+        let size_diff = original_len - word.len();
+        let mut open_parens = 0;
+        let mut close_parens = 0;
+        for c in word.chars() {
+            if c == '(' {
+                open_parens += 1;
+            } else if c == ')' {
+                close_parens += 1;
+            }
+        }
+        while close_parens < open_parens
+            && let Some(tmp_word) = orig_word.get(size_diff..=(word.len() + size_diff))
+            && tmp_word.ends_with(')')
+        {
+            word = tmp_word;
+            close_parens += 1;
+        }
+
         // Adjust for the current word
         let offset = word.as_ptr() as usize - text.as_ptr() as usize;
         let span = Span::new(
@@ -92,13 +112,15 @@ fn check_word(cx: &LateContext<'_>, word: &str, span: Span, code_level: isize, b
     if let Ok(url) = Url::parse(word) {
         // try to get around the fact that `foo::bar` parses as a valid URL
         if !url.cannot_be_a_base() {
-            span_lint(
+            span_lint_and_sugg(
                 cx,
                 DOC_MARKDOWN,
                 span,
                 "you should put bare URLs between `<`/`>` or make a proper Markdown link",
+                "try",
+                format!("<{word}>"),
+                Applicability::MachineApplicable,
             );
-
             return;
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs
index 9f08973948a..3d875e7ac2d 100644
--- a/src/tools/clippy/clippy_lints/src/doc/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs
@@ -261,7 +261,7 @@ declare_clippy_lint! {
     /// Checks for the doc comments of publicly visible
     /// safe functions and traits and warns if there is a `# Safety` section.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Safe functions and traits are safe to implement and therefore do not
     /// need to describe safety preconditions that users are required to uphold.
     ///
diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
index 119473c2454..4a6ffcd9a78 100644
--- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
@@ -52,9 +52,10 @@ declare_clippy_lint! {
     /// Checks for usage of `std::mem::forget(t)` where `t` is
     /// `Drop` or has a field that implements `Drop`.
     ///
-    /// ### Why is this bad?
-    /// `std::mem::forget(t)` prevents `t` from running its
-    /// destructor, possibly causing leaks.
+    /// ### Why restrict this?
+    /// `std::mem::forget(t)` prevents `t` from running its destructor, possibly causing leaks.
+    /// It is not possible to detect all means of creating leaks, but it may be desirable to
+    /// prohibit the simple ones.
     ///
     /// ### Example
     /// ```no_run
diff --git a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs
index a6ca7fe9e0b..bb6f9aac223 100644
--- a/src/tools/clippy/clippy_lints/src/else_if_without_else.rs
+++ b/src/tools/clippy/clippy_lints/src/else_if_without_else.rs
@@ -11,7 +11,7 @@ declare_clippy_lint! {
     /// Checks for usage of if expressions with an `else if` branch,
     /// but without a final `else` branch.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Some coding guidelines require this (e.g., MISRA-C:2004 Rule 14.10).
     ///
     /// ### Example
diff --git a/src/tools/clippy/clippy_lints/src/empty_drop.rs b/src/tools/clippy/clippy_lints/src/empty_drop.rs
index 74db250b3ae..c5fc72b5e2d 100644
--- a/src/tools/clippy/clippy_lints/src/empty_drop.rs
+++ b/src/tools/clippy/clippy_lints/src/empty_drop.rs
@@ -9,7 +9,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for empty `Drop` implementations.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Empty `Drop` implementations have no effect when dropping an instance of the type. They are
     /// most likely useless. However, an empty `Drop` implementation prevents a type from being
     /// destructured, which might be the intention behind adding the implementation as a marker.
diff --git a/src/tools/clippy/clippy_lints/src/empty_enum.rs b/src/tools/clippy/clippy_lints/src/empty_enum.rs
index 420888b6ccb..d16714695cb 100644
--- a/src/tools/clippy/clippy_lints/src/empty_enum.rs
+++ b/src/tools/clippy/clippy_lints/src/empty_enum.rs
@@ -7,32 +7,53 @@ use rustc_session::declare_lint_pass;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for `enum`s with no variants.
+    /// Checks for `enum`s with no variants, which therefore are uninhabited types
+    /// (cannot be instantiated).
     ///
-    /// As of this writing, the `never_type` is still a
-    /// nightly-only experimental API. Therefore, this lint is only triggered
-    /// if the `never_type` is enabled.
+    /// As of this writing, the `never_type` is still a nightly-only experimental API.
+    /// Therefore, this lint is only triggered if `#![feature(never_type)]` is enabled.
     ///
     /// ### Why is this bad?
-    /// If you want to introduce a type which
-    /// can't be instantiated, you should use `!` (the primitive type "never"),
-    /// or a wrapper around it, because `!` has more extensive
-    /// compiler support (type inference, etc...) and wrappers
-    /// around it are the conventional way to define an uninhabited type.
-    /// For further information visit [never type documentation](https://doc.rust-lang.org/std/primitive.never.html)
+    /// * If you only want a type which can’t be instantiated, you should use [`!`]
+    ///   (the primitive type "never"), because [`!`] has more extensive compiler support
+    ///   (type inference, etc.) and implementations of common traits.
     ///
+    /// * If you need to introduce a distinct type, consider using a [newtype] `struct`
+    ///   containing [`!`] instead (`struct MyType(pub !)`), because it is more idiomatic
+    ///   to use a `struct` rather than an `enum` when an `enum` is unnecessary.
+    ///
+    ///   If you do this, note that the [visibility] of the [`!`] field determines whether
+    ///   the uninhabitedness is visible in documentation, and whether it can be pattern
+    ///   matched to mark code unreachable. If the field is not visible, then the struct
+    ///   acts like any other struct with private fields.
+    ///
+    /// * If the enum has no variants only because all variants happen to be
+    ///   [disabled by conditional compilation][cfg], then it would be appropriate
+    ///   to allow the lint, with `#[allow(empty_enum)]`.
+    ///
+    /// For further information, visit
+    /// [the never type’s documentation][`!`].
     ///
     /// ### Example
     /// ```no_run
-    /// enum Test {}
+    /// enum CannotExist {}
     /// ```
     ///
     /// Use instead:
     /// ```no_run
     /// #![feature(never_type)]
     ///
-    /// struct Test(!);
+    /// /// Use the `!` type directly...
+    /// type CannotExist = !;
+    ///
+    /// /// ...or define a newtype which is distinct.
+    /// struct CannotExist2(pub !);
     /// ```
+    ///
+    /// [`!`]: https://doc.rust-lang.org/std/primitive.never.html
+    /// [cfg]: https://doc.rust-lang.org/reference/conditional-compilation.html
+    /// [newtype]: https://doc.rust-lang.org/book/ch19-04-advanced-types.html#using-the-newtype-pattern-for-type-safety-and-abstraction
+    /// [visibility]: https://doc.rust-lang.org/reference/visibility-and-privacy.html
     #[clippy::version = "pre 1.29.0"]
     pub EMPTY_ENUM,
     pedantic,
diff --git a/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs
index 969df6d85b5..745599b0e57 100644
--- a/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs
+++ b/src/tools/clippy/clippy_lints/src/empty_with_brackets.rs
@@ -11,16 +11,23 @@ declare_clippy_lint! {
     /// ### What it does
     /// Finds structs without fields (a so-called "empty struct") that are declared with brackets.
     ///
-    /// ### Why is this bad?
-    /// Empty brackets after a struct declaration can be omitted.
+    /// ### Why restrict this?
+    /// Empty brackets after a struct declaration can be omitted,
+    /// and it may be desirable to do so consistently for style.
+    ///
+    /// However, removing the brackets also introduces a public constant named after the struct,
+    /// so this is not just a syntactic simplification but an an API change, and adding them back
+    /// is a *breaking* API change.
     ///
     /// ### Example
     /// ```no_run
     /// struct Cookie {}
+    /// struct Biscuit();
     /// ```
     /// Use instead:
     /// ```no_run
     /// struct Cookie;
+    /// struct Biscuit;
     /// ```
     #[clippy::version = "1.62.0"]
     pub EMPTY_STRUCTS_WITH_BRACKETS,
@@ -32,14 +39,20 @@ declare_clippy_lint! {
     /// ### What it does
     /// Finds enum variants without fields that are declared with empty brackets.
     ///
-    /// ### Why is this bad?
-    /// Empty brackets while defining enum variants are redundant and can be omitted.
+    /// ### Why restrict this?
+    /// Empty brackets after a enum variant declaration are redundant and can be omitted,
+    /// and it may be desirable to do so consistently for style.
+    ///
+    /// However, removing the brackets also introduces a public constant named after the variant,
+    /// so this is not just a syntactic simplification but an an API change, and adding them back
+    /// is a *breaking* API change.
     ///
     /// ### Example
     /// ```no_run
     /// enum MyEnum {
     ///     HasData(u8),
-    ///     HasNoData(), // redundant parentheses
+    ///     HasNoData(),       // redundant parentheses
+    ///     NoneHereEither {}, // redundant braces
     /// }
     /// ```
     ///
@@ -48,6 +61,7 @@ declare_clippy_lint! {
     /// enum MyEnum {
     ///     HasData(u8),
     ///     HasNoData,
+    ///     NoneHereEither,
     /// }
     /// ```
     #[clippy::version = "1.77.0"]
diff --git a/src/tools/clippy/clippy_lints/src/endian_bytes.rs b/src/tools/clippy/clippy_lints/src/endian_bytes.rs
index dd03df797de..bb766e96338 100644
--- a/src/tools/clippy/clippy_lints/src/endian_bytes.rs
+++ b/src/tools/clippy/clippy_lints/src/endian_bytes.rs
@@ -13,8 +13,9 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for the usage of the `to_ne_bytes` method and/or the function `from_ne_bytes`.
     ///
-    /// ### Why is this bad?
-    /// It's not, but some may prefer to specify the target endianness explicitly.
+    /// ### Why restrict this?
+    /// To ensure use of explicitly chosen endianness rather than the target’s endianness,
+    /// such as when implementing network protocols or file formats rather than FFI.
     ///
     /// ### Example
     /// ```rust,ignore
@@ -31,9 +32,8 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for the usage of the `to_le_bytes` method and/or the function `from_le_bytes`.
     ///
-    /// ### Why is this bad?
-    /// It's not, but some may wish to lint usage of this method, either to suggest using the host
-    /// endianness or big endian.
+    /// ### Why restrict this?
+    /// To ensure use of big endian or the target’s endianness rather than little endian.
     ///
     /// ### Example
     /// ```rust,ignore
@@ -50,9 +50,8 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for the usage of the `to_be_bytes` method and/or the function `from_be_bytes`.
     ///
-    /// ### Why is this bad?
-    /// It's not, but some may wish to lint usage of this method, either to suggest using the host
-    /// endianness or little endian.
+    /// ### Why restrict this?
+    /// To ensure use of little endian or the target’s endianness rather than big endian.
     ///
     /// ### Example
     /// ```rust,ignore
diff --git a/src/tools/clippy/clippy_lints/src/error_impl_error.rs b/src/tools/clippy/clippy_lints/src/error_impl_error.rs
index 8dbb47fadc5..8e49138cd26 100644
--- a/src/tools/clippy/clippy_lints/src/error_impl_error.rs
+++ b/src/tools/clippy/clippy_lints/src/error_impl_error.rs
@@ -12,7 +12,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for types named `Error` that implement `Error`.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// It can become confusing when a codebase has 20 types all named `Error`, requiring either
     /// aliasing them in the `use` statement or qualifying them like `my_module::Error`. This
     /// hinders comprehension, as it requires you to memorize every variation of importing `Error`
diff --git a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs
index 9ffda645742..436dc8611bd 100644
--- a/src/tools/clippy/clippy_lints/src/exhaustive_items.rs
+++ b/src/tools/clippy/clippy_lints/src/exhaustive_items.rs
@@ -10,10 +10,10 @@ declare_clippy_lint! {
     /// ### What it does
     /// Warns on any exported `enum`s that are not tagged `#[non_exhaustive]`
     ///
-    /// ### Why is this bad?
-    /// Exhaustive enums are typically fine, but a project which does
-    /// not wish to make a stability commitment around exported enums may wish to
-    /// disable them by default.
+    /// ### Why restrict this?
+    /// Making an `enum` exhaustive is a stability commitment: adding a variant is a breaking change.
+    /// A project may wish to ensure that there are no exhaustive enums or that every exhaustive
+    /// `enum` is explicitly `#[allow]`ed.
     ///
     /// ### Example
     /// ```no_run
@@ -40,10 +40,10 @@ declare_clippy_lint! {
     /// ### What it does
     /// Warns on any exported `struct`s that are not tagged `#[non_exhaustive]`
     ///
-    /// ### Why is this bad?
-    /// Exhaustive structs are typically fine, but a project which does
-    /// not wish to make a stability commitment around exported structs may wish to
-    /// disable them by default.
+    /// ### Why restrict this?
+    /// Making a `struct` exhaustive is a stability commitment: adding a field is a breaking change.
+    /// A project may wish to ensure that there are no exhaustive structs or that every exhaustive
+    /// `struct` is explicitly `#[allow]`ed.
     ///
     /// ### Example
     /// ```no_run
diff --git a/src/tools/clippy/clippy_lints/src/exit.rs b/src/tools/clippy/clippy_lints/src/exit.rs
index 106844dd434..91c94d66458 100644
--- a/src/tools/clippy/clippy_lints/src/exit.rs
+++ b/src/tools/clippy/clippy_lints/src/exit.rs
@@ -9,11 +9,13 @@ declare_clippy_lint! {
     /// ### What it does
     /// Detects calls to the `exit()` function which terminates the program.
     ///
-    /// ### Why is this bad?
-    /// Exit terminates the program at the location it is called. For unrecoverable
-    /// errors `panics` should be used to provide a stacktrace and potentially other
-    /// information. A normal termination or one with an error code should happen in
-    /// the main function.
+    /// ### Why restrict this?
+    /// `exit()` immediately terminates the program with no information other than an exit code.
+    /// This provides no means to troubleshoot a problem, and may be an unexpected side effect.
+    ///
+    /// Codebases may use this lint to require that all exits are performed either by panicking
+    /// (which produces a message, a code location, and optionally a backtrace)
+    /// or by returning from `main()` (which is a single place to look).
     ///
     /// ### Example
     /// ```no_run
diff --git a/src/tools/clippy/clippy_lints/src/float_literal.rs b/src/tools/clippy/clippy_lints/src/float_literal.rs
index 2cd4e9e99a5..4ec9bd757ff 100644
--- a/src/tools/clippy/clippy_lints/src/float_literal.rs
+++ b/src/tools/clippy/clippy_lints/src/float_literal.rs
@@ -38,9 +38,9 @@ declare_clippy_lint! {
     /// Checks for whole number float literals that
     /// cannot be represented as the underlying type without loss.
     ///
-    /// ### Why is this bad?
-    /// Rust will silently lose precision during
-    /// conversion to a float.
+    /// ### Why restrict this?
+    /// If the value was intended to be exact, it will not be.
+    /// This may be especially surprising when the lost precision is to the left of the decimal point.
     ///
     /// ### Example
     /// ```no_run
diff --git a/src/tools/clippy/clippy_lints/src/format_push_string.rs b/src/tools/clippy/clippy_lints/src/format_push_string.rs
index 2b08437d827..a75538dd329 100644
--- a/src/tools/clippy/clippy_lints/src/format_push_string.rs
+++ b/src/tools/clippy/clippy_lints/src/format_push_string.rs
@@ -11,7 +11,7 @@ declare_clippy_lint! {
     /// Detects cases where the result of a `format!` call is
     /// appended to an existing `String`.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Introduces an extra, avoidable heap allocation.
     ///
     /// ### Known problems
diff --git a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
index 7729c556e1f..2cbc4b07234 100644
--- a/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/misnamed_getters.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
-use rustc_hir::{Body, ExprKind, FnDecl, Safety, ImplicitSelfKind};
+use rustc_hir::{Body, ExprKind, FnDecl, ImplicitSelfKind, Safety};
 use rustc_lint::LateContext;
 use rustc_middle::ty;
 use rustc_span::Span;
diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs
index dfcaac9abef..26534492ddd 100644
--- a/src/tools/clippy/clippy_lints/src/functions/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs
@@ -338,8 +338,10 @@ declare_clippy_lint! {
 declare_clippy_lint! {
     /// ### What it does
     /// Lints when `impl Trait` is being used in a function's parameters.
-    /// ### Why is this bad?
-    /// Turbofish syntax (`::<>`) cannot be used when `impl Trait` is being used, making `impl Trait` less powerful. Readability may also be a factor.
+    ///
+    /// ### Why restrict this?
+    /// Turbofish syntax (`::<>`) cannot be used to specify the type of an `impl Trait` parameter,
+    /// making `impl Trait` less powerful. Readability may also be a factor.
     ///
     /// ### Example
     /// ```no_run
@@ -366,9 +368,8 @@ declare_clippy_lint! {
     /// Lints when the name of function parameters from trait impl is
     /// different than its default implementation.
     ///
-    /// ### Why is this bad?
-    /// Using the default name for parameters of a trait method is often
-    /// more desirable for consistency's sake.
+    /// ### Why restrict this?
+    /// Using the default name for parameters of a trait method is more consistent.
     ///
     /// ### Example
     /// ```rust
diff --git a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
index f5ba62ae432..0b200815219 100644
--- a/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
+++ b/src/tools/clippy/clippy_lints/src/if_then_some_else_none.rs
@@ -15,7 +15,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for if-else that could be written using either `bool::then` or `bool::then_some`.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Looks a little redundant. Using `bool::then` is more concise and incurs no loss of clarity.
     /// For simple calculations and known values, use `bool::then_some`, which is eagerly evaluated
     /// in comparison to `bool::then`.
diff --git a/src/tools/clippy/clippy_lints/src/implicit_return.rs b/src/tools/clippy/clippy_lints/src/implicit_return.rs
index 5288efd8df8..cc342007ec6 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_return.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_return.rs
@@ -16,12 +16,13 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for missing return statements at the end of a block.
     ///
-    /// ### Why is this bad?
-    /// Actually omitting the return keyword is idiomatic Rust code. Programmers
-    /// coming from other languages might prefer the expressiveness of `return`. It's possible to miss
-    /// the last returning statement because the only difference is a missing `;`. Especially in bigger
-    /// code with multiple return paths having a `return` keyword makes it easier to find the
-    /// corresponding statements.
+    /// ### Why restrict this?
+    /// Omitting the return keyword whenever possible is idiomatic Rust code, but:
+    ///
+    /// * Programmers coming from other languages might prefer the expressiveness of `return`.
+    /// * It's possible to miss the last returning statement because the only difference is a missing `;`.
+    /// * Especially in bigger code with multiple return paths, having a `return` keyword makes it easier to find the
+    ///   corresponding statements.
     ///
     /// ### Example
     /// ```no_run
diff --git a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
index 35fcd8cdd35..e3e79749bea 100644
--- a/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
+++ b/src/tools/clippy/clippy_lints/src/indexing_slicing.rs
@@ -45,9 +45,10 @@ declare_clippy_lint! {
     /// does report on arrays if we can tell that slicing operations are in bounds and does not
     /// lint on constant `usize` indexing on arrays because that is handled by rustc's `const_err` lint.
     ///
-    /// ### Why is this bad?
-    /// Indexing and slicing can panic at runtime and there are
-    /// safe alternatives.
+    /// ### Why restrict this?
+    /// To avoid implicit panics from indexing and slicing.
+    /// There are “checked” alternatives which do not panic, and can be used with `unwrap()` to make
+    /// an explicit panic when it is desired.
     ///
     /// ### Example
     /// ```rust,no_run
diff --git a/src/tools/clippy/clippy_lints/src/inherent_impl.rs b/src/tools/clippy/clippy_lints/src/inherent_impl.rs
index 1127f00abde..95ae591884b 100644
--- a/src/tools/clippy/clippy_lints/src/inherent_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/inherent_impl.rs
@@ -14,7 +14,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for multiple inherent implementations of a struct
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Splitting the implementation of a type makes the code harder to navigate.
     ///
     /// ### Example
diff --git a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
index e486563808a..1c8fd0a27f9 100644
--- a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
+++ b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Expr, ExprKind};
@@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for NumberedFields {
                     snippet_with_applicability(cx, path.span(), "..", &mut appl),
                     expr_spans
                         .into_iter_sorted()
-                        .map(|(_, span)| snippet_with_applicability(cx, span, "..", &mut appl))
+                        .map(|(_, span)| snippet_with_context(cx, span, path.span().ctxt(), "..", &mut appl).0)
                         .intersperse(Cow::Borrowed(", "))
                         .collect::<String>()
                 );
diff --git a/src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs b/src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs
index a3577b765c0..cf598d5045e 100644
--- a/src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs
+++ b/src/tools/clippy/clippy_lints/src/integer_division_remainder_used.rs
@@ -7,10 +7,10 @@ use rustc_session::declare_lint_pass;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for the usage of division (/) and remainder (%) operations
-    /// when performed on any integer types using the default Div and Rem trait implementations.
+    /// Checks for the usage of division (`/`) and remainder (`%`) operations
+    /// when performed on any integer types using the default `Div` and `Rem` trait implementations.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// In cryptographic contexts, division can result in timing sidechannel vulnerabilities,
     /// and needs to be replaced with constant-time code instead (e.g. Barrett reduction).
     ///
diff --git a/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs
index 6c6eff9ba48..fb29d982417 100644
--- a/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs
+++ b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs
@@ -14,8 +14,8 @@ declare_clippy_lint! {
     /// ### What it does
     /// This is a restriction lint which prevents the use of hash types (i.e., `HashSet` and `HashMap`) in for loops.
     ///
-    /// ### Why is this bad?
-    /// Because hash types are unordered, when iterated through such as in a for loop, the values are returned in
+    /// ### Why restrict this?
+    /// Because hash types are unordered, when iterated through such as in a `for` loop, the values are returned in
     /// an undefined order. As a result, on redundant systems this may cause inconsistencies and anomalies.
     /// In addition, the unknown order of the elements may reduce readability or introduce other undesired
     /// side effects.
diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs
index 790bed580fd..07efee159aa 100644
--- a/src/tools/clippy/clippy_lints/src/large_include_file.rs
+++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs
@@ -10,10 +10,12 @@ use rustc_span::sym;
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for the inclusion of large files via `include_bytes!()`
-    /// and `include_str!()`
+    /// or `include_str!()`.
     ///
-    /// ### Why is this bad?
-    /// Including large files can increase the size of the binary
+    /// ### Why restrict this?
+    /// Including large files can undesirably increase the size of the binary produced by the compiler.
+    /// This lint may be used to catch mistakes where an unexpectedly large file is included, or
+    /// temporarily to obtain a list of all large files.
     ///
     /// ### Example
     /// ```rust,ignore
diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs
index 619e933b4ff..9fd4f509aa4 100644
--- a/src/tools/clippy/clippy_lints/src/let_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs
@@ -12,9 +12,8 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for `let _ = <expr>` where expr is `#[must_use]`
     ///
-    /// ### Why is this bad?
-    /// It's better to explicitly handle the value of a `#[must_use]`
-    /// expr
+    /// ### Why restrict this?
+    /// To ensure that all `#[must_use]` types are used rather than ignored.
     ///
     /// ### Example
     /// ```no_run
@@ -96,8 +95,8 @@ declare_clippy_lint! {
     /// Checks for `let _ = <expr>` without a type annotation, and suggests to either provide one,
     /// or remove the `let` keyword altogether.
     ///
-    /// ### Why is this bad?
-    /// The `let _ = <expr>` expression ignores the value of `<expr>` but will remain doing so even
+    /// ### Why restrict this?
+    /// The `let _ = <expr>` expression ignores the value of `<expr>`, but will continue to do so even
     /// if the type were to change, thus potentially introducing subtle bugs. By supplying a type
     /// annotation, one will be forced to re-visit the decision to ignore the value in such cases.
     ///
diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs
index 2348dd18220..d2a140a36a8 100644
--- a/src/tools/clippy/clippy_lints/src/literal_representation.rs
+++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs
@@ -132,8 +132,8 @@ declare_clippy_lint! {
     /// ### What it does
     /// Warns if there is a better representation for a numeric literal.
     ///
-    /// ### Why is this bad?
-    /// Especially for big powers of 2 a hexadecimal representation is more
+    /// ### Why restrict this?
+    /// Especially for big powers of 2, a hexadecimal representation is usually more
     /// readable than a decimal representation.
     ///
     /// ### Example
diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs
index 3dcb050d77e..08682942153 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs
@@ -675,9 +675,9 @@ declare_clippy_lint! {
     /// Checks for infinite loops in a function where the return type is not `!`
     /// and lint accordingly.
     ///
-    /// ### Why is this bad?
-    /// A loop should be gently exited somewhere, or at least mark its parent function as
-    /// never return (`!`).
+    /// ### Why restrict this?
+    /// Making the return type `!` serves as documentation that the function does not return.
+    /// If the function is not intended to loop infinitely, then this lint may detect a bug.
     ///
     /// ### Example
     /// ```no_run,ignore
diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs
index ee9f48d71ad..691ecd57535 100644
--- a/src/tools/clippy/clippy_lints/src/matches/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs
@@ -260,7 +260,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for wildcard enum matches using `_`.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// New enum variants added by library updates can be missed.
     ///
     /// ### Known problems
@@ -435,7 +435,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for unnecessary '..' pattern binding on struct when all fields are explicitly matched.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Correctness and readability. It's like having a wildcard pattern after
     /// matching all enum variants explicitly.
     ///
@@ -861,7 +861,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for usage of `Err(x)?`.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// The `?` operator is designed to allow calls that
     /// can fail to be easily chained. For example, `foo()?.bar()` or
     /// `foo(bar()?)`. Because `Err(x)?` can't be used that way (it will
diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
index 6ac0705abb2..2f72e59834f 100644
--- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
@@ -1,12 +1,17 @@
+use std::ops::ControlFlow;
+
 use crate::FxHashSet;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::{indent_of, snippet};
+use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy};
 use clippy_utils::{get_attr, is_lint_allowed};
+use itertools::Itertools;
+use rustc_ast::Mutability;
 use rustc_errors::{Applicability, Diag};
 use rustc_hir::intravisit::{walk_expr, Visitor};
 use rustc_hir::{Arm, Expr, ExprKind, MatchSource};
 use rustc_lint::{LateContext, LintContext};
-use rustc_middle::ty::{GenericArgKind, Ty};
+use rustc_middle::ty::{GenericArgKind, Region, RegionKind, Ty, TyCtxt, TypeVisitable, TypeVisitor};
 use rustc_span::Span;
 
 use super::SIGNIFICANT_DROP_IN_SCRUTINEE;
@@ -22,43 +27,34 @@ pub(super) fn check<'tcx>(
         return;
     }
 
-    if let Some((suggestions, message)) = has_significant_drop_in_scrutinee(cx, scrutinee, source) {
-        for found in suggestions {
-            span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| {
-                set_diagnostic(diag, cx, expr, found);
-                let s = Span::new(expr.span.hi(), expr.span.hi(), expr.span.ctxt(), None);
-                diag.span_label(s, "temporary lives until here");
-                for span in has_significant_drop_in_arms(cx, arms) {
-                    diag.span_label(span, "another value with significant `Drop` created here");
-                }
-                diag.note("this might lead to deadlocks or other unexpected behavior");
-            });
-        }
+    let (suggestions, message) = has_significant_drop_in_scrutinee(cx, scrutinee, source);
+    for found in suggestions {
+        span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| {
+            set_diagnostic(diag, cx, expr, found);
+            let s = Span::new(expr.span.hi(), expr.span.hi(), expr.span.ctxt(), None);
+            diag.span_label(s, "temporary lives until here");
+            for span in has_significant_drop_in_arms(cx, arms) {
+                diag.span_label(span, "another value with significant `Drop` created here");
+            }
+            diag.note("this might lead to deadlocks or other unexpected behavior");
+        });
     }
 }
 
 fn set_diagnostic<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, found: FoundSigDrop) {
-    if found.lint_suggestion == LintSuggestion::MoveAndClone {
-        // If our suggestion is to move and clone, then we want to leave it to the user to
-        // decide how to address this lint, since it may be that cloning is inappropriate.
-        // Therefore, we won't to emit a suggestion.
-        return;
-    }
-
     let original = snippet(cx, found.found_span, "..");
     let trailing_indent = " ".repeat(indent_of(cx, found.found_span).unwrap_or(0));
 
-    let replacement = if found.lint_suggestion == LintSuggestion::MoveAndDerefToCopy {
-        format!("let value = *{original};\n{trailing_indent}")
-    } else if found.is_unit_return_val {
-        // If the return value of the expression to be moved is unit, then we don't need to
-        // capture the result in a temporary -- we can just replace it completely with `()`.
-        format!("{original};\n{trailing_indent}")
-    } else {
-        format!("let value = {original};\n{trailing_indent}")
+    let replacement = {
+        let (def_part, deref_part) = if found.is_unit_return_val {
+            ("", String::new())
+        } else {
+            ("let value = ", "*".repeat(found.peel_ref_times))
+        };
+        format!("{def_part}{deref_part}{original};\n{trailing_indent}")
     };
 
-    let suggestion_message = if found.lint_suggestion == LintSuggestion::MoveOnly {
+    let suggestion_message = if found.peel_ref_times == 0 {
         "try moving the temporary above the match"
     } else {
         "try moving the temporary above the match and create a copy"
@@ -66,8 +62,11 @@ fn set_diagnostic<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: &
 
     let scrutinee_replacement = if found.is_unit_return_val {
         "()".to_owned()
-    } else {
+    } else if found.peel_ref_times == 0 {
         "value".to_owned()
+    } else {
+        let ref_part = "&".repeat(found.peel_ref_times);
+        format!("({ref_part}value)")
     };
 
     diag.multipart_suggestion(
@@ -86,20 +85,18 @@ fn has_significant_drop_in_scrutinee<'tcx>(
     cx: &LateContext<'tcx>,
     scrutinee: &'tcx Expr<'tcx>,
     source: MatchSource,
-) -> Option<(Vec<FoundSigDrop>, &'static str)> {
+) -> (Vec<FoundSigDrop>, &'static str) {
     let mut helper = SigDropHelper::new(cx);
     let scrutinee = match (source, &scrutinee.kind) {
         (MatchSource::ForLoopDesugar, ExprKind::Call(_, [e])) => e,
         _ => scrutinee,
     };
-    helper.find_sig_drop(scrutinee).map(|drops| {
-        let message = if source == MatchSource::Normal {
-            "temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression"
-        } else {
-            "temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression"
-        };
-        (drops, message)
-    })
+    let message = if source == MatchSource::Normal {
+        "temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression"
+    } else {
+        "temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression"
+    };
+    (helper.find_sig_drop(scrutinee), message)
 }
 
 struct SigDropChecker<'a, 'tcx> {
@@ -172,205 +169,248 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
     }
 }
 
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
+enum SigDropHolder {
+    /// No values with significant drop present in this expression.
+    ///
+    /// Expressions that we've emited lints do not count.
+    None,
+    /// Some field in this expression references to values with significant drop.
+    ///
+    /// Example: `(1, &data.lock().field)`.
+    PackedRef,
+    /// The value of this expression references to values with significant drop.
+    ///
+    /// Example: `data.lock().field`.
+    DirectRef,
+    /// This expression should be moved out to avoid significant drop in scrutinee.
+    Moved,
+}
+
+impl Default for SigDropHolder {
+    fn default() -> Self {
+        Self::None
+    }
+}
+
 struct SigDropHelper<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
-    is_chain_end: bool,
-    has_significant_drop: bool,
-    current_sig_drop: Option<FoundSigDrop>,
-    sig_drop_spans: Option<Vec<FoundSigDrop>>,
-    special_handling_for_binary_op: bool,
+    parent_expr: Option<&'tcx Expr<'tcx>>,
+    sig_drop_holder: SigDropHolder,
+    sig_drop_spans: Vec<FoundSigDrop>,
     sig_drop_checker: SigDropChecker<'a, 'tcx>,
 }
 
-#[expect(clippy::enum_variant_names)]
-#[derive(Debug, PartialEq, Eq, Clone, Copy)]
-enum LintSuggestion {
-    MoveOnly,
-    MoveAndDerefToCopy,
-    MoveAndClone,
-}
-
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Debug)]
 struct FoundSigDrop {
     found_span: Span,
     is_unit_return_val: bool,
-    lint_suggestion: LintSuggestion,
+    peel_ref_times: usize,
 }
 
 impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
     fn new(cx: &'a LateContext<'tcx>) -> SigDropHelper<'a, 'tcx> {
         SigDropHelper {
             cx,
-            is_chain_end: true,
-            has_significant_drop: false,
-            current_sig_drop: None,
-            sig_drop_spans: None,
-            special_handling_for_binary_op: false,
+            parent_expr: None,
+            sig_drop_holder: SigDropHolder::None,
+            sig_drop_spans: Vec::new(),
             sig_drop_checker: SigDropChecker::new(cx),
         }
     }
 
-    fn find_sig_drop(&mut self, match_expr: &'tcx Expr<'_>) -> Option<Vec<FoundSigDrop>> {
+    fn find_sig_drop(&mut self, match_expr: &'tcx Expr<'_>) -> Vec<FoundSigDrop> {
         self.visit_expr(match_expr);
 
-        // If sig drop spans is empty but we found a significant drop, it means that we didn't find
-        // a type that was trivially copyable as we moved up the chain after finding a significant
-        // drop, so move the entire scrutinee.
-        if self.has_significant_drop && self.sig_drop_spans.is_none() {
-            self.try_setting_current_suggestion(match_expr, true);
-            self.move_current_suggestion();
-        }
-
-        self.sig_drop_spans.take()
+        core::mem::take(&mut self.sig_drop_spans)
     }
 
-    fn replace_current_sig_drop(
-        &mut self,
-        found_span: Span,
-        is_unit_return_val: bool,
-        lint_suggestion: LintSuggestion,
-    ) {
-        self.current_sig_drop.replace(FoundSigDrop {
+    fn replace_current_sig_drop(&mut self, found_span: Span, is_unit_return_val: bool, peel_ref_times: usize) {
+        self.sig_drop_spans.clear();
+        self.sig_drop_spans.push(FoundSigDrop {
             found_span,
             is_unit_return_val,
-            lint_suggestion,
+            peel_ref_times,
         });
     }
 
-    /// This will try to set the current suggestion (so it can be moved into the suggestions vec
-    /// later). If `allow_move_and_clone` is false, the suggestion *won't* be set -- this gives us
-    /// an opportunity to look for another type in the chain that will be trivially copyable.
-    /// However, if we are at the end of the chain, we want to accept whatever is there. (The
-    /// suggestion won't actually be output, but the diagnostic message will be output, so the user
-    /// can determine the best way to handle the lint.)
-    fn try_setting_current_suggestion(&mut self, expr: &'tcx Expr<'_>, allow_move_and_clone: bool) {
-        if self.current_sig_drop.is_some() {
-            return;
+    fn try_move_sig_drop(&mut self, expr: &'tcx Expr<'_>, parent_expr: &'tcx Expr<'_>) {
+        if self.sig_drop_holder == SigDropHolder::Moved {
+            self.sig_drop_holder = SigDropHolder::None;
         }
-        let ty = self.cx.typeck_results().expr_ty(expr);
-        if ty.is_ref() {
-            // We checked that the type was ref, so builtin_deref will return Some,
-            // but let's avoid any chance of an ICE.
-            if let Some(ty) = ty.builtin_deref(true) {
-                if ty.is_trivially_pure_clone_copy() {
-                    self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndDerefToCopy);
-                } else if allow_move_and_clone {
-                    self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone);
-                }
+
+        if self.sig_drop_holder == SigDropHolder::DirectRef {
+            self.sig_drop_holder = SigDropHolder::PackedRef;
+            self.try_move_sig_drop_direct_ref(expr, parent_expr);
+        } else if self.sig_drop_checker.is_sig_drop_expr(expr) {
+            // The values with significant drop can be moved to some other functions. For example, consider
+            // `drop(data.lock())`. We use `SigDropHolder::None` here to avoid emitting lints in such scenarios.
+            self.sig_drop_holder = SigDropHolder::None;
+            self.try_move_sig_drop_direct_ref(expr, parent_expr);
+        }
+
+        if self.sig_drop_holder != SigDropHolder::None {
+            let parent_ty = self.cx.typeck_results().expr_ty(parent_expr);
+            if !ty_has_erased_regions(parent_ty) && !parent_expr.is_syntactic_place_expr() {
+                self.replace_current_sig_drop(parent_expr.span, parent_ty.is_unit(), 0);
+                self.sig_drop_holder = SigDropHolder::Moved;
+            }
+
+            let (peel_ref_ty, peel_ref_times) = ty_peel_refs(parent_ty);
+            if !ty_has_erased_regions(peel_ref_ty) && is_copy(self.cx, peel_ref_ty) {
+                self.replace_current_sig_drop(parent_expr.span, peel_ref_ty.is_unit(), peel_ref_times);
+                self.sig_drop_holder = SigDropHolder::Moved;
             }
-        } else if ty.is_trivially_pure_clone_copy() {
-            self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveOnly);
-        } else if allow_move_and_clone {
-            self.replace_current_sig_drop(expr.span, false, LintSuggestion::MoveAndClone);
         }
     }
 
-    fn move_current_suggestion(&mut self) {
-        if let Some(current) = self.current_sig_drop.take() {
-            self.sig_drop_spans.get_or_insert_with(Vec::new).push(current);
+    fn try_move_sig_drop_direct_ref(&mut self, expr: &'tcx Expr<'_>, parent_expr: &'tcx Expr<'_>) {
+        let arg_idx = match parent_expr.kind {
+            ExprKind::MethodCall(_, receiver, exprs, _) => std::iter::once(receiver)
+                .chain(exprs.iter())
+                .find_position(|ex| ex.hir_id == expr.hir_id)
+                .map(|(idx, _)| idx),
+            ExprKind::Call(_, exprs) => exprs
+                .iter()
+                .find_position(|ex| ex.hir_id == expr.hir_id)
+                .map(|(idx, _)| idx),
+            ExprKind::Binary(_, lhs, rhs) | ExprKind::AssignOp(_, lhs, rhs) => [lhs, rhs]
+                .iter()
+                .find_position(|ex| ex.hir_id == expr.hir_id)
+                .map(|(idx, _)| idx),
+            ExprKind::Unary(_, ex) => (ex.hir_id == expr.hir_id).then_some(0),
+            _ => {
+                // Here we assume that all other expressions create or propagate the reference to the value with
+                // significant drop.
+                self.sig_drop_holder = SigDropHolder::DirectRef;
+                return;
+            },
+        };
+        let Some(arg_idx) = arg_idx else {
+            return;
+        };
+
+        let fn_sig = if let Some(def_id) = self.cx.typeck_results().type_dependent_def_id(parent_expr.hir_id) {
+            self.cx.tcx.fn_sig(def_id).instantiate_identity()
+        } else {
+            return;
+        };
+
+        let input_re = if let Some(input_ty) = fn_sig.skip_binder().inputs().get(arg_idx)
+            && let rustc_middle::ty::Ref(input_re, _, _) = input_ty.kind()
+        {
+            input_re
+        } else {
+            return;
+        };
+
+        // Late bound lifetime parameters are not related to any constraints, so we can track them in a very
+        // simple manner. For other lifetime parameters, we give up and update the state to `PackedRef`.
+        let RegionKind::ReBound(_, input_re_bound) = input_re.kind() else {
+            self.sig_drop_holder = SigDropHolder::PackedRef;
+            return;
+        };
+        let contains_input_re = |re_bound| {
+            if re_bound == input_re_bound {
+                ControlFlow::Break(())
+            } else {
+                ControlFlow::Continue(())
+            }
+        };
+
+        let output_ty = fn_sig.skip_binder().output();
+        if let rustc_middle::ty::Ref(output_re, peel_ref_ty, _) = output_ty.kind()
+            && input_re == output_re
+            && for_each_top_level_late_bound_region(*peel_ref_ty, contains_input_re).is_continue()
+        {
+            // We're lucky! The output type is still a direct reference to the value with significant drop.
+            self.sig_drop_holder = SigDropHolder::DirectRef;
+        } else if for_each_top_level_late_bound_region(output_ty, contains_input_re).is_continue() {
+            // The lifetime to the value with significant drop goes away. So we can emit a lint that suggests to
+            // move the expression out.
+            self.replace_current_sig_drop(parent_expr.span, output_ty.is_unit(), 0);
+            self.sig_drop_holder = SigDropHolder::Moved;
+        } else {
+            // TODO: The lifetime is still there but it's for a inner type. For instance, consider
+            // `Some(&mutex.lock().field)`, which has a type of `Option<&u32>`. How to address this scenario?
+            self.sig_drop_holder = SigDropHolder::PackedRef;
         }
     }
+}
 
-    fn visit_exprs_for_binary_ops(
-        &mut self,
-        left: &'tcx Expr<'_>,
-        right: &'tcx Expr<'_>,
-        is_unit_return_val: bool,
-        span: Span,
-    ) {
-        self.special_handling_for_binary_op = true;
-        self.visit_expr(left);
-        self.visit_expr(right);
-
-        // If either side had a significant drop, suggest moving the entire scrutinee to avoid
-        // unnecessary copies and to simplify cases where both sides have significant drops.
-        if self.has_significant_drop {
-            self.replace_current_sig_drop(span, is_unit_return_val, LintSuggestion::MoveOnly);
-        }
+fn ty_peel_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
+    let mut n = 0;
+    while let rustc_middle::ty::Ref(_, new_ty, Mutability::Not) = ty.kind() {
+        ty = *new_ty;
+        n += 1;
+    }
+    (ty, n)
+}
+
+fn ty_has_erased_regions(ty: Ty<'_>) -> bool {
+    struct V;
 
-        self.special_handling_for_binary_op = false;
+    impl<'tcx> TypeVisitor<TyCtxt<'tcx>> for V {
+        type Result = ControlFlow<()>;
+
+        fn visit_region(&mut self, region: Region<'tcx>) -> Self::Result {
+            if region.is_erased() {
+                ControlFlow::Break(())
+            } else {
+                ControlFlow::Continue(())
+            }
+        }
     }
+
+    ty.visit_with(&mut V).is_break()
 }
 
 impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
     fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
-        if !self.is_chain_end && self.sig_drop_checker.is_sig_drop_expr(ex) {
-            self.has_significant_drop = true;
+        // We've emited a lint on some neighborhood expression. That lint will suggest to move out the
+        // _parent_ expression (not the expression itself). Since we decide to move out the parent
+        // expression, it is pointless to continue to process the current expression.
+        if self.sig_drop_holder == SigDropHolder::Moved {
             return;
         }
-        self.is_chain_end = false;
+
+        // These states are of neighborhood expressions. We save and clear them here, and we'll later merge
+        // the states of the current expression with them at the end of the method.
+        let sig_drop_holder_before = core::mem::take(&mut self.sig_drop_holder);
+        let sig_drop_spans_before = core::mem::take(&mut self.sig_drop_spans);
+        let parent_expr_before = self.parent_expr.replace(ex);
 
         match ex.kind {
-            ExprKind::MethodCall(_, expr, ..) => {
-                self.visit_expr(expr);
-            }
-            ExprKind::Binary(_, left, right) => {
-                self.visit_exprs_for_binary_ops(left, right, false, ex.span);
-            }
-            ExprKind::Assign(left, right, _) | ExprKind::AssignOp(_, left, right) => {
-                self.visit_exprs_for_binary_ops(left, right, true, ex.span);
-            }
-            ExprKind::Tup(exprs) => {
-                for expr in exprs {
-                    self.visit_expr(expr);
-                    if self.has_significant_drop {
-                        // We may have not have set current_sig_drop if all the suggestions were
-                        // MoveAndClone, so add this tuple item's full expression in that case.
-                        if self.current_sig_drop.is_none() {
-                            self.try_setting_current_suggestion(expr, true);
-                        }
-
-                        // Now we are guaranteed to have something, so add it to the final vec.
-                        self.move_current_suggestion();
-                    }
-                    // Reset `has_significant_drop` after each tuple expression so we can look for
-                    // additional cases.
-                    self.has_significant_drop = false;
-                }
-                if self.sig_drop_spans.is_some() {
-                    self.has_significant_drop = true;
-                }
-            }
-            ExprKind::Array(..) |
-            ExprKind::Call(..) |
-            ExprKind::Unary(..) |
-            ExprKind::If(..) |
-            ExprKind::Match(..) |
-            ExprKind::Field(..) |
-            ExprKind::Index(..) |
-            ExprKind::Ret(..) |
-            ExprKind::Become(..) |
-            ExprKind::Repeat(..) |
-            ExprKind::Yield(..) => walk_expr(self, ex),
-            ExprKind::AddrOf(_, _, _) |
-            ExprKind::Block(_, _) |
-            ExprKind::Break(_, _) |
-            ExprKind::Cast(_, _) |
-            // Don't want to check the closure itself, only invocation, which is covered by MethodCall
-            ExprKind::Closure { .. } |
-            ExprKind::ConstBlock(_) |
-            ExprKind::Continue(_) |
-            ExprKind::DropTemps(_) |
-            ExprKind::Err(_) |
-            ExprKind::InlineAsm(_) |
-            ExprKind::OffsetOf(_, _) |
-            ExprKind::Let(_) |
-            ExprKind::Lit(_) |
-            ExprKind::Loop(_, _, _, _) |
-            ExprKind::Path(_) |
-            ExprKind::Struct(_, _, _) |
-            ExprKind::Type(_, _) => {
-                return;
+            // Skip blocks because values in blocks will be dropped as usual.
+            ExprKind::Block(..) => (),
+            _ => walk_expr(self, ex),
+        }
+
+        if let Some(parent_ex) = parent_expr_before {
+            match parent_ex.kind {
+                ExprKind::Assign(lhs, _, _) | ExprKind::AssignOp(_, lhs, _)
+                    if lhs.hir_id == ex.hir_id && self.sig_drop_holder == SigDropHolder::Moved =>
+                {
+                    // Never move out only the assignee. Instead, we should always move out the whole assigment.
+                    self.replace_current_sig_drop(parent_ex.span, true, 0);
+                },
+                _ => {
+                    self.try_move_sig_drop(ex, parent_ex);
+                },
             }
         }
 
-        // Once a significant temporary has been found, we need to go back up at least 1 level to
-        // find the span to extract for replacement, so the temporary gets dropped. However, for
-        // binary ops, we want to move the whole scrutinee so we avoid unnecessary copies and to
-        // simplify cases where both sides have significant drops.
-        if self.has_significant_drop && !self.special_handling_for_binary_op {
-            self.try_setting_current_suggestion(ex, false);
+        self.sig_drop_holder = std::cmp::max(self.sig_drop_holder, sig_drop_holder_before);
+
+        // We do not need those old spans in neighborhood expressions if we emit a lint that suggests to
+        // move out the _parent_ expression (i.e., `self.sig_drop_holder == SigDropHolder::Moved`).
+        if self.sig_drop_holder != SigDropHolder::Moved {
+            let mut sig_drop_spans = sig_drop_spans_before;
+            sig_drop_spans.append(&mut self.sig_drop_spans);
+            self.sig_drop_spans = sig_drop_spans;
         }
+
+        self.parent_expr = parent_expr_before;
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
index 6c9bdcff826..f4397212cf6 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_on_single_or_empty_collections.rs
@@ -1,8 +1,12 @@
+use std::iter::once;
+
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
 use clippy_utils::{get_expr_use_or_unification_node, is_res_lang_ctor, path_res, std_or_core};
 
 use rustc_errors::Applicability;
+use rustc_hir::def_id::DefId;
+use rustc_hir::hir_id::HirId;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_hir::{Expr, ExprKind, Node};
 use rustc_lint::LateContext;
@@ -25,7 +29,29 @@ impl IterType {
     }
 }
 
-pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, recv: &Expr<'_>) {
+fn is_arg_ty_unified_in_fn<'tcx>(
+    cx: &LateContext<'tcx>,
+    fn_id: DefId,
+    arg_id: HirId,
+    args: impl IntoIterator<Item = &'tcx Expr<'tcx>>,
+) -> bool {
+    let fn_sig = cx.tcx.fn_sig(fn_id).instantiate_identity();
+    let arg_id_in_args = args.into_iter().position(|e| e.hir_id == arg_id).unwrap();
+    let arg_ty_in_args = fn_sig.input(arg_id_in_args).skip_binder();
+
+    cx.tcx.predicates_of(fn_id).predicates.iter().any(|(clause, _)| {
+        clause
+            .as_projection_clause()
+            .and_then(|p| p.map_bound(|p| p.term.ty()).transpose())
+            .is_some_and(|ty| ty.skip_binder() == arg_ty_in_args)
+    }) || fn_sig
+        .inputs()
+        .iter()
+        .enumerate()
+        .any(|(i, ty)| i != arg_id_in_args && ty.skip_binder().walk().any(|arg| arg.as_type() == Some(arg_ty_in_args)))
+}
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, method_name: &str, recv: &'tcx Expr<'tcx>) {
     let item = match recv.kind {
         ExprKind::Array([]) => None,
         ExprKind::Array([e]) => Some(e),
@@ -43,6 +69,25 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: &str, re
     let is_unified = match get_expr_use_or_unification_node(cx.tcx, expr) {
         Some((Node::Expr(parent), child_id)) => match parent.kind {
             ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id == child_id => false,
+            ExprKind::Call(
+                Expr {
+                    kind: ExprKind::Path(path),
+                    hir_id,
+                    ..
+                },
+                args,
+            ) => cx
+                .typeck_results()
+                .qpath_res(path, *hir_id)
+                .opt_def_id()
+                .filter(|fn_id| cx.tcx.def_kind(fn_id).is_fn_like())
+                .is_some_and(|fn_id| is_arg_ty_unified_in_fn(cx, fn_id, child_id, args)),
+            ExprKind::MethodCall(_name, recv, args, _span) => is_arg_ty_unified_in_fn(
+                cx,
+                cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap(),
+                child_id,
+                once(recv).chain(args.iter()),
+            ),
             ExprKind::If(_, _, _)
             | ExprKind::Match(_, _, _)
             | ExprKind::Closure(_)
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 9d67aa23379..75a86c0c834 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -257,7 +257,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for `.unwrap()` or `.unwrap_err()` calls on `Result`s and `.unwrap()` call on `Option`s.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// It is better to handle the `None` or `Err` case,
     /// or at least call `.expect(_)` with a more helpful message. Still, for a lot of
     /// quick-and-dirty code, `unwrap` is a good choice, which is why this lint is
@@ -333,7 +333,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for `.expect()` or `.expect_err()` calls on `Result`s and `.expect()` call on `Option`s.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Usually it is better to handle the `None` or `Err` case.
     /// Still, for a lot of quick-and-dirty code, `expect` is a good choice, which is why
     /// this lint is `Allow` by default.
@@ -1029,8 +1029,8 @@ declare_clippy_lint! {
     /// (`Rc`, `Arc`, `rc::Weak`, or `sync::Weak`), and suggests calling Clone via unified
     /// function syntax instead (e.g., `Rc::clone(foo)`).
     ///
-    /// ### Why is this bad?
-    /// Calling '.clone()' on an Rc, Arc, or Weak
+    /// ### Why restrict this?
+    /// Calling `.clone()` on an `Rc`, `Arc`, or `Weak`
     /// can obscure the fact that only the pointer is being cloned, not the underlying
     /// data.
     ///
@@ -1051,7 +1051,7 @@ declare_clippy_lint! {
     #[clippy::version = "pre 1.29.0"]
     pub CLONE_ON_REF_PTR,
     restriction,
-    "using 'clone' on a ref-counted pointer"
+    "using `clone` on a ref-counted pointer"
 }
 
 declare_clippy_lint! {
@@ -1359,7 +1359,7 @@ declare_clippy_lint! {
     /// Checks for usage of `.get().unwrap()` (or
     /// `.get_mut().unwrap`) on a standard library type which implements `Index`
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Using the Index trait (`[]`) is more clear and more
     /// concise.
     ///
@@ -1743,7 +1743,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for `FileType::is_file()`.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// When people testing a file type with `FileType::is_file`
     /// they are testing whether a path is something they can get bytes from. But
     /// `is_file` doesn't cover special file types in unix-like systems, and doesn't cover
@@ -2688,8 +2688,9 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for instances of `map_err(|_| Some::Enum)`
     ///
-    /// ### Why is this bad?
-    /// This `map_err` throws away the original error rather than allowing the enum to contain and report the cause of the error
+    /// ### Why restrict this?
+    /// This `map_err` throws away the original error rather than allowing the enum to
+    /// contain and report the cause of the error.
     ///
     /// ### Example
     /// Before:
@@ -3145,7 +3146,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for usage of File::read_to_end and File::read_to_string.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// `fs::{read, read_to_string}` provide the same functionality when `buf` is empty with fewer imports and no intermediate values.
     /// See also: [fs::read docs](https://doc.rust-lang.org/std/fs/fn.read.html), [fs::read_to_string docs](https://doc.rust-lang.org/std/fs/fn.read_to_string.html)
     ///
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs
index 7431dc1cf0b..d1300dd43c2 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs
@@ -38,7 +38,7 @@ pub fn check_for_loop_iter(
 ) -> bool {
     if let Some(grandparent) = get_parent_expr(cx, expr).and_then(|parent| get_parent_expr(cx, parent))
         && let Some(ForLoop { pat, body, .. }) = ForLoop::hir(grandparent)
-        && let (clone_or_copy_needed, addr_of_exprs) = clone_or_copy_needed(cx, pat, body)
+        && 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)
     {
@@ -123,14 +123,12 @@ pub fn check_for_loop_iter(
                     Applicability::MachineApplicable
                 };
                 diag.span_suggestion(expr.span, "use", snippet, applicability);
-                for addr_of_expr in addr_of_exprs {
-                    match addr_of_expr.kind {
-                        ExprKind::AddrOf(_, _, referent) => {
-                            let span = addr_of_expr.span.with_hi(referent.span.lo());
-                            diag.span_suggestion(span, "remove this `&`", "", applicability);
-                        },
-                        _ => unreachable!(),
-                    }
+                if !references_to_binding.is_empty() {
+                    diag.multipart_suggestion(
+                        "remove any references to the binding",
+                        references_to_binding,
+                        applicability,
+                    );
                 }
             },
         );
diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs
index 34d7b9acbe4..1a55b7160fb 100644
--- a/src/tools/clippy/clippy_lints/src/methods/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs
@@ -9,6 +9,7 @@ use rustc_lint::LateContext;
 use rustc_middle::hir::nested_filter;
 use rustc_middle::ty::{self, Ty};
 use rustc_span::symbol::sym;
+use rustc_span::Span;
 
 pub(super) fn derefs_to_slice<'tcx>(
     cx: &LateContext<'tcx>,
@@ -96,15 +97,15 @@ pub(super) fn clone_or_copy_needed<'tcx>(
     cx: &LateContext<'tcx>,
     pat: &Pat<'tcx>,
     body: &'tcx Expr<'tcx>,
-) -> (bool, Vec<&'tcx Expr<'tcx>>) {
+) -> (bool, Vec<(Span, String)>) {
     let mut visitor = CloneOrCopyVisitor {
         cx,
         binding_hir_ids: pat_bindings(pat),
         clone_or_copy_needed: false,
-        addr_of_exprs: Vec::new(),
+        references_to_binding: Vec::new(),
     };
     visitor.visit_expr(body);
-    (visitor.clone_or_copy_needed, visitor.addr_of_exprs)
+    (visitor.clone_or_copy_needed, visitor.references_to_binding)
 }
 
 /// Returns a vector of all `HirId`s bound by the pattern.
@@ -127,7 +128,7 @@ struct CloneOrCopyVisitor<'cx, 'tcx> {
     cx: &'cx LateContext<'tcx>,
     binding_hir_ids: Vec<HirId>,
     clone_or_copy_needed: bool,
-    addr_of_exprs: Vec<&'tcx Expr<'tcx>>,
+    references_to_binding: Vec<(Span, String)>,
 }
 
 impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> {
@@ -142,8 +143,11 @@ impl<'cx, 'tcx> Visitor<'tcx> for CloneOrCopyVisitor<'cx, 'tcx> {
         if self.is_binding(expr) {
             if let Some(parent) = get_parent_expr(self.cx, expr) {
                 match parent.kind {
-                    ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) => {
-                        self.addr_of_exprs.push(parent);
+                    ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, referent) => {
+                        if !parent.span.from_expansion() {
+                            self.references_to_binding
+                                .push((parent.span.until(referent.span), String::new()));
+                        }
                         return;
                     },
                     ExprKind::MethodCall(.., args, _) => {
diff --git a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
index c6b7f5b0ce2..e43b712021a 100644
--- a/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
+++ b/src/tools/clippy/clippy_lints/src/min_ident_chars.rs
@@ -12,14 +12,13 @@ use std::borrow::Cow;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for idents which comprise of a single letter.
+    /// Checks for identifiers which consist of a single character (or fewer than the configured threshold).
     ///
     /// Note: This lint can be very noisy when enabled; it may be desirable to only enable it
     /// temporarily.
     ///
-    /// ### Why is this bad?
-    /// In many cases it's not, but at times it can severely hinder readability. Some codebases may
-    /// wish to disallow this to improve readability.
+    /// ### Why restrict this?
+    /// To improve readability by requiring that every variable has a name more specific than a single letter can be.
     ///
     /// ### Example
     /// ```rust,ignore
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
index 2f5499d7656..fedcfd11fdc 100644
--- a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
@@ -23,7 +23,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for structure field patterns bound to wildcards.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Using `..` instead is shorter and leaves the focus on
     /// the fields that are actually bound.
     ///
@@ -138,7 +138,7 @@ declare_clippy_lint! {
     /// To enforce unseparated literal suffix style,
     /// see the `separated_literal_suffix` lint.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Suffix style should be consistent.
     ///
     /// ### Example
@@ -166,7 +166,7 @@ declare_clippy_lint! {
     /// To enforce separated literal suffix style,
     /// see the `unseparated_literal_suffix` lint.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Suffix style should be consistent.
     ///
     /// ### Example
diff --git a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
index 04df7b7a7e5..dd98352da86 100644
--- a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
@@ -10,7 +10,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks assertions without a custom panic message.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Without a good custom message, it'd be hard to understand what went wrong when the assertion fails.
     /// A good custom message should be more about why the failure of the assertion is problematic
     /// and not what is failed because the assertion already conveys that.
diff --git a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
index c29e46b941c..752723a0c68 100644
--- a/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_asserts_for_indexing.rs
@@ -21,7 +21,7 @@ declare_clippy_lint! {
     /// Checks for repeated slice indexing without asserting beforehand that the length
     /// is greater than the largest index used to index into the slice.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// In the general case where the compiler does not have a lot of information
     /// about the length of a slice, indexing it repeatedly will generate a bounds check
     /// for every single index.
diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs
index 2fb784dae1c..ca344dc5c81 100644
--- a/src/tools/clippy/clippy_lints/src/missing_doc.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs
@@ -20,9 +20,9 @@ use rustc_span::{sym, Span};
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Warns if there is missing doc for any private documentable item
+    /// Warns if there is missing documentation for any private documentable item.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Doc is good. *rustc* has a `MISSING_DOCS`
     /// allowed-by-default lint for
     /// public members, but has no way to enforce documentation of private items.
diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs
index c6a76478806..33a14d8b7fe 100644
--- a/src/tools/clippy/clippy_lints/src/missing_inline.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs
@@ -10,13 +10,16 @@ declare_clippy_lint! {
     /// It lints if an exported function, method, trait method with default impl,
     /// or trait method impl is not `#[inline]`.
     ///
-    /// ### Why is this bad?
-    /// In general, it is not. Functions can be inlined across
-    /// crates when that's profitable as long as any form of LTO is used. When LTO is disabled,
-    /// functions that are not `#[inline]` cannot be inlined across crates. Certain types of crates
-    /// might intend for most of the methods in their public API to be able to be inlined across
-    /// crates even when LTO is disabled. For these types of crates, enabling this lint might make
-    /// sense. It allows the crate to require all exported methods to be `#[inline]` by default, and
+    /// ### Why restrict this?
+    /// When a function is not marked `#[inline]`, it is not
+    /// [a “small” candidate for automatic inlining][small], and LTO is not in use, then it is not
+    /// possible for the function to be inlined into the code of any crate other than the one in
+    /// which it is defined.  Depending on the role of the function and the relationship of the crates,
+    /// this could significantly reduce performance.
+    ///
+    /// Certain types of crates might intend for most of the methods in their public API to be able
+    /// to be inlined across crates even when LTO is disabled.
+    /// This lint allows those crates to require all exported methods to be `#[inline]` by default, and
     /// then opt out for specific methods where this might not make sense.
     ///
     /// ### Example
@@ -51,6 +54,8 @@ declare_clippy_lint! {
     ///    fn def_bar() {} // missing #[inline]
     /// }
     /// ```
+    ///
+    /// [small]: https://github.com/rust-lang/rust/pull/116505
     #[clippy::version = "pre 1.29.0"]
     pub MISSING_INLINE_IN_PUBLIC_ITEMS,
     restriction,
diff --git a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs
index 6f844bc646a..85029a5e6a0 100644
--- a/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_trait_methods.rs
@@ -10,16 +10,16 @@ use rustc_session::declare_lint_pass;
 declare_clippy_lint! {
     /// ### What it does
     /// Checks if a provided method is used implicitly by a trait
-    /// implementation. A usage example would be a wrapper where every method
-    /// should perform some operation before delegating to the inner type's
     /// implementation.
     ///
+    /// ### Why restrict this?
+    /// To ensure that a certain implementation implements every method; for example,
+    /// a wrapper type where every method should delegate to the corresponding method of
+    /// the inner type's implementation.
+    ///
     /// This lint should typically be enabled on a specific trait `impl` item
     /// rather than globally.
     ///
-    /// ### Why is this bad?
-    /// Indicates that a method is missing.
-    ///
     /// ### Example
     /// ```no_run
     /// trait Trait {
diff --git a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
index 181351910db..9c5a8a0cfcd 100644
--- a/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
+++ b/src/tools/clippy/clippy_lints/src/mixed_read_write_in_expression.rs
@@ -12,9 +12,10 @@ declare_clippy_lint! {
     /// whether the read occurs before or after the write depends on the evaluation
     /// order of sub-expressions.
     ///
-    /// ### Why is this bad?
-    /// It is often confusing to read. As described [here](https://doc.rust-lang.org/reference/expressions.html?highlight=subexpression#evaluation-order-of-operands),
-    /// the operands of these expressions are evaluated before applying the effects of the expression.
+    /// ### Why restrict this?
+    /// While [the evaluation order of sub-expressions] is fully specified in Rust,
+    /// it still may be confusing to read an expression where the evaluation order
+    /// affects its behavior.
     ///
     /// ### Known problems
     /// Code which intentionally depends on the evaluation
@@ -40,6 +41,8 @@ declare_clippy_lint! {
     /// };
     /// let a = tmp + x;
     /// ```
+    ///
+    /// [order]: (https://doc.rust-lang.org/reference/expressions.html?highlight=subexpression#evaluation-order-of-operands)
     #[clippy::version = "pre 1.29.0"]
     pub MIXED_READ_WRITE_IN_EXPRESSION,
     restriction,
diff --git a/src/tools/clippy/clippy_lints/src/module_style.rs b/src/tools/clippy/clippy_lints/src/module_style.rs
index 6c031c08175..305499f9da4 100644
--- a/src/tools/clippy/clippy_lints/src/module_style.rs
+++ b/src/tools/clippy/clippy_lints/src/module_style.rs
@@ -10,9 +10,9 @@ use std::path::{Component, Path};
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks that module layout uses only self named module files, bans `mod.rs` files.
+    /// Checks that module layout uses only self named module files; bans `mod.rs` files.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Having multiple module layout styles in a project can be confusing.
     ///
     /// ### Example
@@ -41,7 +41,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks that module layout uses only `mod.rs` files.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Having multiple module layout styles in a project can be confusing.
     ///
     /// ### Example
diff --git a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
index 5306205aed7..5b4ef852f0d 100644
--- a/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
+++ b/src/tools/clippy/clippy_lints/src/multiple_unsafe_ops_per_block.rs
@@ -2,7 +2,7 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::visitors::{for_each_expr_with_closures, Descend, Visitable};
 use core::ops::ControlFlow::Continue;
 use hir::def::{DefKind, Res};
-use hir::{BlockCheckMode, ExprKind, Safety, QPath, UnOp};
+use hir::{BlockCheckMode, ExprKind, QPath, Safety, UnOp};
 use rustc_ast::Mutability;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
@@ -15,7 +15,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for `unsafe` blocks that contain more than one unsafe operation.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Combined with `undocumented_unsafe_blocks`,
     /// this lint ensures that each unsafe operation must be independently justified.
     /// Combined with `unused_unsafe`, this lint also ensures
diff --git a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs
index 7ecc8617694..853e476a006 100644
--- a/src/tools/clippy/clippy_lints/src/mutex_atomic.rs
+++ b/src/tools/clippy/clippy_lints/src/mutex_atomic.rs
@@ -14,7 +14,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for usage of `Mutex<X>` where an atomic will do.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Using a mutex just to make access to a plain bool or
     /// reference sequential is shooting flies with cannons.
     /// `std::sync::atomic::AtomicBool` and `std::sync::atomic::AtomicPtr` are leaner and
diff --git a/src/tools/clippy/clippy_lints/src/needless_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bool.rs
index f9ee4a3dc93..e1866eaa18a 100644
--- a/src/tools/clippy/clippy_lints/src/needless_bool.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_bool.rs
@@ -6,8 +6,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{
-    higher, is_else_clause, is_expn_of, is_parent_stmt, peel_blocks, peel_blocks_with_stmt, span_extract_comment,
-    SpanlessEq,
+    higher, is_block_like, is_else_clause, is_expn_of, is_parent_stmt, peel_blocks, peel_blocks_with_stmt,
+    span_extract_comment, SpanlessEq,
 };
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
@@ -121,14 +121,7 @@ fn condition_needs_parentheses(e: &Expr<'_>) -> bool {
     | ExprKind::Type(i, _)
     | ExprKind::Index(i, _, _) = inner.kind
     {
-        if matches!(
-            i.kind,
-            ExprKind::Block(..)
-                | ExprKind::ConstBlock(..)
-                | ExprKind::If(..)
-                | ExprKind::Loop(..)
-                | ExprKind::Match(..)
-        ) {
+        if is_block_like(i) {
             return true;
         }
         inner = i;
diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
index 7b26235291a..eacfe9ff328 100644
--- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
+++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
@@ -98,6 +98,10 @@ struct SimilarNamesLocalVisitor<'a, 'tcx> {
 
 impl<'a, 'tcx> SimilarNamesLocalVisitor<'a, 'tcx> {
     fn check_single_char_names(&self) {
+        if self.single_char_names.last().map(Vec::len) == Some(0) {
+            return;
+        }
+
         let num_single_char_names = self.single_char_names.iter().flatten().count();
         let threshold = self.lint.single_char_binding_names_threshold;
         if num_single_char_names as u64 > threshold {
diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs
index e002429e3a4..0948973df5a 100644
--- a/src/tools/clippy/clippy_lints/src/operators/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs
@@ -70,7 +70,7 @@ declare_clippy_lint! {
     /// Known safe built-in types like `Wrapping` or `Saturating`, floats, operations in constant
     /// environments, allowed types and non-constant operations that won't overflow are ignored.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// For integers, overflow will trigger a panic in debug builds or wrap the result in
     /// release mode; division by zero will cause a panic in either mode. As a result, it is
     /// desirable to explicitly call checked, wrapping or saturating arithmetic methods.
@@ -100,7 +100,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for float arithmetic.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// For some embedded systems or kernel development, it
     /// can be useful to rule out floating-point numbers.
     ///
@@ -502,7 +502,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for division of integers
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// When outside of some very specific algorithms,
     /// integer division is very often a mistake because it discards the
     /// remainder.
@@ -596,7 +596,7 @@ declare_clippy_lint! {
     /// value and constant, except in functions called `*eq*` (which probably
     /// implement equality for a type involving floats).
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Floating point calculations are usually imprecise, so
     /// asking if two values are *exactly* equal is asking for trouble. For a good
     /// guide on what to do, see [the floating point
@@ -653,8 +653,8 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for modulo arithmetic.
     ///
-    /// ### Why is this bad?
-    /// The results of modulo (%) operation might differ
+    /// ### Why restrict this?
+    /// The results of modulo (`%`) operation might differ
     /// depending on the language, when negative numbers are involved.
     /// If you interop with different languages it might be beneficial
     /// to double check all places that use modulo arithmetic.
diff --git a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
index f821a4efee7..806638c0505 100644
--- a/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/panic_in_result_fn.rs
@@ -13,9 +13,9 @@ use rustc_span::{sym, Span};
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for usage of `panic!` or assertions in a function of type result.
+    /// Checks for usage of `panic!` or assertions in a function whose return type is `Result`.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// For some codebases, it is desirable for functions of type result to return an error instead of crashing. Hence panicking macros should be avoided.
     ///
     /// ### Known problems
diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
index 75066c1f0d2..80ef761906e 100644
--- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
+++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
@@ -14,8 +14,8 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for usage of `panic!`.
     ///
-    /// ### Why is this bad?
-    /// `panic!` will stop the execution of the executable.
+    /// ### Why restrict this?
+    /// This macro, or panics in general, may be unwanted in production code.
     ///
     /// ### Example
     /// ```no_run
@@ -31,8 +31,8 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for usage of `unimplemented!`.
     ///
-    /// ### Why is this bad?
-    /// This macro should not be present in production code.
+    /// ### Why restrict this?
+    /// This macro, or panics in general, may be unwanted in production code.
     ///
     /// ### Example
     /// ```no_run
@@ -48,9 +48,9 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for usage of `todo!`.
     ///
-    /// ### Why is this bad?
-    /// The `todo!` macro is often used for unfinished code, and it causes
-    /// code to panic. It should not be present in production code.
+    /// ### Why restrict this?
+    /// The `todo!` macro indicates the presence of unfinished code,
+    /// so it should not be present in production code.
     ///
     /// ### Example
     /// ```no_run
@@ -70,8 +70,8 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for usage of `unreachable!`.
     ///
-    /// ### Why is this bad?
-    /// This macro can cause code to panic.
+    /// ### Why restrict this?
+    /// This macro, or panics in general, may be unwanted in production code.
     ///
     /// ### Example
     /// ```no_run
diff --git a/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs b/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs
index ffa403e27ca..2d20cbea698 100644
--- a/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs
+++ b/src/tools/clippy/clippy_lints/src/partial_pub_fields.rs
@@ -5,15 +5,16 @@ use rustc_session::declare_lint_pass;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks whether partial fields of a struct are public.
+    /// Checks whether some but not all fields of a `struct` are public.
     ///
     /// Either make all fields of a type public, or make none of them public
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Most types should either be:
     /// * Abstract data types: complex objects with opaque implementation which guard
-    /// interior invariants and expose intentionally limited API to the outside world.
-    /// * Data: relatively simple objects which group a bunch of related attributes together.
+    ///   interior invariants and expose intentionally limited API to the outside world.
+    /// * Data: relatively simple objects which group a bunch of related attributes together,
+    ///   but have no invariants.
     ///
     /// ### Example
     /// ```no_run
diff --git a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
index 44db061b8be..9661a57b8b9 100644
--- a/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
+++ b/src/tools/clippy/clippy_lints/src/pattern_type_mismatch.rs
@@ -30,9 +30,8 @@ declare_clippy_lint! {
     /// this lint can still be used to highlight areas of interest and ensure a good understanding
     /// of ownership semantics.
     ///
-    /// ### Why is this bad?
-    /// It isn't bad in general. But in some contexts it can be desirable
-    /// because it increases ownership hints in the code, and will guard against some changes
+    /// ### Why restrict this?
+    /// It increases ownership hints in the code, and will guard against some changes
     /// in ownership.
     ///
     /// ### Example
diff --git a/src/tools/clippy/clippy_lints/src/pub_use.rs b/src/tools/clippy/clippy_lints/src/pub_use.rs
index c0e999e76ef..ab8f8a1689d 100644
--- a/src/tools/clippy/clippy_lints/src/pub_use.rs
+++ b/src/tools/clippy/clippy_lints/src/pub_use.rs
@@ -5,13 +5,11 @@ use rustc_session::declare_lint_pass;
 
 declare_clippy_lint! {
     /// ### What it does
-    ///
     /// Restricts the usage of `pub use ...`
     ///
-    /// ### Why is this bad?
-    ///
-    /// `pub use` is usually fine, but a project may wish to limit `pub use` instances to prevent
-    /// unintentional exports or to encourage placing exported items directly in public modules
+    /// ### Why restrict this?
+    /// A project may wish to limit `pub use` instances to prevent
+    /// unintentional exports, or to encourage placing exported items directly in public modules.
     ///
     /// ### Example
     /// ```no_run
diff --git a/src/tools/clippy/clippy_lints/src/question_mark_used.rs b/src/tools/clippy/clippy_lints/src/question_mark_used.rs
index ddfc53083c4..f5e6cb804da 100644
--- a/src/tools/clippy/clippy_lints/src/question_mark_used.rs
+++ b/src/tools/clippy/clippy_lints/src/question_mark_used.rs
@@ -9,7 +9,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for expressions that use the question mark operator and rejects them.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Sometimes code wants to avoid the question mark operator because for instance a local
     /// block requires a macro to re-throw errors to attach additional information to the
     /// error.
diff --git a/src/tools/clippy/clippy_lints/src/raw_strings.rs b/src/tools/clippy/clippy_lints/src/raw_strings.rs
index 7e71f48c6d9..3a004245459 100644
--- a/src/tools/clippy/clippy_lints/src/raw_strings.rs
+++ b/src/tools/clippy/clippy_lints/src/raw_strings.rs
@@ -15,8 +15,10 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for raw string literals where a string literal can be used instead.
     ///
-    /// ### Why is this bad?
-    /// It's just unnecessary, but there are many cases where using a raw string literal is more
+    /// ### Why restrict this?
+    /// For consistent style by using simpler string literals whenever possible.
+    ///
+    /// However, there are many cases where using a raw string literal is more
     /// idiomatic than a string literal, so it's opt-in.
     ///
     /// ### Example
diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
index c99b657c23a..7f87d18e502 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
@@ -46,7 +46,7 @@ declare_clippy_lint! {
     /// Checks for slicing expressions which are equivalent to dereferencing the
     /// value.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Some people may prefer to dereference rather than slice.
     ///
     /// ### Example
diff --git a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs
index 11b95ee3a54..81556f39614 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs
@@ -11,7 +11,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Warns about needless / redundant type annotations.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Code without type annotations is shorter and in most cases
     /// more idiomatic and easier to modify.
     ///
diff --git a/src/tools/clippy/clippy_lints/src/ref_patterns.rs b/src/tools/clippy/clippy_lints/src/ref_patterns.rs
index 607a0740b84..467038523b4 100644
--- a/src/tools/clippy/clippy_lints/src/ref_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/ref_patterns.rs
@@ -6,9 +6,11 @@ use rustc_session::declare_lint_pass;
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for usages of the `ref` keyword.
-    /// ### Why is this bad?
+    ///
+    /// ### Why restrict this?
     /// The `ref` keyword can be confusing for people unfamiliar with it, and often
     /// it is more concise to use `&` instead.
+    ///
     /// ### Example
     /// ```no_run
     /// let opt = Some(5);
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index e8f9d438104..48d6fb3c037 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -3,8 +3,8 @@ use clippy_utils::source::{snippet_opt, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
 use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
 use clippy_utils::{
-    fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res, path_to_local_id, span_contains_cfg,
-    span_find_starting_semi,
+    binary_expr_needs_parentheses, fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res,
+    path_to_local_id, span_contains_cfg, span_find_starting_semi,
 };
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
@@ -129,7 +129,7 @@ enum RetReplacement<'tcx> {
     Empty,
     Block,
     Unit,
-    IfSequence(Cow<'tcx, str>, Applicability),
+    NeedsPar(Cow<'tcx, str>, Applicability),
     Expr(Cow<'tcx, str>, Applicability),
 }
 
@@ -139,13 +139,13 @@ impl<'tcx> RetReplacement<'tcx> {
             Self::Empty | Self::Expr(..) => "remove `return`",
             Self::Block => "replace `return` with an empty block",
             Self::Unit => "replace `return` with a unit value",
-            Self::IfSequence(..) => "remove `return` and wrap the sequence with parentheses",
+            Self::NeedsPar(..) => "remove `return` and wrap the sequence with parentheses",
         }
     }
 
     fn applicability(&self) -> Applicability {
         match self {
-            Self::Expr(_, ap) | Self::IfSequence(_, ap) => *ap,
+            Self::Expr(_, ap) | Self::NeedsPar(_, ap) => *ap,
             _ => Applicability::MachineApplicable,
         }
     }
@@ -157,7 +157,7 @@ impl<'tcx> Display for RetReplacement<'tcx> {
             Self::Empty => write!(f, ""),
             Self::Block => write!(f, "{{}}"),
             Self::Unit => write!(f, "()"),
-            Self::IfSequence(inner, _) => write!(f, "({inner})"),
+            Self::NeedsPar(inner, _) => write!(f, "({inner})"),
             Self::Expr(inner, _) => write!(f, "{inner}"),
         }
     }
@@ -244,7 +244,11 @@ impl<'tcx> LateLintPass<'tcx> for Return {
                     err.span_label(local.span, "unnecessary `let` binding");
 
                     if let Some(mut snippet) = snippet_opt(cx, initexpr.span) {
-                        if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
+                        if binary_expr_needs_parentheses(initexpr) {
+                            if !has_enclosing_paren(&snippet) {
+                                snippet = format!("({snippet})");
+                            }
+                        } else if !cx.typeck_results().expr_adjustments(retexpr).is_empty() {
                             if !has_enclosing_paren(&snippet) {
                                 snippet = format!("({snippet})");
                             }
@@ -349,8 +353,8 @@ fn check_final_expr<'tcx>(
 
                 let mut applicability = Applicability::MachineApplicable;
                 let (snippet, _) = snippet_with_context(cx, inner_expr.span, ret_span.ctxt(), "..", &mut applicability);
-                if expr_contains_conjunctive_ifs(inner_expr) {
-                    RetReplacement::IfSequence(snippet, applicability)
+                if binary_expr_needs_parentheses(inner_expr) {
+                    RetReplacement::NeedsPar(snippet, applicability)
                 } else {
                     RetReplacement::Expr(snippet, applicability)
                 }
@@ -404,18 +408,6 @@ fn check_final_expr<'tcx>(
     }
 }
 
-fn expr_contains_conjunctive_ifs<'tcx>(expr: &'tcx Expr<'tcx>) -> bool {
-    fn contains_if(expr: &Expr<'_>, on_if: bool) -> bool {
-        match expr.kind {
-            ExprKind::If(..) => on_if,
-            ExprKind::Binary(_, left, right) => contains_if(left, true) || contains_if(right, true),
-            _ => false,
-        }
-    }
-
-    contains_if(expr, false)
-}
-
 fn emit_return_lint(
     cx: &LateContext<'_>,
     ret_span: Span,
diff --git a/src/tools/clippy/clippy_lints/src/same_name_method.rs b/src/tools/clippy/clippy_lints/src/same_name_method.rs
index 7e27f70bcf9..8fdd19c549f 100644
--- a/src/tools/clippy/clippy_lints/src/same_name_method.rs
+++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs
@@ -14,7 +14,7 @@ declare_clippy_lint! {
     /// It lints if a struct has two methods with the same name:
     /// one from a trait, another not from trait.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Confusing.
     ///
     /// ### Example
diff --git a/src/tools/clippy/clippy_lints/src/semicolon_block.rs b/src/tools/clippy/clippy_lints/src/semicolon_block.rs
index 0b3adfb7a4b..0e77acdfd77 100644
--- a/src/tools/clippy/clippy_lints/src/semicolon_block.rs
+++ b/src/tools/clippy/clippy_lints/src/semicolon_block.rs
@@ -11,8 +11,7 @@ declare_clippy_lint! {
     /// Suggests moving the semicolon after a block to the inside of the block, after its last
     /// expression.
     ///
-    /// ### Why is this bad?
-    ///
+    /// ### Why restrict this?
     /// For consistency it's best to have the semicolon inside/outside the block. Either way is fine
     /// and this lint suggests inside the block.
     /// Take a look at `semicolon_outside_block` for the other alternative.
@@ -40,8 +39,7 @@ declare_clippy_lint! {
     ///
     /// Suggests moving the semicolon from a block's final expression outside of the block.
     ///
-    /// ### Why is this bad?
-    ///
+    /// ### Why restrict this?
     /// For consistency it's best to have the semicolon inside/outside the block. Either way is fine
     /// and this lint suggests outside the block.
     /// Take a look at `semicolon_inside_block` for the other alternative.
diff --git a/src/tools/clippy/clippy_lints/src/shadow.rs b/src/tools/clippy/clippy_lints/src/shadow.rs
index 9db08acb03b..80f5fd0b494 100644
--- a/src/tools/clippy/clippy_lints/src/shadow.rs
+++ b/src/tools/clippy/clippy_lints/src/shadow.rs
@@ -15,10 +15,10 @@ declare_clippy_lint! {
     /// Checks for bindings that shadow other bindings already in
     /// scope, while just changing reference level or mutability.
     ///
-    /// ### Why is this bad?
-    /// Not much, in fact it's a very common pattern in Rust
-    /// code. Still, some may opt to avoid it in their code base, they can set this
-    /// lint to `Warn`.
+    /// ### Why restrict this?
+    /// To require that what are formally distinct variables be given distinct names.
+    ///
+    /// See also `shadow_reuse` and `shadow_unrelated` for other restrictions on shadowing.
     ///
     /// ### Example
     /// ```no_run
@@ -42,12 +42,13 @@ declare_clippy_lint! {
     /// Checks for bindings that shadow other bindings already in
     /// scope, while reusing the original value.
     ///
-    /// ### Why is this bad?
-    /// Not too much, in fact it's a common pattern in Rust
-    /// code. Still, some argue that name shadowing like this hurts readability,
+    /// ### Why restrict this?
+    /// Some argue that name shadowing like this hurts readability,
     /// because a value may be bound to different things depending on position in
     /// the code.
     ///
+    /// See also `shadow_same` and `shadow_unrelated` for other restrictions on shadowing.
+    ///
     /// ### Example
     /// ```no_run
     /// let x = 2;
@@ -70,11 +71,14 @@ declare_clippy_lint! {
     /// scope, either without an initialization or with one that does not even use
     /// the original value.
     ///
-    /// ### Why is this bad?
-    /// Name shadowing can hurt readability, especially in
+    /// ### Why restrict this?
+    /// Shadowing a binding with a closely related one is part of idiomatic Rust,
+    /// but shadowing a binding by accident with an unrelated one may indicate a mistake.
+    ///
+    /// Additionally, name shadowing in general can hurt readability, especially in
     /// large code bases, because it is easy to lose track of the active binding at
-    /// any place in the code. This can be alleviated by either giving more specific
-    /// names to bindings or introducing more scopes to contain the bindings.
+    /// any place in the code. If linting against all shadowing is desired, you may wish
+    /// to use the `shadow_same` and `shadow_reuse` lints as well.
     ///
     /// ### Example
     /// ```no_run
diff --git a/src/tools/clippy/clippy_lints/src/single_call_fn.rs b/src/tools/clippy/clippy_lints/src/single_call_fn.rs
index 2ce7e714c64..c71bc3f7fcd 100644
--- a/src/tools/clippy/clippy_lints/src/single_call_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/single_call_fn.rs
@@ -13,13 +13,20 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for functions that are only used once. Does not lint tests.
     ///
-    /// ### Why is this bad?
-    /// It's usually not, splitting a function into multiple parts often improves readability and in
-    /// the case of generics, can prevent the compiler from duplicating the function dozens of
-    /// time; instead, only duplicating a thunk. But this can prevent segmentation across a
-    /// codebase, where many small functions are used only once.
+    /// ### Why restrict this?
+    /// If a function is only used once (perhaps because it used to be used more widely),
+    /// then the code could be simplified by moving that function's code into its caller.
     ///
-    /// Note: If this lint is used, prepare to allow this a lot.
+    /// However, there are reasons not to do this everywhere:
+    ///
+    /// * Splitting a large function into multiple parts often improves readability
+    ///   by giving names to its parts.
+    /// * A function’s signature might serve a necessary purpose, such as constraining
+    ///   the type of a closure passed to it.
+    /// * Generic functions might call non-generic functions to reduce duplication
+    ///   in the produced machine code.
+    ///
+    /// If this lint is used, prepare to `#[allow]` it a lot.
     ///
     /// ### Example
     /// ```no_run
diff --git a/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs
index 42f1564db35..72feb977c31 100644
--- a/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs
+++ b/src/tools/clippy/clippy_lints/src/single_char_lifetime_names.rs
@@ -9,11 +9,10 @@ declare_clippy_lint! {
     /// Checks for lifetimes with names which are one character
     /// long.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// A single character is likely not enough to express the
     /// purpose of a lifetime. Using a longer name can make code
-    /// easier to understand, especially for those who are new to
-    /// Rust.
+    /// easier to understand.
     ///
     /// ### Known problems
     /// Rust programmers and learning resources tend to use single
diff --git a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
index 926c56332cc..12b70075a3d 100644
--- a/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
+++ b/src/tools/clippy/clippy_lints/src/std_instead_of_core.rs
@@ -12,11 +12,9 @@ use rustc_span::{sym, Span};
 
 declare_clippy_lint! {
     /// ### What it does
-    ///
     /// Finds items imported through `std` when available through `core`.
     ///
-    /// ### Why is this bad?
-    ///
+    /// ### Why restrict this?
     /// Crates which have `no_std` compatibility may wish to ensure types are imported from core to ensure
     /// disabling `std` does not cause the crate to fail to compile. This lint is also useful for crates
     /// migrating to become `no_std` compatible.
@@ -37,11 +35,9 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    ///
     /// Finds items imported through `std` when available through `alloc`.
     ///
-    /// ### Why is this bad?
-    ///
+    /// ### Why restrict this?
     /// Crates which have `no_std` compatibility and require alloc may wish to ensure types are imported from
     /// alloc to ensure disabling `std` does not cause the crate to fail to compile. This lint is also useful
     /// for crates migrating to become `no_std` compatible.
@@ -63,11 +59,9 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    ///
     /// Finds items imported through `alloc` when available through `core`.
     ///
-    /// ### Why is this bad?
-    ///
+    /// ### Why restrict this?
     /// Crates which have `no_std` compatibility and may optionally require alloc may wish to ensure types are
     /// 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.
diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs
index 292124196ff..f8d36d6b92f 100644
--- a/src/tools/clippy/clippy_lints/src/strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strings.rs
@@ -45,10 +45,10 @@ declare_clippy_lint! {
     /// `String`, but only if [`string_add_assign`](#string_add_assign) does *not*
     /// match.
     ///
-    /// ### Why is this bad?
-    /// It's not bad in and of itself. However, this particular
+    /// ### Why restrict this?
+    /// This particular
     /// `Add` implementation is asymmetric (the other operand need not be `String`,
-    /// but `x` does), while addition as mathematically defined is symmetric, also
+    /// but `x` does), while addition as mathematically defined is symmetric, and
     /// the `String::push_str(_)` function is a perfectly good replacement.
     /// Therefore, some dislike it and wish not to have it in their code.
     ///
@@ -123,7 +123,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for slice operations on strings
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// UTF-8 characters span multiple bytes, and it is easy to inadvertently confuse character
     /// counts and string indices. This may lead to panics, and should warrant some test cases
     /// containing wide UTF-8 characters. This lint is most useful in code that should avoid
@@ -364,10 +364,10 @@ declare_clippy_lint! {
     /// ### What it does
     /// This lint checks for `.to_string()` method calls on values of type `&str`.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// The `to_string` method is also used on other types to convert them to a string.
-    /// When called on a `&str` it turns the `&str` into the owned variant `String`, which can be better
-    /// expressed with `.to_owned()`.
+    /// When called on a `&str` it turns the `&str` into the owned variant `String`, which can be
+    /// more specifically expressed with `.to_owned()`.
     ///
     /// ### Example
     /// ```no_run
@@ -415,9 +415,10 @@ declare_clippy_lint! {
     /// ### What it does
     /// This lint checks for `.to_string()` method calls on values of type `String`.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// The `to_string` method is also used on other types to convert them to a string.
-    /// When called on a `String` it only clones the `String`, which can be better expressed with `.clone()`.
+    /// When called on a `String` it only clones the `String`, which can be more specifically
+    /// expressed with `.clone()`.
     ///
     /// ### Example
     /// ```no_run
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs b/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs
index 1cc27670fa8..d150a5f858a 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_xor_used_as_pow.rs
@@ -11,8 +11,10 @@ use rustc_session::declare_lint_pass;
 declare_clippy_lint! {
     /// ### What it does
     /// Warns for a Bitwise XOR (`^`) operator being probably confused as a powering. It will not trigger if any of the numbers are not in decimal.
-    /// ### Why is this bad?
+    ///
+    /// ### Why restrict this?
     /// It's most probably a typo and may lead to unexpected behaviours.
+    ///
     /// ### Example
     /// ```no_run
     /// let x = 3_i32 ^ 4_i32;
diff --git a/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
index da557582647..58e42892c41 100644
--- a/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
+++ b/src/tools/clippy/clippy_lints/src/tests_outside_test_module.rs
@@ -11,9 +11,11 @@ declare_clippy_lint! {
     /// ### What it does
     /// Triggers when a testing function (marked with the `#[test]` attribute) isn't inside a testing module
     /// (marked with `#[cfg(test)]`).
-    /// ### Why is this bad?
+    ///
+    /// ### Why restrict this?
     /// The idiomatic (and more performant) way of writing tests is inside a testing module (flagged with `#[cfg(test)]`),
     /// having test functions outside of this module is confusing and may lead to them being "hidden".
+    ///
     /// ### Example
     /// ```no_run
     /// #[test]
diff --git a/src/tools/clippy/clippy_lints/src/types/mod.rs b/src/tools/clippy/clippy_lints/src/types/mod.rs
index 5e45ab211ef..62ef65ca122 100644
--- a/src/tools/clippy/clippy_lints/src/types/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/types/mod.rs
@@ -217,7 +217,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for `Rc<T>` and `Arc<T>` when `T` is a mutable buffer type such as `String` or `Vec`.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Expressions such as `Rc<String>` usually have no advantage over `Rc<str>`, since
     /// it is larger and involves an extra level of indirection, and doesn't implement `Borrow<str>`.
     ///
@@ -274,7 +274,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for `Rc<Mutex<T>>`.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// `Rc` is used in single thread and `Mutex` is used in multi thread.
     /// Consider using `Rc<RefCell<T>>` in single thread or `Arc<Mutex<T>>` in multi thread.
     ///
diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
index 4120bb1331b..6cf9229fdd0 100644
--- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -38,10 +38,9 @@ declare_clippy_lint! {
     /// );
     /// ```
     ///
-    /// ### Why is this bad?
-    /// Undocumented unsafe blocks and impls can make it difficult to
-    /// read and maintain code, as well as uncover unsoundness
-    /// and bugs.
+    /// ### Why restrict this?
+    /// Undocumented unsafe blocks and impls can make it difficult to read and maintain code.
+    /// Writing out the safety justification may help in discovering unsoundness or bugs.
     ///
     /// ### Example
     /// ```no_run
@@ -67,7 +66,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for `// SAFETY: ` comments on safe code.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Safe code has no safety requirements, so there is no need to
     /// describe safety invariants.
     ///
diff --git a/src/tools/clippy/clippy_lints/src/unicode.rs b/src/tools/clippy/clippy_lints/src/unicode.rs
index 3d319b9fe76..d42697b31d1 100644
--- a/src/tools/clippy/clippy_lints/src/unicode.rs
+++ b/src/tools/clippy/clippy_lints/src/unicode.rs
@@ -31,7 +31,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for non-ASCII characters in string and char literals.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Yeah, we know, the 90's called and wanted their charset
     /// back. Even so, there still are editors and other programs out there that
     /// don't work well with Unicode. So if the code is meant to be used
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs b/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs
index 528a1dfcfc1..93dff1b8579 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_self_imports.rs
@@ -9,7 +9,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for imports ending in `::{self}`.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// In most cases, this can be written much more cleanly by omitting `::{self}`.
     ///
     /// ### Known problems
diff --git a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
index aca500590ce..197ab0f173b 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap_in_result.rs
@@ -13,8 +13,10 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for functions of type `Result` that contain `expect()` or `unwrap()`
     ///
-    /// ### Why is this bad?
-    /// These functions promote recoverable errors to non-recoverable errors which may be undesirable in code bases which wish to avoid panics.
+    /// ### Why restrict this?
+    /// These functions promote recoverable errors to non-recoverable errors,
+    /// which may be undesirable in code bases which wish to avoid panics,
+    /// or be a bug in the specific function.
     ///
     /// ### Known problems
     /// This can cause false positives in functions that handle both recoverable and non recoverable errors.
diff --git a/src/tools/clippy/clippy_lints/src/visibility.rs b/src/tools/clippy/clippy_lints/src/visibility.rs
index 9818b98dd5b..11dcceca7ab 100644
--- a/src/tools/clippy/clippy_lints/src/visibility.rs
+++ b/src/tools/clippy/clippy_lints/src/visibility.rs
@@ -32,7 +32,7 @@ declare_clippy_lint! {
     /// ### What it does
     /// Checks for usage of `pub(<loc>)` with `in`.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Consistency. Use it or don't, just be consistent about it.
     ///
     /// Also see the `pub_without_shorthand` lint for an alternative.
@@ -57,7 +57,7 @@ declare_clippy_lint! {
     /// Note: As you cannot write a module's path in `pub(<loc>)`, this will only trigger on
     /// `pub(super)` and the like.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// Consistency. Use it or don't, just be consistent about it.
     ///
     /// Also see the `pub_with_shorthand` lint for an alternative.
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index ff6ee0d10ad..652ce88bd95 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -66,7 +66,7 @@ declare_clippy_lint! {
     /// Checks for printing on *stdout*. The purpose of this lint
     /// is to catch debugging remnants.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// People often print on *stdout* while debugging an
     /// application and might forget to remove those prints afterward.
     ///
@@ -88,7 +88,7 @@ declare_clippy_lint! {
     /// Checks for printing on *stderr*. The purpose of this lint
     /// is to catch debugging remnants.
     ///
-    /// ### Why is this bad?
+    /// ### Why restrict this?
     /// People often print on *stderr* while debugging an
     /// application and might forget to remove those prints afterward.
     ///
@@ -110,15 +110,18 @@ declare_clippy_lint! {
     /// Checks for usage of `Debug` formatting. The purpose of this
     /// lint is to catch debugging remnants.
     ///
-    /// ### Why is this bad?
-    /// The purpose of the `Debug` trait is to facilitate
-    /// debugging Rust code. It should not be used in user-facing output.
+    /// ### Why restrict this?
+    /// The purpose of the `Debug` trait is to facilitate debugging Rust code,
+    /// and [no guarantees are made about its output][stability].
+    /// It should not be used in user-facing output.
     ///
     /// ### Example
     /// ```no_run
     /// # let foo = "bar";
     /// println!("{:?}", foo);
     /// ```
+    ///
+    /// [stability]: https://doc.rust-lang.org/stable/std/fmt/trait.Debug.html#stability
     #[clippy::version = "pre 1.29.0"]
     pub USE_DEBUG,
     restriction,
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index f5d3967d130..cd88ccd87cf 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -412,8 +412,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
     /// Simple constant folding: Insert an expression, get a constant or none.
     pub fn expr(&mut self, e: &Expr<'_>) -> Option<Constant<'tcx>> {
         match e.kind {
-            ExprKind::ConstBlock(e) |
-            ExprKind::DropTemps(e) => self.expr(e),
+            ExprKind::ConstBlock(e) | ExprKind::DropTemps(e) => self.expr(e),
             ExprKind::Path(ref qpath) => {
                 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)?;
@@ -491,8 +490,7 @@ impl<'a, 'tcx> ConstEvalLateContext<'a, 'tcx> {
     /// leaves the local crate.
     pub fn expr_is_empty(&mut self, e: &Expr<'_>) -> Option<bool> {
         match e.kind {
-            ExprKind::ConstBlock(e) |
-            ExprKind::DropTemps(e) => self.expr_is_empty(e),
+            ExprKind::ConstBlock(e) | ExprKind::DropTemps(e) => self.expr_is_empty(e),
             ExprKind::Path(ref qpath) => {
                 if !self
                     .typeck_results
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index cc5ccd4053a..817d4095eb5 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -769,7 +769,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                 // closures inherit TypeckResults
                 self.hash_expr(self.cx.tcx.hir().body(body).value);
             },
-            ExprKind::ConstBlock(ref l_id) => {
+            ExprKind::ConstBlock(l_id) => {
                 self.hash_expr(l_id);
             },
             ExprKind::DropTemps(e) | ExprKind::Yield(e, _) => {
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 94d4656377f..b10830b24e1 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -3370,3 +3370,25 @@ pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
         Node::Stmt(..) | Node::Block(Block { stmts: &[], .. })
     )
 }
+
+/// Returns true if the given `expr` is a block or resembled as a block,
+/// such as `if`, `loop`, `match` expressions etc.
+pub fn is_block_like(expr: &Expr<'_>) -> bool {
+    matches!(
+        expr.kind,
+        ExprKind::Block(..) | ExprKind::ConstBlock(..) | ExprKind::If(..) | ExprKind::Loop(..) | ExprKind::Match(..)
+    )
+}
+
+/// Returns true if the given `expr` is binary expression that needs to be wrapped in parentheses.
+pub fn binary_expr_needs_parentheses(expr: &Expr<'_>) -> bool {
+    fn contains_block(expr: &Expr<'_>, is_operand: bool) -> bool {
+        match expr.kind {
+            ExprKind::Binary(_, lhs, _) => contains_block(lhs, true),
+            _ if is_block_like(expr) => is_operand,
+            _ => false,
+        }
+    }
+
+    contains_block(expr, false)
+}
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 2dacc34867f..3414b5ef680 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -9,7 +9,7 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
-use rustc_hir::{Expr, FnDecl, Safety, LangItem, TyKind};
+use rustc_hir::{Expr, FnDecl, LangItem, Safety, TyKind};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
 use rustc_middle::mir::interpret::Scalar;
@@ -18,8 +18,8 @@ use rustc_middle::traits::EvaluationResult;
 use rustc_middle::ty::layout::ValidityRequirement;
 use rustc_middle::ty::{
     self, AdtDef, AliasTy, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
-    GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, Upcast, TraitRef, Ty, TyCtxt,
-    TypeSuperVisitable, TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, VariantDef, VariantDiscr,
+    GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
+    TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
 };
 use rustc_span::symbol::Ident;
 use rustc_span::{sym, Span, Symbol, DUMMY_SP};
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index a0585ffdb45..dd8b9ece773 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2024-05-16"
+channel = "nightly-2024-05-30"
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
diff --git a/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs b/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs
index b91a83308b5..ca71dddcc24 100644
--- a/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs
+++ b/src/tools/clippy/tests/ui-internal/disallow_span_lint.rs
@@ -11,11 +11,15 @@ use rustc_lint::{Lint, LintContext};
 use rustc_middle::ty::TyCtxt;
 
 pub fn a(cx: impl LintContext, lint: &'static Lint, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) {
-    cx.span_lint(lint, span, |lint| { lint.primary_message(msg); });
+    cx.span_lint(lint, span, |lint| {
+        lint.primary_message(msg);
+    });
 }
 
 pub fn b(tcx: TyCtxt<'_>, lint: &'static Lint, hir_id: HirId, span: impl Into<MultiSpan>, msg: impl Into<DiagMessage>) {
-    tcx.node_span_lint(lint, hir_id, span, |lint| { lint.primary_message(msg); });
+    tcx.node_span_lint(lint, hir_id, span, |lint| {
+        lint.primary_message(msg);
+    });
 }
 
 fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr
index 1cfbc8efc8e..1be4b665bcb 100644
--- a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr
+++ b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr
@@ -1,18 +1,22 @@
 error: use of a disallowed method `rustc_lint::context::LintContext::span_lint`
   --> tests/ui-internal/disallow_span_lint.rs:14:5
    |
-LL |     cx.span_lint(lint, span, |lint| { lint.primary_message(msg); });
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | /     cx.span_lint(lint, span, |lint| {
+LL | |         lint.primary_message(msg);
+LL | |     });
+   | |______^
    |
    = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead (from clippy.toml)
    = note: `-D clippy::disallowed-methods` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]`
 
 error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_lint`
-  --> tests/ui-internal/disallow_span_lint.rs:18:5
+  --> tests/ui-internal/disallow_span_lint.rs:20:5
    |
-LL |     tcx.node_span_lint(lint, hir_id, span, |lint| { lint.primary_message(msg); });
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | /     tcx.node_span_lint(lint, hir_id, span, |lint| {
+LL | |         lint.primary_message(msg);
+LL | |     });
+   | |______^
    |
    = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead (from clippy.toml)
 
diff --git a/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.fixed b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.fixed
index 6f42487bbf4..eb93eb8e8ed 100644
--- a/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.fixed
+++ b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.fixed
@@ -1,3 +1,4 @@
+#![feature(lint_reasons)]
 #![allow(unused)]
 #![warn(clippy::derive_partial_eq_without_eq)]
 
@@ -14,6 +15,22 @@ pub struct MissingEq {
     bar: String,
 }
 
+// Check that we honor the `allow` attribute
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Debug, PartialEq)]
+pub struct AllowedMissingEq {
+    foo: u32,
+    bar: String,
+}
+
+// Check that we honor the `expect` attribute
+#[expect(clippy::derive_partial_eq_without_eq)]
+#[derive(Debug, PartialEq)]
+pub struct ExpectedMissingEq {
+    foo: u32,
+    bar: String,
+}
+
 // Eq is derived
 #[derive(PartialEq, Eq)]
 pub struct NotMissingEq {
diff --git a/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.rs b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.rs
index 24f687c6c9d..42dc435bdd5 100644
--- a/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.rs
+++ b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.rs
@@ -1,3 +1,4 @@
+#![feature(lint_reasons)]
 #![allow(unused)]
 #![warn(clippy::derive_partial_eq_without_eq)]
 
@@ -14,6 +15,22 @@ pub struct MissingEq {
     bar: String,
 }
 
+// Check that we honor the `allow` attribute
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Debug, PartialEq)]
+pub struct AllowedMissingEq {
+    foo: u32,
+    bar: String,
+}
+
+// Check that we honor the `expect` attribute
+#[expect(clippy::derive_partial_eq_without_eq)]
+#[derive(Debug, PartialEq)]
+pub struct ExpectedMissingEq {
+    foo: u32,
+    bar: String,
+}
+
 // Eq is derived
 #[derive(PartialEq, Eq)]
 pub struct NotMissingEq {
diff --git a/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.stderr b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.stderr
index 3d92112dc36..29cd7da6b77 100644
--- a/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.stderr
+++ b/src/tools/clippy/tests/ui/derive_partial_eq_without_eq.stderr
@@ -1,5 +1,5 @@
 error: you are deriving `PartialEq` and can implement `Eq`
-  --> tests/ui/derive_partial_eq_without_eq.rs:11:17
+  --> tests/ui/derive_partial_eq_without_eq.rs:12:17
    |
 LL | #[derive(Debug, PartialEq)]
    |                 ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
@@ -8,73 +8,73 @@ LL | #[derive(Debug, PartialEq)]
    = help: to override `-D warnings` add `#[allow(clippy::derive_partial_eq_without_eq)]`
 
 error: you are deriving `PartialEq` and can implement `Eq`
-  --> tests/ui/derive_partial_eq_without_eq.rs:53:10
+  --> tests/ui/derive_partial_eq_without_eq.rs:70:10
    |
 LL | #[derive(PartialEq)]
    |          ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
 
 error: you are deriving `PartialEq` and can implement `Eq`
-  --> tests/ui/derive_partial_eq_without_eq.rs:59:10
+  --> tests/ui/derive_partial_eq_without_eq.rs:76:10
    |
 LL | #[derive(PartialEq)]
    |          ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
 
 error: you are deriving `PartialEq` and can implement `Eq`
-  --> tests/ui/derive_partial_eq_without_eq.rs:65:10
+  --> tests/ui/derive_partial_eq_without_eq.rs:82:10
    |
 LL | #[derive(PartialEq)]
    |          ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
 
 error: you are deriving `PartialEq` and can implement `Eq`
-  --> tests/ui/derive_partial_eq_without_eq.rs:68:10
+  --> tests/ui/derive_partial_eq_without_eq.rs:85:10
    |
 LL | #[derive(PartialEq)]
    |          ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
 
 error: you are deriving `PartialEq` and can implement `Eq`
-  --> tests/ui/derive_partial_eq_without_eq.rs:74:10
+  --> tests/ui/derive_partial_eq_without_eq.rs:91:10
    |
 LL | #[derive(PartialEq)]
    |          ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
 
 error: you are deriving `PartialEq` and can implement `Eq`
-  --> tests/ui/derive_partial_eq_without_eq.rs:80:10
+  --> tests/ui/derive_partial_eq_without_eq.rs:97:10
    |
 LL | #[derive(PartialEq)]
    |          ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
 
 error: you are deriving `PartialEq` and can implement `Eq`
-  --> tests/ui/derive_partial_eq_without_eq.rs:93:17
+  --> tests/ui/derive_partial_eq_without_eq.rs:110:17
    |
 LL | #[derive(Debug, PartialEq, Clone)]
    |                 ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
 
 error: you are deriving `PartialEq` and can implement `Eq`
-  --> tests/ui/derive_partial_eq_without_eq.rs:96:10
+  --> tests/ui/derive_partial_eq_without_eq.rs:113:10
    |
 LL | #[derive(PartialEq)]
    |          ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
 
 error: you are deriving `PartialEq` and can implement `Eq`
-  --> tests/ui/derive_partial_eq_without_eq.rs:103:14
+  --> tests/ui/derive_partial_eq_without_eq.rs:120:14
    |
 LL |     #[derive(PartialEq)]
    |              ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
 
 error: you are deriving `PartialEq` and can implement `Eq`
-  --> tests/ui/derive_partial_eq_without_eq.rs:106:14
+  --> tests/ui/derive_partial_eq_without_eq.rs:123:14
    |
 LL |     #[derive(PartialEq)]
    |              ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
 
 error: you are deriving `PartialEq` and can implement `Eq`
-  --> tests/ui/derive_partial_eq_without_eq.rs:166:14
+  --> tests/ui/derive_partial_eq_without_eq.rs:183:14
    |
 LL |     #[derive(PartialEq)]
    |              ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
 
 error: you are deriving `PartialEq` and can implement `Eq`
-  --> tests/ui/derive_partial_eq_without_eq.rs:174:14
+  --> tests/ui/derive_partial_eq_without_eq.rs:191:14
    |
 LL |     #[derive(PartialEq)]
    |              ^^^^^^^^^ help: consider deriving `Eq` as well: `PartialEq, Eq`
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
index 7e22c847b1b..84673f1f43f 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
@@ -240,3 +240,6 @@ extern {
     /// `foo()`
     fn in_extern();
 }
+
+/// <https://github.com/rust-lang/rust-clippy/pull/12836>
+fn check_autofix_for_base_urls() {}
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
index 3e2cb0df54b..4d017a99e0f 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
@@ -240,3 +240,6 @@ extern {
     /// foo()
     fn in_extern();
 }
+
+/// https://github.com/rust-lang/rust-clippy/pull/12836
+fn check_autofix_for_base_urls() {}
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
index cd2228c47e3..a9263f62d38 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
@@ -363,5 +363,11 @@ help: try
 LL |     /// `foo()`
    |         ~~~~~~~
 
-error: aborting due to 33 previous errors
+error: you should put bare URLs between `<`/`>` or make a proper Markdown link
+  --> tests/ui/doc/doc-fixable.rs:244:5
+   |
+LL | /// https://github.com/rust-lang/rust-clippy/pull/12836
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `<https://github.com/rust-lang/rust-clippy/pull/12836>`
+
+error: aborting due to 34 previous errors
 
diff --git a/src/tools/clippy/tests/ui/doc/issue_12795.fixed b/src/tools/clippy/tests/ui/doc/issue_12795.fixed
new file mode 100644
index 00000000000..ade23bf975c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/issue_12795.fixed
@@ -0,0 +1,9 @@
+#![warn(clippy::doc_markdown)]
+
+//! A comment with `a_b(x)` and `a_c` in it and (`a_b((c))` ) too and (maybe `a_b((c))`)
+//~^ ERROR: item in documentation is missing backticks
+//~| ERROR: item in documentation is missing backticks
+//~| ERROR: item in documentation is missing backticks
+//~| ERROR: item in documentation is missing backticks
+
+pub fn main() {}
diff --git a/src/tools/clippy/tests/ui/doc/issue_12795.rs b/src/tools/clippy/tests/ui/doc/issue_12795.rs
new file mode 100644
index 00000000000..6d94a07e303
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/issue_12795.rs
@@ -0,0 +1,9 @@
+#![warn(clippy::doc_markdown)]
+
+//! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe a_b((c)))
+//~^ ERROR: item in documentation is missing backticks
+//~| ERROR: item in documentation is missing backticks
+//~| ERROR: item in documentation is missing backticks
+//~| ERROR: item in documentation is missing backticks
+
+pub fn main() {}
diff --git a/src/tools/clippy/tests/ui/doc/issue_12795.stderr b/src/tools/clippy/tests/ui/doc/issue_12795.stderr
new file mode 100644
index 00000000000..5700145ec8f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/issue_12795.stderr
@@ -0,0 +1,48 @@
+error: item in documentation is missing backticks
+  --> tests/ui/doc/issue_12795.rs:3:20
+   |
+LL | //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe a_b((c)))
+   |                    ^^^^^^
+   |
+   = note: `-D clippy::doc-markdown` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::doc_markdown)]`
+help: try
+   |
+LL | //! A comment with `a_b(x)` and a_c in it and (a_b((c)) ) too and (maybe a_b((c)))
+   |                    ~~~~~~~~
+
+error: item in documentation is missing backticks
+  --> tests/ui/doc/issue_12795.rs:3:31
+   |
+LL | //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe a_b((c)))
+   |                               ^^^
+   |
+help: try
+   |
+LL | //! A comment with a_b(x) and `a_c` in it and (a_b((c)) ) too and (maybe a_b((c)))
+   |                               ~~~~~
+
+error: item in documentation is missing backticks
+  --> tests/ui/doc/issue_12795.rs:3:46
+   |
+LL | //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe a_b((c)))
+   |                                              ^^^^^^^^
+   |
+help: try
+   |
+LL | //! A comment with a_b(x) and a_c in it and (`a_b((c))` ) too and (maybe a_b((c)))
+   |                                              ~~~~~~~~~~
+
+error: item in documentation is missing backticks
+  --> tests/ui/doc/issue_12795.rs:3:72
+   |
+LL | //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe a_b((c)))
+   |                                                                        ^^^^^^^^
+   |
+help: try
+   |
+LL | //! A comment with a_b(x) and a_c in it and (a_b((c)) ) too and (maybe `a_b((c))`)
+   |                                                                        ~~~~~~~~~~
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/iter_on_empty_collections.fixed b/src/tools/clippy/tests/ui/iter_on_empty_collections.fixed
index 794629f240e..0f28b48d9ab 100644
--- a/src/tools/clippy/tests/ui/iter_on_empty_collections.fixed
+++ b/src/tools/clippy/tests/ui/iter_on_empty_collections.fixed
@@ -20,6 +20,28 @@ fn array() {
     };
 
     let _ = if false { ["test"].iter() } else { [].iter() };
+
+    let smth = Some(vec![1, 2, 3]);
+
+    // Don't trigger when the empty collection iter is relied upon for its concrete type
+    // But do trigger if it is just an iterator, despite being an argument to a method
+    for i in smth.as_ref().map_or([].iter(), |s| s.iter()).chain(std::iter::empty()) {
+        println!("{i}");
+    }
+
+    // Same as above, but for empty collection iters with extra layers
+    for i in smth.as_ref().map_or({ [].iter() }, |s| s.iter()) {
+        println!("{y}", y = i + 1);
+    }
+
+    // Same as above, but for regular function calls
+    for i in Option::map_or(smth.as_ref(), [].iter(), |s| s.iter()) {
+        println!("{i}");
+    }
+
+    // Same as above, but when there are no predicates that mention the collection iter type.
+    let mut iter = [34, 228, 35].iter();
+    let _ = std::mem::replace(&mut iter, [].iter());
 }
 
 macro_rules! in_macros {
diff --git a/src/tools/clippy/tests/ui/iter_on_empty_collections.rs b/src/tools/clippy/tests/ui/iter_on_empty_collections.rs
index a6461a702eb..702da514df7 100644
--- a/src/tools/clippy/tests/ui/iter_on_empty_collections.rs
+++ b/src/tools/clippy/tests/ui/iter_on_empty_collections.rs
@@ -20,6 +20,28 @@ fn array() {
     };
 
     let _ = if false { ["test"].iter() } else { [].iter() };
+
+    let smth = Some(vec![1, 2, 3]);
+
+    // Don't trigger when the empty collection iter is relied upon for its concrete type
+    // But do trigger if it is just an iterator, despite being an argument to a method
+    for i in smth.as_ref().map_or([].iter(), |s| s.iter()).chain([].iter()) {
+        println!("{i}");
+    }
+
+    // Same as above, but for empty collection iters with extra layers
+    for i in smth.as_ref().map_or({ [].iter() }, |s| s.iter()) {
+        println!("{y}", y = i + 1);
+    }
+
+    // Same as above, but for regular function calls
+    for i in Option::map_or(smth.as_ref(), [].iter(), |s| s.iter()) {
+        println!("{i}");
+    }
+
+    // Same as above, but when there are no predicates that mention the collection iter type.
+    let mut iter = [34, 228, 35].iter();
+    let _ = std::mem::replace(&mut iter, [].iter());
 }
 
 macro_rules! in_macros {
diff --git a/src/tools/clippy/tests/ui/iter_on_empty_collections.stderr b/src/tools/clippy/tests/ui/iter_on_empty_collections.stderr
index ade20ff26a0..da9caa6925b 100644
--- a/src/tools/clippy/tests/ui/iter_on_empty_collections.stderr
+++ b/src/tools/clippy/tests/ui/iter_on_empty_collections.stderr
@@ -37,5 +37,11 @@ error: `iter` call on an empty collection
 LL |     assert_eq!(None.iter().next(), Option::<&i32>::None);
    |                ^^^^^^^^^^^ help: try: `std::iter::empty()`
 
-error: aborting due to 6 previous errors
+error: `iter` call on an empty collection
+  --> tests/ui/iter_on_empty_collections.rs:28:66
+   |
+LL |     for i in smth.as_ref().map_or([].iter(), |s| s.iter()).chain([].iter()) {
+   |                                                                  ^^^^^^^^^ help: try: `std::iter::empty()`
+
+error: aborting due to 7 previous errors
 
diff --git a/src/tools/clippy/tests/ui/let_and_return.fixed b/src/tools/clippy/tests/ui/let_and_return.fixed
index 4187019e589..b68b41cdca2 100644
--- a/src/tools/clippy/tests/ui/let_and_return.fixed
+++ b/src/tools/clippy/tests/ui/let_and_return.fixed
@@ -210,4 +210,38 @@ fn issue9150() -> usize {
     x
 }
 
+fn issue12801() {
+    fn left_is_if() -> String {
+        
+        (if true { "a".to_string() } else { "b".to_string() } + "c")
+        //~^ ERROR: returning the result of a `let` binding from a block
+    }
+
+    fn no_par_needed() -> String {
+        
+        "c".to_string() + if true { "a" } else { "b" }
+        //~^ ERROR: returning the result of a `let` binding from a block
+    }
+
+    fn conjunctive_blocks() -> String {
+        
+        ({ "a".to_string() } + "b" + { "c" } + "d")
+        //~^ ERROR: returning the result of a `let` binding from a block
+    }
+
+    #[allow(clippy::overly_complex_bool_expr)]
+    fn other_ops() {
+        let _ = || {
+            
+            (if true { 2 } else { 3 } << 4)
+            //~^ ERROR: returning the result of a `let` binding from a block
+        };
+        let _ = || {
+            
+            ({ true } || { false } && { 2 <= 3 })
+            //~^ ERROR: returning the result of a `let` binding from a block
+        };
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/let_and_return.rs b/src/tools/clippy/tests/ui/let_and_return.rs
index 54444957b7d..6b9035f9428 100644
--- a/src/tools/clippy/tests/ui/let_and_return.rs
+++ b/src/tools/clippy/tests/ui/let_and_return.rs
@@ -210,4 +210,38 @@ fn issue9150() -> usize {
     x
 }
 
+fn issue12801() {
+    fn left_is_if() -> String {
+        let s = if true { "a".to_string() } else { "b".to_string() } + "c";
+        s
+        //~^ ERROR: returning the result of a `let` binding from a block
+    }
+
+    fn no_par_needed() -> String {
+        let s = "c".to_string() + if true { "a" } else { "b" };
+        s
+        //~^ ERROR: returning the result of a `let` binding from a block
+    }
+
+    fn conjunctive_blocks() -> String {
+        let s = { "a".to_string() } + "b" + { "c" } + "d";
+        s
+        //~^ ERROR: returning the result of a `let` binding from a block
+    }
+
+    #[allow(clippy::overly_complex_bool_expr)]
+    fn other_ops() {
+        let _ = || {
+            let s = if true { 2 } else { 3 } << 4;
+            s
+            //~^ ERROR: returning the result of a `let` binding from a block
+        };
+        let _ = || {
+            let s = { true } || { false } && { 2 <= 3 };
+            s
+            //~^ ERROR: returning the result of a `let` binding from a block
+        };
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/let_and_return.stderr b/src/tools/clippy/tests/ui/let_and_return.stderr
index ff5962ec196..75efa05d770 100644
--- a/src/tools/clippy/tests/ui/let_and_return.stderr
+++ b/src/tools/clippy/tests/ui/let_and_return.stderr
@@ -78,5 +78,75 @@ LL +                 E::B(x) => x,
 LL +             }) as _
    |
 
-error: aborting due to 5 previous errors
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:216:9
+   |
+LL |         let s = if true { "a".to_string() } else { "b".to_string() } + "c";
+   |         ------------------------------------------------------------------- unnecessary `let` binding
+LL |         s
+   |         ^
+   |
+help: return the expression directly
+   |
+LL ~         
+LL ~         (if true { "a".to_string() } else { "b".to_string() } + "c")
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:222:9
+   |
+LL |         let s = "c".to_string() + if true { "a" } else { "b" };
+   |         ------------------------------------------------------- unnecessary `let` binding
+LL |         s
+   |         ^
+   |
+help: return the expression directly
+   |
+LL ~         
+LL ~         "c".to_string() + if true { "a" } else { "b" }
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:228:9
+   |
+LL |         let s = { "a".to_string() } + "b" + { "c" } + "d";
+   |         -------------------------------------------------- unnecessary `let` binding
+LL |         s
+   |         ^
+   |
+help: return the expression directly
+   |
+LL ~         
+LL ~         ({ "a".to_string() } + "b" + { "c" } + "d")
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:236:13
+   |
+LL |             let s = if true { 2 } else { 3 } << 4;
+   |             -------------------------------------- unnecessary `let` binding
+LL |             s
+   |             ^
+   |
+help: return the expression directly
+   |
+LL ~             
+LL ~             (if true { 2 } else { 3 } << 4)
+   |
+
+error: returning the result of a `let` binding from a block
+  --> tests/ui/let_and_return.rs:241:13
+   |
+LL |             let s = { true } || { false } && { 2 <= 3 };
+   |             -------------------------------------------- unnecessary `let` binding
+LL |             s
+   |             ^
+   |
+help: return the expression directly
+   |
+LL ~             
+LL ~             ({ true } || { false } && { 2 <= 3 })
+   |
+
+error: aborting due to 10 previous errors
 
diff --git a/src/tools/clippy/tests/ui/many_single_char_names.rs b/src/tools/clippy/tests/ui/many_single_char_names.rs
index 2af45eaab8a..68578340d90 100644
--- a/src/tools/clippy/tests/ui/many_single_char_names.rs
+++ b/src/tools/clippy/tests/ui/many_single_char_names.rs
@@ -1,5 +1,3 @@
-//@compile-flags: -Zdeduplicate-diagnostics=yes
-
 #![allow(clippy::too_many_arguments, clippy::diverging_sub_expression)]
 #![warn(clippy::many_single_char_names)]
 
diff --git a/src/tools/clippy/tests/ui/many_single_char_names.stderr b/src/tools/clippy/tests/ui/many_single_char_names.stderr
index 3b2460b5c07..131836ef7c8 100644
--- a/src/tools/clippy/tests/ui/many_single_char_names.stderr
+++ b/src/tools/clippy/tests/ui/many_single_char_names.stderr
@@ -1,5 +1,5 @@
 error: 5 bindings with single-character names in scope
-  --> tests/ui/many_single_char_names.rs:7:9
+  --> tests/ui/many_single_char_names.rs:5:9
    |
 LL |     let a: i32;
    |         ^
@@ -14,7 +14,7 @@ LL |             let e: i32;
    = help: to override `-D warnings` add `#[allow(clippy::many_single_char_names)]`
 
 error: 6 bindings with single-character names in scope
-  --> tests/ui/many_single_char_names.rs:7:9
+  --> tests/ui/many_single_char_names.rs:5:9
    |
 LL |     let a: i32;
    |         ^
@@ -28,7 +28,7 @@ LL |             let f: i32;
    |                 ^
 
 error: 5 bindings with single-character names in scope
-  --> tests/ui/many_single_char_names.rs:7:9
+  --> tests/ui/many_single_char_names.rs:5:9
    |
 LL |     let a: i32;
    |         ^
@@ -40,13 +40,13 @@ LL |             e => panic!(),
    |             ^
 
 error: 8 bindings with single-character names in scope
-  --> tests/ui/many_single_char_names.rs:36:13
+  --> tests/ui/many_single_char_names.rs:34:13
    |
 LL | fn bindings(a: i32, b: i32, c: i32, d: i32, e: i32, f: i32, g: i32, h: i32) {}
    |             ^       ^       ^       ^       ^       ^       ^       ^
 
 error: 8 bindings with single-character names in scope
-  --> tests/ui/many_single_char_names.rs:40:10
+  --> tests/ui/many_single_char_names.rs:38:10
    |
 LL |     let (a, b, c, d, e, f, g, h): (bool, bool, bool, bool, bool, bool, bool, bool) = unimplemented!();
    |          ^  ^  ^  ^  ^  ^  ^  ^
diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed
index 2575f2449e1..a9271cb399d 100644
--- a/src/tools/clippy/tests/ui/needless_return.fixed
+++ b/src/tools/clippy/tests/ui/needless_return.fixed
@@ -323,4 +323,8 @@ fn allow_works() -> i32 {
     }
 }
 
+fn conjunctive_blocks() -> String {
+    ({ "a".to_string() } + "b" + { "c" })
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs
index 04f21834d88..dc888bf667f 100644
--- a/src/tools/clippy/tests/ui/needless_return.rs
+++ b/src/tools/clippy/tests/ui/needless_return.rs
@@ -333,4 +333,8 @@ fn allow_works() -> i32 {
     }
 }
 
+fn conjunctive_blocks() -> String {
+    return { "a".to_string() } + "b" + { "c" };
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr
index 758ff6d985c..bf5a89d8b75 100644
--- a/src/tools/clippy/tests/ui/needless_return.stderr
+++ b/src/tools/clippy/tests/ui/needless_return.stderr
@@ -653,5 +653,17 @@ LL -         return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4
 LL +         (if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 })
    |
 
-error: aborting due to 52 previous errors
+error: unneeded `return` statement
+  --> tests/ui/needless_return.rs:337:5
+   |
+LL |     return { "a".to_string() } + "b" + { "c" };
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove `return` and wrap the sequence with parentheses
+   |
+LL -     return { "a".to_string() } + "b" + { "c" };
+LL +     ({ "a".to_string() } + "b" + { "c" })
+   |
+
+error: aborting due to 53 previous errors
 
diff --git a/src/tools/clippy/tests/ui/numbered_fields.fixed b/src/tools/clippy/tests/ui/numbered_fields.fixed
index dc88081ba0a..108520eed38 100644
--- a/src/tools/clippy/tests/ui/numbered_fields.fixed
+++ b/src/tools/clippy/tests/ui/numbered_fields.fixed
@@ -34,4 +34,9 @@ fn main() {
 
     // Aliases can't be tuple constructed #8638
     let _ = Alias { 0: 0, 1: 1, 2: 2 };
+
+    // Issue #12367
+    struct TupleStructVec(Vec<usize>);
+
+    let _ = TupleStructVec(vec![0, 1, 2, 3]);
 }
diff --git a/src/tools/clippy/tests/ui/numbered_fields.rs b/src/tools/clippy/tests/ui/numbered_fields.rs
index e8fa652e3c1..c718661a682 100644
--- a/src/tools/clippy/tests/ui/numbered_fields.rs
+++ b/src/tools/clippy/tests/ui/numbered_fields.rs
@@ -42,4 +42,9 @@ fn main() {
 
     // Aliases can't be tuple constructed #8638
     let _ = Alias { 0: 0, 1: 1, 2: 2 };
+
+    // Issue #12367
+    struct TupleStructVec(Vec<usize>);
+
+    let _ = TupleStructVec { 0: vec![0, 1, 2, 3] };
 }
diff --git a/src/tools/clippy/tests/ui/numbered_fields.stderr b/src/tools/clippy/tests/ui/numbered_fields.stderr
index 96426cab1e6..9d3f59cd376 100644
--- a/src/tools/clippy/tests/ui/numbered_fields.stderr
+++ b/src/tools/clippy/tests/ui/numbered_fields.stderr
@@ -23,5 +23,11 @@ LL | |         1: 3u32,
 LL | |     };
    | |_____^ help: try: `TupleStruct(1u32, 3u32, 2u8)`
 
-error: aborting due to 2 previous errors
+error: used a field initializer for a tuple struct
+  --> tests/ui/numbered_fields.rs:49:13
+   |
+LL |     let _ = TupleStructVec { 0: vec![0, 1, 2, 3] };
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `TupleStructVec(vec![0, 1, 2, 3])`
+
+error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs
index 7fc89bb9538..8ee15440ccf 100644
--- a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs
+++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs
@@ -274,11 +274,20 @@ fn should_trigger_lint_for_tuple_in_scrutinee() {
             },
             (_, _, _) => {},
         };
+    }
+}
+
+// Should not trigger lint since `String::as_str` returns a reference (i.e., `&str`)
+// to the locked data (i.e., the `String`) and it is not surprising that matching such
+// a reference needs to keep the data locked until the end of the match block.
+fn should_not_trigger_lint_for_string_as_str() {
+    let mutex1 = Mutex::new(StateWithField { s: "one".to_owned() });
 
+    {
+        let mutex2 = Mutex::new(StateWithField { s: "two".to_owned() });
         let mutex3 = Mutex::new(StateWithField { s: "three".to_owned() });
+
         match mutex3.lock().unwrap().s.as_str() {
-            //~^ ERROR: temporary with significant `Drop` in `match` scrutinee will live until
-            //~| NOTE: this might lead to deadlocks or other unexpected behavior
             "three" => {
                 println!("started");
                 mutex1.lock().unwrap().s.len();
@@ -289,8 +298,6 @@ fn should_trigger_lint_for_tuple_in_scrutinee() {
         };
 
         match (true, mutex3.lock().unwrap().s.as_str()) {
-            //~^ ERROR: temporary with significant `Drop` in `match` scrutinee will live until
-            //~| NOTE: this might lead to deadlocks or other unexpected behavior
             (_, "three") => {
                 println!("started");
                 mutex1.lock().unwrap().s.len();
@@ -514,16 +521,15 @@ impl StateStringWithBoxedMutexGuard {
     }
 }
 
-fn should_trigger_lint_for_boxed_mutex_guard_holding_string() {
+fn should_not_trigger_lint_for_string_ref() {
     let s = StateStringWithBoxedMutexGuard::new();
 
     let matcher = String::from("A String");
 
-    // Should trigger lint because a temporary Box holding a type with a significant drop in a match
-    // scrutinee may have a potentially surprising lifetime.
+    // Should not trigger lint because the second `deref` returns a string reference (`&String`).
+    // So it is not surprising that matching the reference implies that the lock needs to be held
+    // until the end of the block.
     match s.lock().deref().deref() {
-        //~^ ERROR: temporary with significant `Drop` in `match` scrutinee will live until the
-        //~| NOTE: this might lead to deadlocks or other unexpected behavior
         matcher => println!("Value is {}", s.lock().deref()),
         _ => println!("Value was not a match"),
     };
@@ -639,13 +645,12 @@ fn should_trigger_lint_for_non_ref_move_and_clone_suggestion() {
     };
 }
 
-fn should_trigger_lint_for_read_write_lock_for_loop() {
-    // For-in loops desugar to match expressions and are prone to the type of deadlock this lint is
-    // designed to look for.
+fn should_not_trigger_lint_for_read_write_lock_for_loop() {
     let rwlock = RwLock::<Vec<String>>::new(vec!["1".to_string()]);
+    // Should not trigger lint. Since we're iterating over the data, it's obvious that the lock
+    // has to be held until the iteration finishes.
+    // https://github.com/rust-lang/rust-clippy/issues/8987
     for s in rwlock.read().unwrap().iter() {
-        //~^ ERROR: temporary with significant `Drop` in `for` loop condition will live until
-        //~| NOTE: this might lead to deadlocks or other unexpected behavior
         println!("{}", s);
     }
 }
@@ -731,4 +736,69 @@ fn should_not_trigger_for_significant_drop_ref() {
     };
 }
 
+struct Foo<'a>(&'a Vec<u32>);
+
+impl<'a> Foo<'a> {
+    fn copy_old_lifetime(&self) -> &'a Vec<u32> {
+        self.0
+    }
+
+    fn reborrow_new_lifetime(&self) -> &Vec<u32> {
+        self.0
+    }
+}
+
+fn should_trigger_lint_if_and_only_if_lifetime_is_irrelevant() {
+    let vec = Vec::new();
+    let mutex = Mutex::new(Foo(&vec));
+
+    // Should trigger lint even if `copy_old_lifetime()` has a lifetime, as the lifetime of
+    // `&vec` is unrelated to the temporary with significant drop (i.e., the `MutexGuard`).
+    for val in mutex.lock().unwrap().copy_old_lifetime() {
+        //~^ ERROR: temporary with significant `Drop` in `for` loop condition will live until the
+        //~| NOTE: this might lead to deadlocks or other unexpected behavior
+        println!("{}", val);
+    }
+
+    // Should not trigger lint because `reborrow_new_lifetime()` has a lifetime and the
+    // lifetime is related to the temporary with significant drop (i.e., the `MutexGuard`).
+    for val in mutex.lock().unwrap().reborrow_new_lifetime() {
+        println!("{}", val);
+    }
+}
+
+fn should_not_trigger_lint_for_complex_lifetime() {
+    let mutex = Mutex::new(vec!["hello".to_owned()]);
+    let string = "world".to_owned();
+
+    // Should not trigger lint due to the relevant lifetime.
+    for c in mutex.lock().unwrap().first().unwrap().chars() {
+        println!("{}", c);
+    }
+
+    // Should trigger lint due to the irrelevant lifetime.
+    //
+    // FIXME: The lifetime is too complex to analyze. In order to avoid false positives, we do not
+    // trigger lint.
+    for c in mutex.lock().unwrap().first().map(|_| &string).unwrap().chars() {
+        println!("{}", c);
+    }
+}
+
+fn should_not_trigger_lint_with_explicit_drop() {
+    let mutex = Mutex::new(vec![1]);
+
+    // Should not trigger lint since the drop explicitly happens.
+    for val in [drop(mutex.lock().unwrap()), ()] {
+        println!("{:?}", val);
+    }
+
+    // Should trigger lint if there is no explicit drop.
+    for val in [mutex.lock().unwrap()[0], 2] {
+        //~^ ERROR: temporary with significant `Drop` in `for` loop condition will live until the
+        //~| NOTE: this might lead to deadlocks or other unexpected behavior
+        println!("{:?}", val);
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr
index 811bb065527..4a483e79d8a 100644
--- a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr
+++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr
@@ -160,45 +160,53 @@ LL ~         match (mutex1.lock().unwrap().s.len(), true, value) {
    |
 
 error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
-  --> tests/ui/significant_drop_in_scrutinee.rs:279:15
+  --> tests/ui/significant_drop_in_scrutinee.rs:319:11
    |
-LL |         match mutex3.lock().unwrap().s.as_str() {
-   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     match mutex.lock().unwrap().s.len() > 1 {
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ...
-LL |                 mutex1.lock().unwrap().s.len();
-   |                 ---------------------- another value with significant `Drop` created here
-LL |                 mutex2.lock().unwrap().s.len();
-   |                 ---------------------- another value with significant `Drop` created here
+LL |             mutex.lock().unwrap().s.len();
+   |             --------------------- another value with significant `Drop` created here
 ...
-LL |         };
-   |          - temporary lives until here
+LL |     };
+   |      - temporary lives until here
    |
    = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+   |
+LL ~     let value = mutex.lock().unwrap().s.len();
+LL ~     match value > 1 {
+   |
 
 error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
-  --> tests/ui/significant_drop_in_scrutinee.rs:291:22
+  --> tests/ui/significant_drop_in_scrutinee.rs:328:15
    |
-LL |         match (true, mutex3.lock().unwrap().s.as_str()) {
-   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     match 1 < mutex.lock().unwrap().s.len() {
+   |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ...
-LL |                 mutex1.lock().unwrap().s.len();
-   |                 ---------------------- another value with significant `Drop` created here
-LL |                 mutex2.lock().unwrap().s.len();
-   |                 ---------------------- another value with significant `Drop` created here
+LL |             mutex.lock().unwrap().s.len();
+   |             --------------------- another value with significant `Drop` created here
 ...
-LL |         };
-   |          - temporary lives until here
+LL |     };
+   |      - temporary lives until here
    |
    = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+   |
+LL ~     let value = mutex.lock().unwrap().s.len();
+LL ~     match 1 < value {
+   |
 
 error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
-  --> tests/ui/significant_drop_in_scrutinee.rs:312:11
+  --> tests/ui/significant_drop_in_scrutinee.rs:348:11
    |
-LL |     match mutex.lock().unwrap().s.len() > 1 {
-   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() {
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ...
-LL |             mutex.lock().unwrap().s.len();
-   |             --------------------- another value with significant `Drop` created here
+LL |                 mutex1.lock().unwrap().s.len(),
+   |                 ---------------------- another value with significant `Drop` created here
+LL |                 mutex2.lock().unwrap().s.len()
+   |                 ---------------------- another value with significant `Drop` created here
 ...
 LL |     };
    |      - temporary lives until here
@@ -206,18 +214,20 @@ LL |     };
    = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
-LL ~     let value = mutex.lock().unwrap().s.len() > 1;
-LL ~     match value {
+LL ~     let value = mutex1.lock().unwrap().s.len();
+LL ~     match value < mutex2.lock().unwrap().s.len() {
    |
 
 error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
-  --> tests/ui/significant_drop_in_scrutinee.rs:321:11
+  --> tests/ui/significant_drop_in_scrutinee.rs:348:44
    |
-LL |     match 1 < mutex.lock().unwrap().s.len() {
-   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() {
+   |                                            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ...
-LL |             mutex.lock().unwrap().s.len();
-   |             --------------------- another value with significant `Drop` created here
+LL |                 mutex1.lock().unwrap().s.len(),
+   |                 ---------------------- another value with significant `Drop` created here
+LL |                 mutex2.lock().unwrap().s.len()
+   |                 ---------------------- another value with significant `Drop` created here
 ...
 LL |     };
    |      - temporary lives until here
@@ -225,15 +235,15 @@ LL |     };
    = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
-LL ~     let value = 1 < mutex.lock().unwrap().s.len();
-LL ~     match value {
+LL ~     let value = mutex2.lock().unwrap().s.len();
+LL ~     match mutex1.lock().unwrap().s.len() < value {
    |
 
 error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
-  --> tests/ui/significant_drop_in_scrutinee.rs:341:11
+  --> tests/ui/significant_drop_in_scrutinee.rs:361:11
    |
-LL |     match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() {
-   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() {
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ...
 LL |                 mutex1.lock().unwrap().s.len(),
    |                 ---------------------- another value with significant `Drop` created here
@@ -246,15 +256,15 @@ LL |     };
    = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
-LL ~     let value = mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len();
-LL ~     match value {
+LL ~     let value = mutex1.lock().unwrap().s.len();
+LL ~     match value >= mutex2.lock().unwrap().s.len() {
    |
 
 error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
-  --> tests/ui/significant_drop_in_scrutinee.rs:354:11
+  --> tests/ui/significant_drop_in_scrutinee.rs:361:45
    |
 LL |     match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() {
-   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                                             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 ...
 LL |                 mutex1.lock().unwrap().s.len(),
    |                 ---------------------- another value with significant `Drop` created here
@@ -267,15 +277,15 @@ LL |     };
    = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
-LL ~     let value = mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len();
-LL ~     match value {
+LL ~     let value = mutex2.lock().unwrap().s.len();
+LL ~     match mutex1.lock().unwrap().s.len() >= value {
    |
 
 error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
-  --> tests/ui/significant_drop_in_scrutinee.rs:391:11
+  --> tests/ui/significant_drop_in_scrutinee.rs:398:11
    |
 LL |     match get_mutex_guard().s.len() > 1 {
-   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^
 ...
 LL |             mutex1.lock().unwrap().s.len();
    |             ---------------------- another value with significant `Drop` created here
@@ -286,12 +296,12 @@ LL |     };
    = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
-LL ~     let value = get_mutex_guard().s.len() > 1;
-LL ~     match value {
+LL ~     let value = get_mutex_guard().s.len();
+LL ~     match value > 1 {
    |
 
 error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
-  --> tests/ui/significant_drop_in_scrutinee.rs:410:11
+  --> tests/ui/significant_drop_in_scrutinee.rs:417:11
    |
 LL |       match match i {
    |  ___________^
@@ -299,9 +309,9 @@ LL | |
 LL | |
 LL | |         100 => mutex1.lock().unwrap(),
 ...  |
+LL | |     .s
 LL | |     .len()
-LL | |         > 1
-   | |___________^
+   | |__________^
 ...
 LL |               mutex1.lock().unwrap().s.len();
    |               ---------------------- another value with significant `Drop` created here
@@ -319,13 +329,12 @@ LL +         100 => mutex1.lock().unwrap(),
 LL +         _ => mutex2.lock().unwrap(),
 LL +     }
 LL +     .s
-LL +     .len()
-LL +         > 1;
+LL +     .len();
 LL ~     match value
    |
 
 error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
-  --> tests/ui/significant_drop_in_scrutinee.rs:438:11
+  --> tests/ui/significant_drop_in_scrutinee.rs:445:11
    |
 LL |       match if i > 1 {
    |  ___________^
@@ -333,9 +342,9 @@ LL | |
 LL | |
 LL | |         mutex1.lock().unwrap()
 ...  |
+LL | |     .s
 LL | |     .len()
-LL | |         > 1
-   | |___________^
+   | |__________^
 ...
 LL |               mutex1.lock().unwrap().s.len();
    |               ---------------------- another value with significant `Drop` created here
@@ -354,13 +363,12 @@ LL +     } else {
 LL +         mutex2.lock().unwrap()
 LL +     }
 LL +     .s
-LL +     .len()
-LL +         > 1;
+LL +     .len();
 LL ~     match value
    |
 
 error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
-  --> tests/ui/significant_drop_in_scrutinee.rs:494:11
+  --> tests/ui/significant_drop_in_scrutinee.rs:501:11
    |
 LL |     match s.lock().deref().deref() {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^
@@ -374,25 +382,11 @@ LL |     };
 help: try moving the temporary above the match and create a copy
    |
 LL ~     let value = *s.lock().deref().deref();
-LL ~     match value {
+LL ~     match (&value) {
    |
 
 error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
-  --> tests/ui/significant_drop_in_scrutinee.rs:524:11
-   |
-LL |     match s.lock().deref().deref() {
-   |           ^^^^^^^^^^^^^^^^^^^^^^^^
-...
-LL |         matcher => println!("Value is {}", s.lock().deref()),
-   |                                            -------- another value with significant `Drop` created here
-LL |         _ => println!("Value was not a match"),
-LL |     };
-   |      - temporary lives until here
-   |
-   = note: this might lead to deadlocks or other unexpected behavior
-
-error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
-  --> tests/ui/significant_drop_in_scrutinee.rs:545:11
+  --> tests/ui/significant_drop_in_scrutinee.rs:551:11
    |
 LL |     match mutex.lock().unwrap().i = i {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -411,10 +405,10 @@ LL ~     match () {
    |
 
 error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
-  --> tests/ui/significant_drop_in_scrutinee.rs:553:11
+  --> tests/ui/significant_drop_in_scrutinee.rs:559:15
    |
 LL |     match i = mutex.lock().unwrap().i {
-   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |               ^^^^^^^^^^^^^^^^^^^^^^^
 ...
 LL |             println!("{}", mutex.lock().unwrap().i);
    |                            --------------------- another value with significant `Drop` created here
@@ -425,12 +419,12 @@ LL |     };
    = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
-LL ~     i = mutex.lock().unwrap().i;
-LL ~     match () {
+LL ~     let value = mutex.lock().unwrap().i;
+LL ~     match i = value {
    |
 
 error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
-  --> tests/ui/significant_drop_in_scrutinee.rs:561:11
+  --> tests/ui/significant_drop_in_scrutinee.rs:567:11
    |
 LL |     match mutex.lock().unwrap().i += 1 {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -449,10 +443,10 @@ LL ~     match () {
    |
 
 error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
-  --> tests/ui/significant_drop_in_scrutinee.rs:569:11
+  --> tests/ui/significant_drop_in_scrutinee.rs:575:16
    |
 LL |     match i += mutex.lock().unwrap().i {
-   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                ^^^^^^^^^^^^^^^^^^^^^^^
 ...
 LL |             println!("{}", mutex.lock().unwrap().i);
    |                            --------------------- another value with significant `Drop` created here
@@ -463,12 +457,12 @@ LL |     };
    = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
-LL ~     i += mutex.lock().unwrap().i;
-LL ~     match () {
+LL ~     let value = mutex.lock().unwrap().i;
+LL ~     match i += value {
    |
 
 error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
-  --> tests/ui/significant_drop_in_scrutinee.rs:634:11
+  --> tests/ui/significant_drop_in_scrutinee.rs:640:11
    |
 LL |     match rwlock.read().unwrap().to_number() {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -477,20 +471,14 @@ LL |     };
    |      - temporary lives until here
    |
    = note: this might lead to deadlocks or other unexpected behavior
-
-error: temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression
-  --> tests/ui/significant_drop_in_scrutinee.rs:646:14
+help: try moving the temporary above the match
    |
-LL |     for s in rwlock.read().unwrap().iter() {
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-...
-LL |     }
-   |      - temporary lives until here
+LL ~     let value = rwlock.read().unwrap().to_number();
+LL ~     match value {
    |
-   = note: this might lead to deadlocks or other unexpected behavior
 
 error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
-  --> tests/ui/significant_drop_in_scrutinee.rs:663:11
+  --> tests/ui/significant_drop_in_scrutinee.rs:668:11
    |
 LL |     match mutex.lock().unwrap().foo() {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -506,7 +494,7 @@ LL ~     match value {
    |
 
 error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
-  --> tests/ui/significant_drop_in_scrutinee.rs:726:11
+  --> tests/ui/significant_drop_in_scrutinee.rs:731:11
    |
 LL |     match guard.take().len() {
    |           ^^^^^^^^^^^^^^^^^^
@@ -521,5 +509,37 @@ LL ~     let value = guard.take().len();
 LL ~     match value {
    |
 
+error: temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression
+  --> tests/ui/significant_drop_in_scrutinee.rs:757:16
+   |
+LL |     for val in mutex.lock().unwrap().copy_old_lifetime() {
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL |     }
+   |      - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+   |
+LL ~     let value = mutex.lock().unwrap().copy_old_lifetime();
+LL ~     for val in value {
+   |
+
+error: temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression
+  --> tests/ui/significant_drop_in_scrutinee.rs:797:17
+   |
+LL |     for val in [mutex.lock().unwrap()[0], 2] {
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL |     }
+   |      - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+   |
+LL ~     let value = mutex.lock().unwrap()[0];
+LL ~     for val in [value, 2] {
+   |
+
 error: aborting due to 27 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed
index 2c582c90ba8..dc5e163ff04 100644
--- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed
@@ -170,3 +170,32 @@ fn check_mut_iteratee_and_modify_inner_variable() {
         }
     }
 }
+
+mod issue_12821 {
+    fn foo() {
+        let v: Vec<_> = "hello".chars().collect();
+        for c in v.iter() {
+            //~^ ERROR: unnecessary use of `cloned`
+            println!("{c}"); // should not suggest to remove `&`
+        }
+    }
+
+    fn bar() {
+        let v: Vec<_> = "hello".chars().collect();
+        for c in v.iter() {
+            //~^ ERROR: unnecessary use of `cloned`
+            let ref_c = c; //~ HELP: remove any references to the binding
+            println!("{ref_c}");
+        }
+    }
+
+    fn baz() {
+        let v: Vec<_> = "hello".chars().enumerate().collect();
+        for (i, c) in v.iter() {
+            //~^ ERROR: unnecessary use of `cloned`
+            let ref_c = c; //~ HELP: remove any references to the binding
+            let ref_i = i;
+            println!("{i} {ref_c}"); // should not suggest to remove `&` from `i`
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs
index a28ccd1efef..8f797ac717f 100644
--- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs
@@ -170,3 +170,32 @@ fn check_mut_iteratee_and_modify_inner_variable() {
         }
     }
 }
+
+mod issue_12821 {
+    fn foo() {
+        let v: Vec<_> = "hello".chars().collect();
+        for c in v.iter().cloned() {
+            //~^ ERROR: unnecessary use of `cloned`
+            println!("{c}"); // should not suggest to remove `&`
+        }
+    }
+
+    fn bar() {
+        let v: Vec<_> = "hello".chars().collect();
+        for c in v.iter().cloned() {
+            //~^ ERROR: unnecessary use of `cloned`
+            let ref_c = &c; //~ HELP: remove any references to the binding
+            println!("{ref_c}");
+        }
+    }
+
+    fn baz() {
+        let v: Vec<_> = "hello".chars().enumerate().collect();
+        for (i, c) in v.iter().cloned() {
+            //~^ ERROR: unnecessary use of `cloned`
+            let ref_c = &c; //~ HELP: remove any references to the binding
+            let ref_i = &i;
+            println!("{i} {ref_c}"); // should not suggest to remove `&` from `i`
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr
index fb98cfddc26..0bdb37a521f 100644
--- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr
@@ -10,7 +10,7 @@ help: use
    |
 LL |     for (t, path) in files {
    |                      ~~~~~
-help: remove this `&`
+help: remove any references to the binding
    |
 LL -         let other = match get_file_path(&t) {
 LL +         let other = match get_file_path(t) {
@@ -26,11 +26,49 @@ help: use
    |
 LL |     for (t, path) in files.iter() {
    |                      ~~~~~~~~~~~~
-help: remove this `&`
+help: remove any references to the binding
    |
 LL -         let other = match get_file_path(&t) {
 LL +         let other = match get_file_path(t) {
    |
 
-error: aborting due to 2 previous errors
+error: unnecessary use of `cloned`
+  --> tests/ui/unnecessary_iter_cloned.rs:177:18
+   |
+LL |         for c in v.iter().cloned() {
+   |                  ^^^^^^^^^^^^^^^^^ help: use: `v.iter()`
+
+error: unnecessary use of `cloned`
+  --> tests/ui/unnecessary_iter_cloned.rs:185:18
+   |
+LL |         for c in v.iter().cloned() {
+   |                  ^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL |         for c in v.iter() {
+   |                  ~~~~~~~~
+help: remove any references to the binding
+   |
+LL -             let ref_c = &c;
+LL +             let ref_c = c;
+   |
+
+error: unnecessary use of `cloned`
+  --> tests/ui/unnecessary_iter_cloned.rs:194:23
+   |
+LL |         for (i, c) in v.iter().cloned() {
+   |                       ^^^^^^^^^^^^^^^^^
+   |
+help: use
+   |
+LL |         for (i, c) in v.iter() {
+   |                       ~~~~~~~~
+help: remove any references to the binding
+   |
+LL ~             let ref_c = c;
+LL ~             let ref_i = i;
+   |
+
+error: aborting due to 5 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
index 5475df9c7b9..2829f3cd6e9 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
@@ -487,7 +487,7 @@ help: use
    |
 LL |     for t in file_types {
    |              ~~~~~~~~~~
-help: remove this `&`
+help: remove any references to the binding
    |
 LL -         let path = match get_file_path(&t) {
 LL +         let path = match get_file_path(t) {
diff --git a/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs
index 70dcaa3afa4..5187e079042 100644
--- a/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs
+++ b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.rs
@@ -1,3 +1,4 @@
+#![feature(lint_reasons)]
 #![warn(clippy::unsafe_derive_deserialize)]
 #![allow(unused, clippy::missing_safety_doc)]
 
@@ -71,4 +72,14 @@ impl G {
     }
 }
 
+// Check that we honor the `expect` attribute on the ADT
+#[expect(clippy::unsafe_derive_deserialize)]
+#[derive(Deserialize)]
+pub struct H;
+impl H {
+    pub fn unsafe_block(&self) {
+        unsafe {}
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr
index f2d4429f707..06719f23d57 100644
--- a/src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr
+++ b/src/tools/clippy/tests/ui/unsafe_derive_deserialize.stderr
@@ -1,5 +1,5 @@
 error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
-  --> tests/ui/unsafe_derive_deserialize.rs:8:10
+  --> tests/ui/unsafe_derive_deserialize.rs:9:10
    |
 LL | #[derive(Deserialize)]
    |          ^^^^^^^^^^^
@@ -10,7 +10,7 @@ LL | #[derive(Deserialize)]
    = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
-  --> tests/ui/unsafe_derive_deserialize.rs:17:10
+  --> tests/ui/unsafe_derive_deserialize.rs:18:10
    |
 LL | #[derive(Deserialize)]
    |          ^^^^^^^^^^^
@@ -19,7 +19,7 @@ LL | #[derive(Deserialize)]
    = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
-  --> tests/ui/unsafe_derive_deserialize.rs:24:10
+  --> tests/ui/unsafe_derive_deserialize.rs:25:10
    |
 LL | #[derive(Deserialize)]
    |          ^^^^^^^^^^^
@@ -28,7 +28,7 @@ LL | #[derive(Deserialize)]
    = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: you are deriving `serde::Deserialize` on a type that has methods using `unsafe`
-  --> tests/ui/unsafe_derive_deserialize.rs:33:10
+  --> tests/ui/unsafe_derive_deserialize.rs:34:10
    |
 LL | #[derive(Deserialize)]
    |          ^^^^^^^^^^^