about summary refs log tree commit diff
diff options
context:
space:
mode:
-rwxr-xr-xsrc/tools/clippy/.github/driver.sh19
-rw-r--r--src/tools/clippy/.github/workflows/clippy.yml6
-rw-r--r--src/tools/clippy/.github/workflows/clippy_bors.yml4
-rw-r--r--src/tools/clippy/CHANGELOG.md7
-rw-r--r--src/tools/clippy/book/src/lint_configuration.md42
-rw-r--r--src/tools/clippy/clippy_config/src/conf.rs31
-rw-r--r--src/tools/clippy/clippy_config/src/msrvs.rs3
-rw-r--r--src/tools/clippy/clippy_dev/Cargo.toml3
-rw-r--r--src/tools/clippy/clippy_dev/src/main.rs570
-rw-r--r--src/tools/clippy/clippy_dev/src/new_lint.rs14
-rw-r--r--src/tools/clippy/clippy_dev/src/serve.rs2
-rw-r--r--src/tools/clippy/clippy_dev/src/update_lints.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/assigning_clones.rs37
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/mod.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs49
-rw-r--r--src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs95
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/missing_headers.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/mod.rs129
-rw-r--r--src/tools/clippy/clippy_lints/src/escape.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/explicit_write.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/format.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/format_args.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/format_impl.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/from_str_radix_10.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/mod.rs59
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs110
-rw-r--r--src/tools/clippy/clippy_lints/src/future_not_send.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs55
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/mod.rs36
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/while_float.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs256
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_clamp.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs37
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs82
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_filter.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_iter_cloned.rs53
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/utils.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs51
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_continue.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_late_init.rs69
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/no_effect.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/panic_unimplemented.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/strings.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unwrap.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs27
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs14
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs18
-rw-r--r--src/tools/clippy/clippy_utils/src/macros.rs78
-rw-r--r--src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs82
-rw-r--r--src/tools/clippy/clippy_utils/src/source.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/sugg.rs3
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs15
-rw-r--r--src/tools/clippy/clippy_utils/src/usage.rs10
-rw-r--r--src/tools/clippy/lintcheck/Cargo.toml2
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr30
-rw-r--r--src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml8
-rw-r--r--src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs260
-rw-r--r--src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr187
-rw-r--r--src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.rs15
-rw-r--r--src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr17
-rw-r--r--src/tools/clippy/tests/ui-toml/panic/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/panic/panic.rs54
-rw-r--r--src/tools/clippy/tests/ui-toml/panic/panic.stderr11
-rw-r--r--src/tools/clippy/tests/ui-toml/renamed_function_params/default/clippy.toml2
-rw-r--r--src/tools/clippy/tests/ui-toml/renamed_function_params/extend/clippy.toml2
-rw-r--r--src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.default.stderr46
-rw-r--r--src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.extend.stderr34
-rw-r--r--src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.rs110
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs6
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr9
-rw-r--r--src/tools/clippy/tests/ui/assigning_clones.fixed26
-rw-r--r--src/tools/clippy/tests/ui/assigning_clones.rs26
-rw-r--r--src/tools/clippy/tests/ui/assigning_clones.stderr46
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.fixed47
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.rs47
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.stderr76
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed77
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_lazy_list.rs77
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr136
-rw-r--r--src/tools/clippy/tests/ui/duplicated_attributes.rs6
-rw-r--r--src/tools/clippy/tests/ui/from_str_radix_10.fixed11
-rw-r--r--src/tools/clippy/tests/ui/from_str_radix_10.rs11
-rw-r--r--src/tools/clippy/tests/ui/from_str_radix_10.stderr16
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms.stderr122
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms2.fixed241
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms2.rs5
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms2.stderr257
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed61
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs1
-rw-r--r--src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr18
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs18
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs28
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr30
-rw-r--r--src/tools/clippy/tests/ui/missing_panics_doc.rs8
-rw-r--r--src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed42
-rw-r--r--src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs36
-rw-r--r--src/tools/clippy/tests/ui/needless_borrows_for_generic_args.stderr26
-rw-r--r--src/tools/clippy/tests/ui/needless_late_init.stderr177
-rw-r--r--src/tools/clippy/tests/ui/no_effect.rs13
-rw-r--r--src/tools/clippy/tests/ui/no_effect.stderr68
-rw-r--r--src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs56
-rw-r--r--src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr28
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed32
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs32
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr4
-rw-r--r--src/tools/clippy/tests/ui/useless_attribute.fixed43
-rw-r--r--src/tools/clippy/tests/ui/useless_attribute.rs43
-rw-r--r--src/tools/clippy/tests/ui/while_float.rs14
-rw-r--r--src/tools/clippy/tests/ui/while_float.stderr20
-rw-r--r--src/tools/clippy/util/gh-pages/script.js2
124 files changed, 4025 insertions, 1120 deletions
diff --git a/src/tools/clippy/.github/driver.sh b/src/tools/clippy/.github/driver.sh
index 2eafdd0fbc8..09202b1878b 100755
--- a/src/tools/clippy/.github/driver.sh
+++ b/src/tools/clippy/.github/driver.sh
@@ -2,15 +2,18 @@
 
 set -ex
 
+sysroot="$(rustc --print sysroot)"
+case $OS in
+    Linux) export LD_LIBRARY_PATH="$sysroot/lib" ;;
+    macOS) export DYLD_FALLBACK_LIBRARY_PATH="$sysroot/lib" ;;
+    Windows) export PATH="$(cygpath "$sysroot")/bin:$PATH" ;;
+    *) exit 1
+esac
+
 # Check sysroot handling
-sysroot=$(./target/debug/clippy-driver --print sysroot)
-test "$sysroot" = "$(rustc --print sysroot)"
-
-if [[ ${OS} == "Windows" ]]; then
-	desired_sysroot=C:/tmp
-else
-	desired_sysroot=/tmp
-fi
+test "$(./target/debug/clippy-driver --print sysroot)" = "$sysroot"
+
+desired_sysroot="target/sysroot"
 # Set --sysroot in command line
 sysroot=$(./target/debug/clippy-driver --sysroot $desired_sysroot --print sysroot)
 test "$sysroot" = $desired_sysroot
diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy.yml
index 8179e3e65b5..06bf3b6fdbf 100644
--- a/src/tools/clippy/.github/workflows/clippy.yml
+++ b/src/tools/clippy/.github/workflows/clippy.yml
@@ -69,6 +69,6 @@ jobs:
       working-directory: clippy_dev
 
     - name: Test clippy-driver
-      run: |
-        TOOLCHAIN=$(rustup show active-toolchain | cut -f1 -d' ')
-        rustup run $TOOLCHAIN bash .github/driver.sh
+      run: .github/driver.sh
+      env:
+        OS: ${{ runner.os }}
diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_bors.yml
index 94515987eba..1f4bec92918 100644
--- a/src/tools/clippy/.github/workflows/clippy_bors.yml
+++ b/src/tools/clippy/.github/workflows/clippy_bors.yml
@@ -116,9 +116,7 @@ jobs:
       working-directory: clippy_dev
 
     - name: Test clippy-driver
-      run: |
-        TOOLCHAIN=$(rustup show active-toolchain | cut -f1 -d' ')
-        rustup run $TOOLCHAIN bash .github/driver.sh
+      run: .github/driver.sh
       env:
         OS: ${{ runner.os }}
 
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 9c9ea114081..d5115f70f66 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -5249,6 +5249,7 @@ Released 2018-09-13
 [`disallowed_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_type
 [`disallowed_types`]: https://rust-lang.github.io/rust-clippy/master/index.html#disallowed_types
 [`diverging_sub_expression`]: https://rust-lang.github.io/rust-clippy/master/index.html#diverging_sub_expression
+[`doc_lazy_continuation`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_lazy_continuation
 [`doc_link_with_quotes`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_link_with_quotes
 [`doc_markdown`]: https://rust-lang.github.io/rust-clippy/master/index.html#doc_markdown
 [`double_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#double_comparisons
@@ -5447,6 +5448,7 @@ Released 2018-09-13
 [`little_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#little_endian_bytes
 [`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
 [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
+[`macro_metavars_in_unsafe`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe
 [`macro_use_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#macro_use_imports
 [`main_recursion`]: https://rust-lang.github.io/rust-clippy/master/index.html#main_recursion
 [`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert
@@ -5702,6 +5704,7 @@ Released 2018-09-13
 [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
 [`ref_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_patterns
 [`regex_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#regex_macro
+[`renamed_function_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params
 [`repeat_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_once
 [`repeat_vec_with_capacity`]: https://rust-lang.github.io/rust-clippy/master/index.html#repeat_vec_with_capacity
 [`replace_consts`]: https://rust-lang.github.io/rust-clippy/master/index.html#replace_consts
@@ -5908,6 +5911,7 @@ Released 2018-09-13
 [`verbose_file_reads`]: https://rust-lang.github.io/rust-clippy/master/index.html#verbose_file_reads
 [`vtable_address_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#vtable_address_comparisons
 [`waker_clone_wake`]: https://rust-lang.github.io/rust-clippy/master/index.html#waker_clone_wake
+[`while_float`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_float
 [`while_immutable_condition`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_immutable_condition
 [`while_let_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_loop
 [`while_let_on_iterator`]: https://rust-lang.github.io/rust-clippy/master/index.html#while_let_on_iterator
@@ -5939,8 +5943,10 @@ Released 2018-09-13
 [`allow-expect-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-expect-in-tests
 [`allow-mixed-uninlined-format-args`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-mixed-uninlined-format-args
 [`allow-one-hash-in-raw-strings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-one-hash-in-raw-strings
+[`allow-panic-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-panic-in-tests
 [`allow-print-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-print-in-tests
 [`allow-private-module-inception`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-private-module-inception
+[`allow-renamed-params-for`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-renamed-params-for
 [`allow-unwrap-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-unwrap-in-tests
 [`allow-useless-vec-in-tests`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-useless-vec-in-tests
 [`allowed-dotfiles`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-dotfiles
@@ -6002,4 +6008,5 @@ Released 2018-09-13
 [`vec-box-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#vec-box-size-threshold
 [`verbose-bit-mask-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#verbose-bit-mask-threshold
 [`warn-on-all-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-on-all-wildcard-imports
+[`warn-unsafe-macro-metavars-in-private-macros`]: https://doc.rust-lang.org/clippy/lint_configuration.html#warn-unsafe-macro-metavars-in-private-macros
 <!-- end autogenerated links to configuration documentation -->
diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md
index f6af9810ca1..c8223007df7 100644
--- a/src/tools/clippy/book/src/lint_configuration.md
+++ b/src/tools/clippy/book/src/lint_configuration.md
@@ -101,6 +101,16 @@ Whether to allow `r#""#` when `r""` can be used
 * [`unnecessary_raw_string_hashes`](https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_raw_string_hashes)
 
 
+## `allow-panic-in-tests`
+Whether `panic` should be allowed in test functions or `#[cfg(test)]`
+
+**Default Value:** `false`
+
+---
+**Affected lints:**
+* [`panic`](https://rust-lang.github.io/rust-clippy/master/index.html#panic)
+
+
 ## `allow-print-in-tests`
 Whether print macros (ex. `println!`) should be allowed in test functions or `#[cfg(test)]`
 
@@ -122,6 +132,28 @@ Whether to allow module inception if it's not public.
 * [`module_inception`](https://rust-lang.github.io/rust-clippy/master/index.html#module_inception)
 
 
+## `allow-renamed-params-for`
+List of trait paths to ignore when checking renamed function parameters.
+
+#### Example
+
+```toml
+allow-renamed-params-for = [ "std::convert::From" ]
+```
+
+#### Noteworthy
+
+- By default, the following traits are ignored: `From`, `TryFrom`, `FromStr`
+- `".."` can be used as part of the list to indicate that the configured values should be appended to the
+default configuration of Clippy. By default, any configuration will replace the default value.
+
+**Default Value:** `["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"]`
+
+---
+**Affected lints:**
+* [`renamed_function_params`](https://rust-lang.github.io/rust-clippy/master/index.html#renamed_function_params)
+
+
 ## `allow-unwrap-in-tests`
 Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
 
@@ -900,3 +932,13 @@ Whether to allow certain wildcard imports (prelude, super in tests).
 * [`wildcard_imports`](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports)
 
 
+## `warn-unsafe-macro-metavars-in-private-macros`
+Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros.
+
+**Default Value:** `false`
+
+---
+**Affected lints:**
+* [`macro_metavars_in_unsafe`](https://rust-lang.github.io/rust-clippy/master/index.html#macro_metavars_in_unsafe)
+
+
diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs
index 5cfcbdb57d7..cfdf620b7d0 100644
--- a/src/tools/clippy/clippy_config/src/conf.rs
+++ b/src/tools/clippy/clippy_config/src/conf.rs
@@ -40,6 +40,8 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
 const DEFAULT_DISALLOWED_NAMES: &[&str] = &["foo", "baz", "quux"];
 const DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z", "w", "n"];
 const DEFAULT_ALLOWED_PREFIXES: &[&str] = &["to", "as", "into", "from", "try_into", "try_from"];
+const DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS: &[&str] =
+    &["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"];
 
 /// Conf with parse errors
 #[derive(Default)]
@@ -455,6 +457,10 @@ define_Conf! {
     ///
     /// Whether `unwrap` should be allowed in test functions or `#[cfg(test)]`
     (allow_unwrap_in_tests: bool = false),
+    /// Lint: PANIC.
+    ///
+    /// Whether `panic` should be allowed in test functions or `#[cfg(test)]`
+    (allow_panic_in_tests: bool = false),
     /// Lint: DBG_MACRO.
     ///
     /// Whether `dbg!` should be allowed in test functions or `#[cfg(test)]`
@@ -613,6 +619,27 @@ define_Conf! {
     /// - Use `".."` as part of the list to indicate that the configured values should be appended to the
     /// default configuration of Clippy. By default, any configuration will replace the default value
     (allowed_prefixes: Vec<String> = DEFAULT_ALLOWED_PREFIXES.iter().map(ToString::to_string).collect()),
+    /// Lint: RENAMED_FUNCTION_PARAMS.
+    ///
+    /// List of trait paths to ignore when checking renamed function parameters.
+    ///
+    /// #### Example
+    ///
+    /// ```toml
+    /// allow-renamed-params-for = [ "std::convert::From" ]
+    /// ```
+    ///
+    /// #### Noteworthy
+    ///
+    /// - By default, the following traits are ignored: `From`, `TryFrom`, `FromStr`
+    /// - `".."` can be used as part of the list to indicate that the configured values should be appended to the
+    /// default configuration of Clippy. By default, any configuration will replace the default value.
+    (allow_renamed_params_for: Vec<String> =
+        DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS.iter().map(ToString::to_string).collect()),
+    /// Lint: MACRO_METAVARS_IN_UNSAFE.
+    ///
+    /// Whether to also emit warnings for unsafe blocks with metavariable expansions in **private** macros.
+    (warn_unsafe_macro_metavars_in_private_macros: bool = false),
 }
 
 /// Search for the configuration file.
@@ -674,6 +701,10 @@ fn deserialize(file: &SourceFile) -> TryConf {
             extend_vec_if_indicator_present(&mut conf.conf.doc_valid_idents, DEFAULT_DOC_VALID_IDENTS);
             extend_vec_if_indicator_present(&mut conf.conf.disallowed_names, DEFAULT_DISALLOWED_NAMES);
             extend_vec_if_indicator_present(&mut conf.conf.allowed_prefixes, DEFAULT_ALLOWED_PREFIXES);
+            extend_vec_if_indicator_present(
+                &mut conf.conf.allow_renamed_params_for,
+                DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS,
+            );
             // TODO: THIS SHOULD BE TESTED, this comment will be gone soon
             if conf.conf.allowed_idents_below_min_chars.contains("..") {
                 conf.conf
diff --git a/src/tools/clippy/clippy_config/src/msrvs.rs b/src/tools/clippy/clippy_config/src/msrvs.rs
index 14808440d48..a3e7d0c3fa5 100644
--- a/src/tools/clippy/clippy_config/src/msrvs.rs
+++ b/src/tools/clippy/clippy_config/src/msrvs.rs
@@ -26,7 +26,8 @@ msrv_aliases! {
     1,63,0 { CLONE_INTO }
     1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }
     1,59,0 { THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST }
-    1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY }
+    1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF }
+    1,56,0 { CONST_FN_UNION }
     1,55,0 { SEEK_REWIND }
     1,54,0 { INTO_KEYS }
     1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN, ARRAY_INTO_ITERATOR }
diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml
index 42a953039b1..4104e7d94f1 100644
--- a/src/tools/clippy/clippy_dev/Cargo.toml
+++ b/src/tools/clippy/clippy_dev/Cargo.toml
@@ -1,11 +1,12 @@
 [package]
 name = "clippy_dev"
+description = "Clippy developer tooling"
 version = "0.0.1"
 edition = "2021"
 
 [dependencies]
 aho-corasick = "1.0"
-clap = "4.1.4"
+clap = { version = "4.4", features = ["derive"] }
 indoc = "1.0"
 itertools = "0.12"
 opener = "0.6"
diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs
index 397a0e99082..366b52b25df 100644
--- a/src/tools/clippy/clippy_dev/src/main.rs
+++ b/src/tools/clippy/clippy_dev/src/main.rs
@@ -2,350 +2,292 @@
 // warn on lints, that are included in `rust-lang/rust`s bootstrap
 #![warn(rust_2018_idioms, unused_lifetimes)]
 
-use clap::{Arg, ArgAction, ArgMatches, Command};
+use clap::{Args, Parser, Subcommand};
 use clippy_dev::{dogfood, fmt, lint, new_lint, serve, setup, update_lints};
-use indoc::indoc;
 use std::convert::Infallible;
 
 fn main() {
-    let matches = get_clap_config();
+    let dev = Dev::parse();
 
-    match matches.subcommand() {
-        Some(("bless", _)) => {
+    match dev.command {
+        DevCommand::Bless => {
             eprintln!("use `cargo bless` to automatically replace `.stderr` and `.fixed` files as tests are being run");
         },
-        Some(("dogfood", matches)) => {
-            dogfood::dogfood(
-                matches.get_flag("fix"),
-                matches.get_flag("allow-dirty"),
-                matches.get_flag("allow-staged"),
-            );
-        },
-        Some(("fmt", matches)) => {
-            fmt::run(matches.get_flag("check"), matches.get_flag("verbose"));
-        },
-        Some(("update_lints", matches)) => {
-            if matches.get_flag("print-only") {
+        DevCommand::Dogfood {
+            fix,
+            allow_dirty,
+            allow_staged,
+        } => dogfood::dogfood(fix, allow_dirty, allow_staged),
+        DevCommand::Fmt { check, verbose } => fmt::run(check, verbose),
+        DevCommand::UpdateLints { print_only, check } => {
+            if print_only {
                 update_lints::print_lints();
-            } else if matches.get_flag("check") {
+            } else if check {
                 update_lints::update(update_lints::UpdateMode::Check);
             } else {
                 update_lints::update(update_lints::UpdateMode::Change);
             }
         },
-        Some(("new_lint", matches)) => {
-            match new_lint::create(
-                matches.get_one::<String>("pass").unwrap(),
-                matches.get_one::<String>("name"),
-                matches.get_one::<String>("category").map(String::as_str),
-                matches.get_one::<String>("type").map(String::as_str),
-                matches.get_flag("msrv"),
-            ) {
-                Ok(()) => update_lints::update(update_lints::UpdateMode::Change),
-                Err(e) => eprintln!("Unable to create lint: {e}"),
-            }
+        DevCommand::NewLint {
+            pass,
+            name,
+            category,
+            r#type,
+            msrv,
+        } => match new_lint::create(&pass, &name, &category, r#type.as_deref(), msrv) {
+            Ok(()) => update_lints::update(update_lints::UpdateMode::Change),
+            Err(e) => eprintln!("Unable to create lint: {e}"),
         },
-        Some(("setup", sub_command)) => match sub_command.subcommand() {
-            Some(("git-hook", matches)) => {
-                if matches.get_flag("remove") {
-                    setup::git_hook::remove_hook();
+        DevCommand::Setup(SetupCommand { subcommand }) => match subcommand {
+            SetupSubcommand::Intellij { remove, repo_path } => {
+                if remove {
+                    setup::intellij::remove_rustc_src();
                 } else {
-                    setup::git_hook::install_hook(matches.get_flag("force-override"));
+                    setup::intellij::setup_rustc_src(&repo_path);
                 }
             },
-            Some(("intellij", matches)) => {
-                if matches.get_flag("remove") {
-                    setup::intellij::remove_rustc_src();
+            SetupSubcommand::GitHook { remove, force_override } => {
+                if remove {
+                    setup::git_hook::remove_hook();
                 } else {
-                    setup::intellij::setup_rustc_src(
-                        matches
-                            .get_one::<String>("rustc-repo-path")
-                            .expect("this field is mandatory and therefore always valid"),
-                    );
+                    setup::git_hook::install_hook(force_override);
                 }
             },
-            Some(("toolchain", matches)) => {
-                setup::toolchain::create(
-                    matches.get_flag("force"),
-                    matches.get_flag("release"),
-                    matches.get_one::<String>("name").unwrap(),
-                );
-            },
-            Some(("vscode-tasks", matches)) => {
-                if matches.get_flag("remove") {
+            SetupSubcommand::Toolchain { force, release, name } => setup::toolchain::create(force, release, &name),
+            SetupSubcommand::VscodeTasks { remove, force_override } => {
+                if remove {
                     setup::vscode::remove_tasks();
                 } else {
-                    setup::vscode::install_tasks(matches.get_flag("force-override"));
+                    setup::vscode::install_tasks(force_override);
                 }
             },
-            _ => {},
-        },
-        Some(("remove", sub_command)) => match sub_command.subcommand() {
-            Some(("git-hook", _)) => setup::git_hook::remove_hook(),
-            Some(("intellij", _)) => setup::intellij::remove_rustc_src(),
-            Some(("vscode-tasks", _)) => setup::vscode::remove_tasks(),
-            _ => {},
-        },
-        Some(("serve", matches)) => {
-            let port = *matches.get_one::<u16>("port").unwrap();
-            let lint = matches.get_one::<String>("lint");
-            serve::run(port, lint);
         },
-        Some(("lint", matches)) => {
-            let path = matches.get_one::<String>("path").unwrap();
-            let args = matches.get_many::<String>("args").into_iter().flatten();
-            lint::run(path, args);
+        DevCommand::Remove(RemoveCommand { subcommand }) => match subcommand {
+            RemoveSubcommand::Intellij => setup::intellij::remove_rustc_src(),
+            RemoveSubcommand::GitHook => setup::git_hook::remove_hook(),
+            RemoveSubcommand::VscodeTasks => setup::vscode::remove_tasks(),
         },
-        Some(("rename_lint", matches)) => {
-            let old_name = matches.get_one::<String>("old_name").unwrap();
-            let new_name = matches.get_one::<String>("new_name").unwrap_or(old_name);
-            let uplift = matches.get_flag("uplift");
-            update_lints::rename(old_name, new_name, uplift);
-        },
-        Some(("deprecate", matches)) => {
-            let name = matches.get_one::<String>("name").unwrap();
-            let reason = matches.get_one("reason");
-            update_lints::deprecate(name, reason);
-        },
-        _ => {},
+        DevCommand::Serve { port, lint } => serve::run(port, lint),
+        DevCommand::Lint { path, args } => lint::run(&path, args.iter()),
+        DevCommand::RenameLint {
+            old_name,
+            new_name,
+            uplift,
+        } => update_lints::rename(&old_name, new_name.as_ref().unwrap_or(&old_name), uplift),
+        DevCommand::Deprecate { name, reason } => update_lints::deprecate(&name, reason.as_deref()),
     }
 }
 
-fn get_clap_config() -> ArgMatches {
-    Command::new("Clippy developer tooling")
-        .arg_required_else_help(true)
-        .subcommands([
-            Command::new("bless").about("bless the test output changes").arg(
-                Arg::new("ignore-timestamp")
-                    .long("ignore-timestamp")
-                    .action(ArgAction::SetTrue)
-                    .help("Include files updated before clippy was built"),
-            ),
-            Command::new("dogfood").about("Runs the dogfood test").args([
-                Arg::new("fix")
-                    .long("fix")
-                    .action(ArgAction::SetTrue)
-                    .help("Apply the suggestions when possible"),
-                Arg::new("allow-dirty")
-                    .long("allow-dirty")
-                    .action(ArgAction::SetTrue)
-                    .help("Fix code even if the working directory has changes")
-                    .requires("fix"),
-                Arg::new("allow-staged")
-                    .long("allow-staged")
-                    .action(ArgAction::SetTrue)
-                    .help("Fix code even if the working directory has staged changes")
-                    .requires("fix"),
-            ]),
-            Command::new("fmt")
-                .about("Run rustfmt on all projects and tests")
-                .args([
-                    Arg::new("check")
-                        .long("check")
-                        .action(ArgAction::SetTrue)
-                        .help("Use the rustfmt --check option"),
-                    Arg::new("verbose")
-                        .short('v')
-                        .long("verbose")
-                        .action(ArgAction::SetTrue)
-                        .help("Echo commands run"),
-                ]),
-            Command::new("update_lints")
-                .about("Updates lint registration and information from the source code")
-                .long_about(
-                    "Makes sure that:\n \
-                    * the lint count in README.md is correct\n \
-                    * the changelog contains markdown link references at the bottom\n \
-                    * all lint groups include the correct lints\n \
-                    * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod`\n \
-                    * all lints are registered in the lint store",
-                )
-                .args([
-                    Arg::new("print-only")
-                        .long("print-only")
-                        .action(ArgAction::SetTrue)
-                        .help(
-                            "Print a table of lints to STDOUT. \
-                            This does not include deprecated and internal lints. \
-                            (Does not modify any files)",
-                        ),
-                    Arg::new("check")
-                        .long("check")
-                        .action(ArgAction::SetTrue)
-                        .help("Checks that `cargo dev update_lints` has been run. Used on CI."),
-                ]),
-            Command::new("new_lint")
-                .about("Create new lint and run `cargo dev update_lints`")
-                .args([
-                    Arg::new("pass")
-                        .short('p')
-                        .long("pass")
-                        .help("Specify whether the lint runs during the early or late pass")
-                        .value_parser(["early", "late"])
-                        .conflicts_with("type")
-                        .default_value("late"),
-                    Arg::new("name")
-                        .short('n')
-                        .long("name")
-                        .help("Name of the new lint in snake case, ex: fn_too_long")
-                        .required(true)
-                        .value_parser(|name: &str| Ok::<_, Infallible>(name.replace('-', "_"))),
-                    Arg::new("category")
-                        .short('c')
-                        .long("category")
-                        .help("What category the lint belongs to")
-                        .default_value("nursery")
-                        .value_parser([
-                            "style",
-                            "correctness",
-                            "suspicious",
-                            "complexity",
-                            "perf",
-                            "pedantic",
-                            "restriction",
-                            "cargo",
-                            "nursery",
-                            "internal",
-                        ]),
-                    Arg::new("type").long("type").help("What directory the lint belongs in"),
-                    Arg::new("msrv")
-                        .long("msrv")
-                        .action(ArgAction::SetTrue)
-                        .help("Add MSRV config code to the lint"),
-                ]),
-            Command::new("setup")
-                .about("Support for setting up your personal development environment")
-                .arg_required_else_help(true)
-                .subcommands([
-                    Command::new("git-hook")
-                        .about("Add a pre-commit git hook that formats your code to make it look pretty")
-                        .args([
-                            Arg::new("remove")
-                                .long("remove")
-                                .action(ArgAction::SetTrue)
-                                .help("Remove the pre-commit hook added with 'cargo dev setup git-hook'"),
-                            Arg::new("force-override")
-                                .long("force-override")
-                                .short('f')
-                                .action(ArgAction::SetTrue)
-                                .help("Forces the override of an existing git pre-commit hook"),
-                        ]),
-                    Command::new("intellij")
-                        .about("Alter dependencies so Intellij Rust can find rustc internals")
-                        .args([
-                            Arg::new("remove")
-                                .long("remove")
-                                .action(ArgAction::SetTrue)
-                                .help("Remove the dependencies added with 'cargo dev setup intellij'"),
-                            Arg::new("rustc-repo-path")
-                                .long("repo-path")
-                                .short('r')
-                                .help("The path to a rustc repo that will be used for setting the dependencies")
-                                .value_name("path")
-                                .conflicts_with("remove")
-                                .required(true),
-                        ]),
-                    Command::new("toolchain")
-                        .about("Install a rustup toolchain pointing to the local clippy build")
-                        .args([
-                            Arg::new("force")
-                                .long("force")
-                                .short('f')
-                                .action(ArgAction::SetTrue)
-                                .help("Override an existing toolchain"),
-                            Arg::new("release")
-                                .long("release")
-                                .short('r')
-                                .action(ArgAction::SetTrue)
-                                .help("Point to --release clippy binaries"),
-                            Arg::new("name")
-                                .long("name")
-                                .default_value("clippy")
-                                .help("The name of the created toolchain"),
-                        ]),
-                    Command::new("vscode-tasks")
-                        .about("Add several tasks to vscode for formatting, validation and testing")
-                        .args([
-                            Arg::new("remove")
-                                .long("remove")
-                                .action(ArgAction::SetTrue)
-                                .help("Remove the tasks added with 'cargo dev setup vscode-tasks'"),
-                            Arg::new("force-override")
-                                .long("force-override")
-                                .short('f')
-                                .action(ArgAction::SetTrue)
-                                .help("Forces the override of existing vscode tasks"),
-                        ]),
-                ]),
-            Command::new("remove")
-                .about("Support for undoing changes done by the setup command")
-                .arg_required_else_help(true)
-                .subcommands([
-                    Command::new("git-hook").about("Remove any existing pre-commit git hook"),
-                    Command::new("vscode-tasks").about("Remove any existing vscode tasks"),
-                    Command::new("intellij").about("Removes rustc source paths added via `cargo dev setup intellij`"),
-                ]),
-            Command::new("serve")
-                .about("Launch a local 'ALL the Clippy Lints' website in a browser")
-                .args([
-                    Arg::new("port")
-                        .long("port")
-                        .short('p')
-                        .help("Local port for the http server")
-                        .default_value("8000")
-                        .value_parser(clap::value_parser!(u16)),
-                    Arg::new("lint").help("Which lint's page to load initially (optional)"),
-                ]),
-            Command::new("lint")
-                .about("Manually run clippy on a file or package")
-                .after_help(indoc! {"
-                    EXAMPLES
-                        Lint a single file:
-                            cargo dev lint tests/ui/attrs.rs
+#[derive(Parser)]
+#[command(name = "dev", about)]
+struct Dev {
+    #[command(subcommand)]
+    command: DevCommand,
+}
 
-                        Lint a package directory:
-                            cargo dev lint tests/ui-cargo/wildcard_dependencies/fail
-                            cargo dev lint ~/my-project
+#[derive(Subcommand)]
+enum DevCommand {
+    /// Bless the test output changes
+    Bless,
+    /// Runs the dogfood test
+    Dogfood {
+        #[arg(long)]
+        /// Apply the suggestions when possible
+        fix: bool,
+        #[arg(long, requires = "fix")]
+        /// Fix code even if the working directory has changes
+        allow_dirty: bool,
+        #[arg(long, requires = "fix")]
+        /// Fix code even if the working directory has staged changes
+        allow_staged: bool,
+    },
+    /// Run rustfmt on all projects and tests
+    Fmt {
+        #[arg(long)]
+        /// Use the rustfmt --check option
+        check: bool,
+        #[arg(short, long)]
+        /// Echo commands run
+        verbose: bool,
+    },
+    #[command(name = "update_lints")]
+    /// Updates lint registration and information from the source code
+    ///
+    /// Makes sure that: {n}
+    /// * the lint count in README.md is correct {n}
+    /// * the changelog contains markdown link references at the bottom {n}
+    /// * all lint groups include the correct lints {n}
+    /// * lint modules in `clippy_lints/*` are visible in `src/lib.rs` via `pub mod` {n}
+    /// * all lints are registered in the lint store
+    UpdateLints {
+        #[arg(long)]
+        /// Print a table of lints to STDOUT
+        ///
+        /// This does not include deprecated and internal lints. (Does not modify any files)
+        print_only: bool,
+        #[arg(long)]
+        /// Checks that `cargo dev update_lints` has been run. Used on CI.
+        check: bool,
+    },
+    #[command(name = "new_lint")]
+    /// Create a new lint and run `cargo dev update_lints`
+    NewLint {
+        #[arg(short, long, value_parser = ["early", "late"], conflicts_with = "type", default_value = "late")]
+        /// Specify whether the lint runs during the early or late pass
+        pass: String,
+        #[arg(
+            short,
+            long,
+            value_parser = |name: &str| Ok::<_, Infallible>(name.replace('-', "_")),
+        )]
+        /// Name of the new lint in snake case, ex: `fn_too_long`
+        name: String,
+        #[arg(
+            short,
+            long,
+            value_parser = [
+                "style",
+                "correctness",
+                "suspicious",
+                "complexity",
+                "perf",
+                "pedantic",
+                "restriction",
+                "cargo",
+                "nursery",
+                "internal",
+            ],
+            default_value = "nursery",
+        )]
+        /// What category the lint belongs to
+        category: String,
+        #[arg(long)]
+        /// What directory the lint belongs in
+        r#type: Option<String>,
+        #[arg(long)]
+        /// Add MSRV config code to the lint
+        msrv: bool,
+    },
+    /// Support for setting up your personal development environment
+    Setup(SetupCommand),
+    /// Support for removing changes done by the setup command
+    Remove(RemoveCommand),
+    /// Launch a local 'ALL the Clippy Lints' website in a browser
+    Serve {
+        #[arg(short, long, default_value = "8000")]
+        /// Local port for the http server
+        port: u16,
+        #[arg(long)]
+        /// Which lint's page to load initially (optional)
+        lint: Option<String>,
+    },
+    #[allow(clippy::doc_markdown)]
+    /// Manually run clippy on a file or package
+    ///
+    /// ## Examples
+    ///
+    /// Lint a single file: {n}
+    ///     cargo dev lint tests/ui/attrs.rs
+    ///
+    /// Lint a package directory: {n}
+    ///     cargo dev lint tests/ui-cargo/wildcard_dependencies/fail {n}
+    ///     cargo dev lint ~/my-project
+    ///
+    /// Run rustfix: {n}
+    ///     cargo dev lint ~/my-project -- --fix
+    ///
+    /// Set lint levels: {n}
+    ///     cargo dev lint file.rs -- -W clippy::pedantic {n}
+    ///     cargo dev lint ~/my-project -- -- -W clippy::pedantic
+    Lint {
+        /// The path to a file or package directory to lint
+        path: String,
+        /// Pass extra arguments to cargo/clippy-driver
+        args: Vec<String>,
+    },
+    #[command(name = "rename_lint")]
+    /// Rename a lint
+    RenameLint {
+        /// The name of the lint to rename
+        old_name: String,
+        #[arg(required_unless_present = "uplift")]
+        /// The new name of the lint
+        new_name: Option<String>,
+        #[arg(long)]
+        /// This lint will be uplifted into rustc
+        uplift: bool,
+    },
+    /// Deprecate the given lint
+    Deprecate {
+        /// The name of the lint to deprecate
+        name: String,
+        #[arg(long, short)]
+        /// The reason for deprecation
+        reason: Option<String>,
+    },
+}
 
-                        Run rustfix:
-                            cargo dev lint ~/my-project -- --fix
+#[derive(Args)]
+struct SetupCommand {
+    #[command(subcommand)]
+    subcommand: SetupSubcommand,
+}
+
+#[derive(Subcommand)]
+enum SetupSubcommand {
+    /// Alter dependencies so Intellij Rust can find rustc internals
+    Intellij {
+        #[arg(long)]
+        /// Remove the dependencies added with 'cargo dev setup intellij'
+        remove: bool,
+        #[arg(long, short, conflicts_with = "remove")]
+        /// The path to a rustc repo that will be used for setting the dependencies
+        repo_path: String,
+    },
+    /// Add a pre-commit git hook that formats your code to make it look pretty
+    GitHook {
+        #[arg(long)]
+        /// Remove the pre-commit hook added with 'cargo dev setup git-hook'
+        remove: bool,
+        #[arg(long, short)]
+        /// Forces the override of an existing git pre-commit hook
+        force_override: bool,
+    },
+    /// Install a rustup toolchain pointing to the local clippy build
+    Toolchain {
+        #[arg(long, short)]
+        /// Override an existing toolchain
+        force: bool,
+        #[arg(long, short)]
+        /// Point to --release clippy binary
+        release: bool,
+        #[arg(long, default_value = "clippy")]
+        /// Name of the toolchain
+        name: String,
+    },
+    /// Add several tasks to vscode for formatting, validation and testing
+    VscodeTasks {
+        #[arg(long)]
+        /// Remove the tasks added with 'cargo dev setup vscode-tasks'
+        remove: bool,
+        #[arg(long, short)]
+        /// Forces the override of existing vscode tasks
+        force_override: bool,
+    },
+}
+
+#[derive(Args)]
+struct RemoveCommand {
+    #[command(subcommand)]
+    subcommand: RemoveSubcommand,
+}
 
-                        Set lint levels:
-                            cargo dev lint file.rs -- -W clippy::pedantic
-                            cargo dev lint ~/my-project -- -- -W clippy::pedantic
-                "})
-                .args([
-                    Arg::new("path")
-                        .required(true)
-                        .help("The path to a file or package directory to lint"),
-                    Arg::new("args")
-                        .action(ArgAction::Append)
-                        .help("Pass extra arguments to cargo/clippy-driver"),
-                ]),
-            Command::new("rename_lint").about("Renames the given lint").args([
-                Arg::new("old_name")
-                    .index(1)
-                    .required(true)
-                    .help("The name of the lint to rename"),
-                Arg::new("new_name")
-                    .index(2)
-                    .required_unless_present("uplift")
-                    .help("The new name of the lint"),
-                Arg::new("uplift")
-                    .long("uplift")
-                    .action(ArgAction::SetTrue)
-                    .help("This lint will be uplifted into rustc"),
-            ]),
-            Command::new("deprecate").about("Deprecates the given lint").args([
-                Arg::new("name")
-                    .index(1)
-                    .required(true)
-                    .help("The name of the lint to deprecate"),
-                Arg::new("reason")
-                    .long("reason")
-                    .short('r')
-                    .help("The reason for deprecation"),
-            ]),
-        ])
-        .get_matches()
+#[derive(Subcommand)]
+enum RemoveSubcommand {
+    /// Remove the dependencies added with 'cargo dev setup intellij'
+    Intellij,
+    /// Remove the pre-commit git hook
+    GitHook,
+    /// Remove the tasks added with 'cargo dev setup vscode-tasks'
+    VscodeTasks,
 }
diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs
index 2940d56350f..b6481dde4dd 100644
--- a/src/tools/clippy/clippy_dev/src/new_lint.rs
+++ b/src/tools/clippy/clippy_dev/src/new_lint.rs
@@ -36,22 +36,16 @@ impl<T> Context for io::Result<T> {
 /// # Errors
 ///
 /// This function errors out if the files couldn't be created or written to.
-pub fn create(
-    pass: &String,
-    lint_name: Option<&String>,
-    category: Option<&str>,
-    mut ty: Option<&str>,
-    msrv: bool,
-) -> io::Result<()> {
-    if category == Some("cargo") && ty.is_none() {
+pub fn create(pass: &str, name: &str, category: &str, mut ty: Option<&str>, msrv: bool) -> io::Result<()> {
+    if category == "cargo" && ty.is_none() {
         // `cargo` is a special category, these lints should always be in `clippy_lints/src/cargo`
         ty = Some("cargo");
     }
 
     let lint = LintData {
         pass,
-        name: lint_name.expect("`name` argument is validated by clap"),
-        category: category.expect("`category` argument is validated by clap"),
+        name,
+        category,
         ty,
         project_root: clippy_project_root(),
     };
diff --git a/src/tools/clippy/clippy_dev/src/serve.rs b/src/tools/clippy/clippy_dev/src/serve.rs
index ea925f6709f..4a4261d1a1e 100644
--- a/src/tools/clippy/clippy_dev/src/serve.rs
+++ b/src/tools/clippy/clippy_dev/src/serve.rs
@@ -8,7 +8,7 @@ use std::{env, thread};
 /// # Panics
 ///
 /// Panics if the python commands could not be spawned
-pub fn run(port: u16, lint: Option<&String>) -> ! {
+pub fn run(port: u16, lint: Option<String>) -> ! {
     let mut url = Some(match lint {
         None => format!("http://localhost:{port}"),
         Some(lint) => format!("http://localhost:{port}/#{lint}"),
diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs
index 625b1339591..45353901c98 100644
--- a/src/tools/clippy/clippy_dev/src/update_lints.rs
+++ b/src/tools/clippy/clippy_dev/src/update_lints.rs
@@ -314,7 +314,7 @@ const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note";
 /// # Panics
 ///
 /// If a file path could not read from or written to
-pub fn deprecate(name: &str, reason: Option<&String>) {
+pub fn deprecate(name: &str, reason: Option<&str>) {
     fn finish(
         (lints, mut deprecated_lints, renamed_lints): (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>),
         name: &str,
@@ -335,7 +335,7 @@ pub fn deprecate(name: &str, reason: Option<&String>) {
         println!("note: you must run `cargo uitest` to update the test results");
     }
 
-    let reason = reason.map_or(DEFAULT_DEPRECATION_REASON, String::as_str);
+    let reason = reason.unwrap_or(DEFAULT_DEPRECATION_REASON);
     let name_lower = name.to_lowercase();
     let name_upper = name.to_uppercase();
 
diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs
index f0dafb1ae0d..e94a6f3e3fc 100644
--- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs
+++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs
@@ -2,15 +2,15 @@ use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::HirNode;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{is_trait_method, path_to_local};
+use clippy_utils::{is_trait_method, local_is_initialized, path_to_local};
 use rustc_errors::Applicability;
-use rustc_hir::{self as hir, Expr, ExprKind, Node};
+use rustc_hir::{self as hir, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, Instance, Mutability};
 use rustc_session::impl_lint_pass;
 use rustc_span::def_id::DefId;
 use rustc_span::symbol::sym;
-use rustc_span::ExpnKind;
+use rustc_span::{ExpnKind, SyntaxContext};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -36,6 +36,7 @@ declare_clippy_lint! {
     /// Use instead:
     /// ```rust
     /// struct Thing;
+    ///
     /// impl Clone for Thing {
     ///     fn clone(&self) -> Self { todo!() }
     ///     fn clone_from(&mut self, other: &Self) { todo!() }
@@ -47,7 +48,7 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "1.78.0"]
     pub ASSIGNING_CLONES,
-    perf,
+    pedantic,
     "assigning the result of cloning may be inefficient"
 }
 
@@ -67,7 +68,8 @@ impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]);
 impl<'tcx> LateLintPass<'tcx> for AssigningClones {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, assign_expr: &'tcx Expr<'_>) {
         // Do not fire the lint in macros
-        let expn_data = assign_expr.span().ctxt().outer_expn_data();
+        let ctxt = assign_expr.span().ctxt();
+        let expn_data = ctxt.outer_expn_data();
         match expn_data.kind {
             ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) | ExpnKind::Macro(..) => return,
             ExpnKind::Root => {},
@@ -82,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones {
         };
 
         if is_ok_to_suggest(cx, lhs, &call, &self.msrv) {
-            suggest(cx, assign_expr, lhs, &call);
+            suggest(cx, ctxt, assign_expr, lhs, &call);
         }
     }
 
@@ -163,9 +165,7 @@ fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallC
         // TODO: This check currently bails if the local variable has no initializer.
         // That is overly conservative - the lint should fire even if there was no initializer,
         // but the variable has been initialized before `lhs` was evaluated.
-        if let Some(Node::LetStmt(local)) = cx.tcx.hir().parent_id_iter(local).next().map(|p| cx.tcx.hir_node(p))
-            && local.init.is_none()
-        {
+        if !local_is_initialized(cx, local) {
             return false;
         }
     }
@@ -222,14 +222,20 @@ fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallC
     implemented_fns.contains_key(&provided_fn.def_id)
 }
 
-fn suggest<'tcx>(cx: &LateContext<'tcx>, assign_expr: &Expr<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>) {
+fn suggest<'tcx>(
+    cx: &LateContext<'tcx>,
+    ctxt: SyntaxContext,
+    assign_expr: &Expr<'tcx>,
+    lhs: &Expr<'tcx>,
+    call: &CallCandidate<'tcx>,
+) {
     span_lint_and_then(cx, ASSIGNING_CLONES, assign_expr.span, call.message(), |diag| {
         let mut applicability = Applicability::Unspecified;
 
         diag.span_suggestion(
             assign_expr.span,
             call.suggestion_msg(),
-            call.suggested_replacement(cx, lhs, &mut applicability),
+            call.suggested_replacement(cx, ctxt, lhs, &mut applicability),
             applicability,
         );
     });
@@ -275,6 +281,7 @@ impl<'tcx> CallCandidate<'tcx> {
     fn suggested_replacement(
         &self,
         cx: &LateContext<'tcx>,
+        ctxt: SyntaxContext,
         lhs: &Expr<'tcx>,
         applicability: &mut Applicability,
     ) -> String {
@@ -294,7 +301,7 @@ impl<'tcx> CallCandidate<'tcx> {
                         // Determine whether we need to reference the argument to clone_from().
                         let clone_receiver_type = cx.typeck_results().expr_ty(receiver);
                         let clone_receiver_adj_type = cx.typeck_results().expr_ty_adjusted(receiver);
-                        let mut arg_sugg = Sugg::hir_with_applicability(cx, receiver, "_", applicability);
+                        let mut arg_sugg = Sugg::hir_with_context(cx, receiver, ctxt, "_", applicability);
                         if clone_receiver_type != clone_receiver_adj_type {
                             // The receiver may have been a value type, so we need to add an `&` to
                             // be sure the argument to clone_from will be a reference.
@@ -312,7 +319,7 @@ impl<'tcx> CallCandidate<'tcx> {
                             Sugg::hir_with_applicability(cx, lhs, "_", applicability).mut_addr()
                         };
                         // The RHS had to be exactly correct before the call, there is no auto-deref for function calls.
-                        let rhs_sugg = Sugg::hir_with_applicability(cx, self_arg, "_", applicability);
+                        let rhs_sugg = Sugg::hir_with_context(cx, self_arg, ctxt, "_", applicability);
 
                         format!("Clone::clone_from({self_sugg}, {rhs_sugg})")
                     },
@@ -341,11 +348,11 @@ impl<'tcx> CallCandidate<'tcx> {
 
                 match self.kind {
                     CallKind::MethodCall { receiver } => {
-                        let receiver_sugg = Sugg::hir_with_applicability(cx, receiver, "_", applicability);
+                        let receiver_sugg = Sugg::hir_with_context(cx, receiver, ctxt, "_", applicability);
                         format!("{receiver_sugg}.clone_into({rhs_sugg})")
                     },
                     CallKind::FunctionCall { self_arg, .. } => {
-                        let self_sugg = Sugg::hir_with_applicability(cx, self_arg, "_", applicability);
+                        let self_sugg = Sugg::hir_with_context(cx, self_arg, ctxt, "_", applicability);
                         format!("ToOwned::clone_into({self_sugg}, {rhs_sugg})")
                     },
                 }
diff --git a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs
index 736ee48641d..40a1c4e2884 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs
@@ -36,9 +36,10 @@ fn check_duplicated_attr(
     }
     let Some(ident) = attr.ident() else { return };
     let name = ident.name;
-    if name == sym::doc || name == sym::cfg_attr {
+    if name == sym::doc || name == sym::cfg_attr || name == sym::rustc_on_unimplemented {
         // FIXME: Would be nice to handle `cfg_attr` as well. Only problem is to check that cfg
         // conditions are the same.
+        // `#[rustc_on_unimplemented]` contains duplicated subattributes, that's expected.
         return;
     }
     if let Some(direct_parent) = parent.last()
diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs
index 8f47bc7653b..39f40607799 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs
@@ -61,11 +61,21 @@ declare_clippy_lint! {
     ///
     /// This lint permits lint attributes for lints emitted on the items themself.
     /// For `use` items these lints are:
+    /// * ambiguous_glob_reexports
+    /// * dead_code
     /// * deprecated
+    /// * hidden_glob_reexports
     /// * unreachable_pub
-    /// * unused_imports
+    /// * unused
+    /// * unused_braces
+    /// * unused_import_braces
+    /// * clippy::disallowed_types
     /// * clippy::enum_glob_use
     /// * clippy::macro_use_imports
+    /// * clippy::module_name_repetitions
+    /// * clippy::redundant_pub_crate
+    /// * clippy::single_component_path_imports
+    /// * clippy::unsafe_removed_from_name
     /// * clippy::wildcard_imports
     ///
     /// For `extern crate` items these lints are:
diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
index 7575f502a7c..f0868231d01 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
@@ -2,6 +2,7 @@ use super::utils::{extract_clippy_lint, is_lint_level, is_word};
 use super::{Attribute, USELESS_ATTRIBUTE};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::{first_line_of_span, snippet_opt};
+use rustc_ast::NestedMetaItem;
 use rustc_errors::Applicability;
 use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LintContext};
@@ -20,26 +21,40 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute])
                 for lint in lint_list {
                     match item.kind {
                         ItemKind::Use(..) => {
-                            if is_word(lint, sym::unused_imports)
-                                || is_word(lint, sym::deprecated)
-                                || is_word(lint, sym!(unreachable_pub))
-                                || is_word(lint, sym!(unused))
-                                || is_word(lint, sym!(unused_import_braces))
-                                || extract_clippy_lint(lint).map_or(false, |s| {
-                                    matches!(
-                                        s.as_str(),
-                                        "wildcard_imports"
-                                            | "enum_glob_use"
-                                            | "redundant_pub_crate"
-                                            | "macro_use_imports"
-                                            | "unsafe_removed_from_name"
-                                            | "module_name_repetitions"
-                                            | "single_component_path_imports"
-                                    )
-                                })
+                            if let NestedMetaItem::MetaItem(meta_item) = lint
+                                && meta_item.is_word()
+                                && let Some(ident) = meta_item.ident()
+                                && matches!(
+                                    ident.name.as_str(),
+                                    "ambiguous_glob_reexports"
+                                        | "dead_code"
+                                        | "deprecated"
+                                        | "hidden_glob_reexports"
+                                        | "unreachable_pub"
+                                        | "unused"
+                                        | "unused_braces"
+                                        | "unused_import_braces"
+                                        | "unused_imports"
+                                )
                             {
                                 return;
                             }
+
+                            if extract_clippy_lint(lint).is_some_and(|symbol| {
+                                matches!(
+                                    symbol.as_str(),
+                                    "wildcard_imports"
+                                        | "enum_glob_use"
+                                        | "redundant_pub_crate"
+                                        | "macro_use_imports"
+                                        | "unsafe_removed_from_name"
+                                        | "module_name_repetitions"
+                                        | "single_component_path_imports"
+                                        | "disallowed_types"
+                                )
+                            }) {
+                                return;
+                            }
                         },
                         ItemKind::ExternCrate(..) => {
                             if is_word(lint, sym::unused_imports) && skip_unused_imports {
diff --git a/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs
index a3291c9da10..0d9eaac882f 100644
--- a/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs
+++ b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs
@@ -49,7 +49,7 @@ impl LintConfig {
 
 type LintTable = BTreeMap<Spanned<String>, Spanned<LintConfig>>;
 
-#[derive(Deserialize, Debug)]
+#[derive(Deserialize, Debug, Default)]
 struct Lints {
     #[serde(default)]
     rust: LintTable,
@@ -57,9 +57,18 @@ struct Lints {
     clippy: LintTable,
 }
 
+#[derive(Deserialize, Debug, Default)]
+struct Workspace {
+    #[serde(default)]
+    lints: Lints,
+}
+
 #[derive(Deserialize, Debug)]
 struct CargoToml {
+    #[serde(default)]
     lints: Lints,
+    #[serde(default)]
+    workspace: Workspace,
 }
 
 #[derive(Default, Debug)]
@@ -164,5 +173,7 @@ pub fn check(cx: &LateContext<'_>) {
 
         check_table(cx, cargo_toml.lints.rust, &rustc_groups, &file);
         check_table(cx, cargo_toml.lints.clippy, &clippy_groups, &file);
+        check_table(cx, cargo_toml.workspace.lints.rust, &rustc_groups, &file);
+        check_table(cx, cargo_toml.workspace.lints.clippy, &clippy_groups, &file);
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs
index 2b6e17dc103..864489ee3fc 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_sign_loss.rs
@@ -255,8 +255,10 @@ fn expr_add_sign(cx: &LateContext<'_>, expr: &Expr<'_>) -> Sign {
 
 /// Peels binary operators such as [`BinOpKind::Mul`], [`BinOpKind::Div`] or [`BinOpKind::Rem`],
 /// where the result depends on:
+///
 /// - the number of negative values in the entire expression, or
 /// - the number of negative values on the left hand side of the expression.
+///
 /// Ignores overflow.
 ///
 ///
@@ -303,8 +305,10 @@ fn exprs_with_muldiv_binop_peeled<'e>(expr: &'e Expr<'_>) -> Vec<&'e Expr<'e>> {
 }
 
 /// Peels binary operators such as [`BinOpKind::Add`], where the result depends on:
+///
 /// - all the expressions being positive, or
 /// - all the expressions being negative.
+///
 /// Ignores overflow.
 ///
 /// Expressions using other operators are preserved, so we can try to evaluate them later.
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index 5ff7d8e5134..df2ef465700 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -140,6 +140,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::disallowed_names::DISALLOWED_NAMES_INFO,
     crate::disallowed_script_idents::DISALLOWED_SCRIPT_IDENTS_INFO,
     crate::disallowed_types::DISALLOWED_TYPES_INFO,
+    crate::doc::DOC_LAZY_CONTINUATION_INFO,
     crate::doc::DOC_LINK_WITH_QUOTES_INFO,
     crate::doc::DOC_MARKDOWN_INFO,
     crate::doc::EMPTY_DOCS_INFO,
@@ -205,6 +206,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::functions::MUST_USE_CANDIDATE_INFO,
     crate::functions::MUST_USE_UNIT_INFO,
     crate::functions::NOT_UNSAFE_PTR_ARG_DEREF_INFO,
+    crate::functions::RENAMED_FUNCTION_PARAMS_INFO,
     crate::functions::RESULT_LARGE_ERR_INFO,
     crate::functions::RESULT_UNIT_ERR_INFO,
     crate::functions::TOO_MANY_ARGUMENTS_INFO,
@@ -291,9 +293,11 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::loops::SAME_ITEM_PUSH_INFO,
     crate::loops::SINGLE_ELEMENT_LOOP_INFO,
     crate::loops::UNUSED_ENUMERATE_INDEX_INFO,
+    crate::loops::WHILE_FLOAT_INFO,
     crate::loops::WHILE_IMMUTABLE_CONDITION_INFO,
     crate::loops::WHILE_LET_LOOP_INFO,
     crate::loops::WHILE_LET_ON_ITERATOR_INFO,
+    crate::macro_metavars_in_unsafe::MACRO_METAVARS_IN_UNSAFE_INFO,
     crate::macro_use::MACRO_USE_IMPORTS_INFO,
     crate::main_recursion::MAIN_RECURSION_INFO,
     crate::manual_assert::MANUAL_ASSERT_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs
new file mode 100644
index 00000000000..38bc58a5501
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs
@@ -0,0 +1,95 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use itertools::Itertools;
+use rustc_errors::{Applicability, SuggestionStyle};
+use rustc_lint::LateContext;
+use rustc_span::{BytePos, Span};
+use std::ops::Range;
+
+use super::DOC_LAZY_CONTINUATION;
+
+fn map_container_to_text(c: &super::Container) -> &'static str {
+    match c {
+        super::Container::Blockquote => "> ",
+        // numbered list can have up to nine digits, plus the dot, plus four spaces on either side
+        super::Container::List(indent) => &"                  "[0..*indent],
+    }
+}
+
+// TODO: Adjust the parameters as necessary
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    doc: &str,
+    range: Range<usize>,
+    mut span: Span,
+    containers: &[super::Container],
+) {
+    if doc[range.clone()].contains('\t') {
+        // We don't do tab stops correctly.
+        return;
+    }
+
+    let ccount = doc[range.clone()].chars().filter(|c| *c == '>').count();
+    let blockquote_level = containers
+        .iter()
+        .filter(|c| matches!(c, super::Container::Blockquote))
+        .count();
+    let lcount = doc[range.clone()].chars().filter(|c| *c == ' ').count();
+    let list_indentation = containers
+        .iter()
+        .map(|c| {
+            if let super::Container::List(indent) = c {
+                *indent
+            } else {
+                0
+            }
+        })
+        .sum();
+    if ccount < blockquote_level || lcount < list_indentation {
+        let msg = if ccount < blockquote_level {
+            "doc quote missing `>` marker"
+        } else {
+            "doc list item missing indentation"
+        };
+        span_lint_and_then(cx, DOC_LAZY_CONTINUATION, span, msg, |diag| {
+            if ccount == 0 && blockquote_level == 0 {
+                // simpler suggestion style for indentation
+                let indent = list_indentation - lcount;
+                diag.span_suggestion_with_style(
+                    span.shrink_to_hi(),
+                    "indent this line",
+                    std::iter::repeat(" ").take(indent).join(""),
+                    Applicability::MaybeIncorrect,
+                    SuggestionStyle::ShowAlways,
+                );
+                diag.help("if this is supposed to be its own paragraph, add a blank line");
+                return;
+            }
+            let mut doc_start_range = &doc[range];
+            let mut suggested = String::new();
+            for c in containers {
+                let text = map_container_to_text(c);
+                if doc_start_range.starts_with(text) {
+                    doc_start_range = &doc_start_range[text.len()..];
+                    span = span
+                        .with_lo(span.lo() + BytePos(u32::try_from(text.len()).expect("text is not 2**32 or bigger")));
+                } else if matches!(c, super::Container::Blockquote)
+                    && let Some(i) = doc_start_range.find('>')
+                {
+                    doc_start_range = &doc_start_range[i + 1..];
+                    span =
+                        span.with_lo(span.lo() + BytePos(u32::try_from(i).expect("text is not 2**32 or bigger") + 1));
+                } else {
+                    suggested.push_str(text);
+                }
+            }
+            diag.span_suggestion_with_style(
+                span,
+                "add markers to start of line",
+                suggested,
+                Applicability::MachineApplicable,
+                SuggestionStyle::ShowAlways,
+            );
+            diag.help("if this not intended to be a quote at all, escape it with `\\>`");
+        });
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
index 36ba19698c7..010fab803d9 100644
--- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
@@ -1,3 +1,4 @@
+use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_note};
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 use clippy_utils::{is_doc_hidden, return_ty};
@@ -6,15 +7,13 @@ use rustc_lint::LateContext;
 use rustc_middle::ty;
 use rustc_span::{sym, Span};
 
-use super::{DocHeaders, MISSING_ERRORS_DOC, MISSING_PANICS_DOC, MISSING_SAFETY_DOC, UNNECESSARY_SAFETY_DOC};
-
 pub fn check(
     cx: &LateContext<'_>,
     owner_id: OwnerId,
     sig: FnSig<'_>,
     headers: DocHeaders,
     body_id: Option<BodyId>,
-    panic_span: Option<Span>,
+    panic_info: Option<(Span, bool)>,
     check_private_items: bool,
 ) {
     if !check_private_items && !cx.effective_visibilities.is_exported(owner_id.def_id) {
@@ -48,13 +47,13 @@ pub fn check(
         ),
         _ => (),
     }
-    if !headers.panics && panic_span.is_some() {
+    if !headers.panics && panic_info.map_or(false, |el| !el.1) {
         span_lint_and_note(
             cx,
             MISSING_PANICS_DOC,
             span,
             "docs for function which may panic missing `# Panics` section",
-            panic_span,
+            panic_info.map(|el| el.0),
             "first possible panic found here",
         );
     }
diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs
index 7fdb582e640..9f08973948a 100644
--- a/src/tools/clippy/clippy_lints/src/doc/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs
@@ -1,13 +1,14 @@
+mod lazy_continuation;
 use clippy_utils::attrs::is_doc_hidden;
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
 use clippy_utils::macros::{is_panic, root_macro_call_first_node};
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::visitors::Visitable;
-use clippy_utils::{is_entrypoint_fn, is_trait_impl_item, method_chain_args};
+use clippy_utils::{in_constant, is_entrypoint_fn, is_trait_impl_item, method_chain_args};
 use pulldown_cmark::Event::{
     Code, End, FootnoteReference, HardBreak, Html, Rule, SoftBreak, Start, TaskListMarker, Text,
 };
-use pulldown_cmark::Tag::{BlockQuote, CodeBlock, Heading, Item, Link, Paragraph};
+use pulldown_cmark::Tag::{BlockQuote, CodeBlock, FootnoteDefinition, Heading, Item, Link, Paragraph};
 use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options};
 use rustc_ast::ast::Attribute;
 use rustc_data_structures::fx::FxHashSet;
@@ -362,6 +363,63 @@ declare_clippy_lint! {
     "docstrings exist but documentation is empty"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// In CommonMark Markdown, the language used to write doc comments, a
+    /// paragraph nested within a list or block quote does not need any line
+    /// after the first one to be indented or marked. The specification calls
+    /// this a "lazy paragraph continuation."
+    ///
+    /// ### Why is this bad?
+    ///
+    /// This is easy to write but hard to read. Lazy continuations makes
+    /// unintended markers hard to see, and make it harder to deduce the
+    /// document's intended structure.
+    ///
+    /// ### Example
+    ///
+    /// This table is probably intended to have two rows,
+    /// but it does not. It has zero rows, and is followed by
+    /// a block quote.
+    /// ```no_run
+    /// /// Range | Description
+    /// /// ----- | -----------
+    /// /// >= 1  | fully opaque
+    /// /// < 1   | partially see-through
+    /// fn set_opacity(opacity: f32) {}
+    /// ```
+    ///
+    /// Fix it by escaping the marker:
+    /// ```no_run
+    /// /// Range | Description
+    /// /// ----- | -----------
+    /// /// \>= 1 | fully opaque
+    /// /// < 1   | partially see-through
+    /// fn set_opacity(opacity: f32) {}
+    /// ```
+    ///
+    /// This example is actually intended to be a list:
+    /// ```no_run
+    /// /// * Do nothing.
+    /// /// * Then do something. Whatever it is needs done,
+    /// /// it should be done right now.
+    /// # fn do_stuff() {}
+    /// ```
+    ///
+    /// Fix it by indenting the list contents:
+    /// ```no_run
+    /// /// * Do nothing.
+    /// /// * Then do something. Whatever it is needs done,
+    /// ///   it should be done right now.
+    /// # fn do_stuff() {}
+    /// ```
+    #[clippy::version = "1.80.0"]
+    pub DOC_LAZY_CONTINUATION,
+    style,
+    "require every line of a paragraph to be indented and marked"
+}
+
 #[derive(Clone)]
 pub struct Documentation {
     valid_idents: FxHashSet<String>,
@@ -388,6 +446,7 @@ impl_lint_pass!(Documentation => [
     UNNECESSARY_SAFETY_DOC,
     SUSPICIOUS_DOC_COMMENTS,
     EMPTY_DOCS,
+    DOC_LAZY_CONTINUATION,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Documentation {
@@ -402,14 +461,14 @@ impl<'tcx> LateLintPass<'tcx> for Documentation {
                     if !(is_entrypoint_fn(cx, item.owner_id.to_def_id()) || in_external_macro(cx.tcx.sess, item.span)) {
                         let body = cx.tcx.hir().body(body_id);
 
-                        let panic_span = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
+                        let panic_info = FindPanicUnwrap::find_span(cx, cx.tcx.typeck(item.owner_id), body.value);
                         missing_headers::check(
                             cx,
                             item.owner_id,
                             sig,
                             headers,
                             Some(body_id),
-                            panic_span,
+                            panic_info,
                             self.check_private_items,
                         );
                     }
@@ -551,6 +610,7 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
         cx,
         valid_idents,
         parser.into_offset_iter(),
+        &doc,
         Fragments {
             fragments: &fragments,
             doc: &doc,
@@ -560,6 +620,11 @@ fn check_attrs(cx: &LateContext<'_>, valid_idents: &FxHashSet<String>, attrs: &[
 
 const RUST_CODE: &[&str] = &["rust", "no_run", "should_panic", "compile_fail"];
 
+enum Container {
+    Blockquote,
+    List(usize),
+}
+
 /// Checks parsed documentation.
 /// This walks the "events" (think sections of markdown) produced by `pulldown_cmark`,
 /// so lints here will generally access that information.
@@ -569,6 +634,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
     cx: &LateContext<'_>,
     valid_idents: &FxHashSet<String>,
     events: Events,
+    doc: &str,
     fragments: Fragments<'_>,
 ) -> DocHeaders {
     // true if a safety header was found
@@ -576,6 +642,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
     let mut in_code = false;
     let mut in_link = None;
     let mut in_heading = false;
+    let mut in_footnote_definition = false;
     let mut is_rust = false;
     let mut no_test = false;
     let mut ignore = false;
@@ -586,7 +653,11 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
     let mut code_level = 0;
     let mut blockquote_level = 0;
 
-    for (event, range) in events {
+    let mut containers = Vec::new();
+
+    let mut events = events.peekable();
+
+    while let Some((event, range)) = events.next() {
         match event {
             Html(tag) => {
                 if tag.starts_with("<code") {
@@ -599,8 +670,14 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
                     blockquote_level -= 1;
                 }
             },
-            Start(BlockQuote) => blockquote_level += 1,
-            End(BlockQuote) => blockquote_level -= 1,
+            Start(BlockQuote) => {
+                blockquote_level += 1;
+                containers.push(Container::Blockquote);
+            },
+            End(BlockQuote) => {
+                blockquote_level -= 1;
+                containers.pop();
+            },
             Start(CodeBlock(ref kind)) => {
                 in_code = true;
                 if let CodeBlockKind::Fenced(lang) = kind {
@@ -633,6 +710,13 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
                 if let Start(Heading(_, _, _)) = event {
                     in_heading = true;
                 }
+                if let Start(Item) = event {
+                    if let Some((_next_event, next_range)) = events.peek() {
+                        containers.push(Container::List(next_range.start - range.start));
+                    } else {
+                        containers.push(Container::List(0));
+                    }
+                }
                 ticks_unbalanced = false;
                 paragraph_range = range;
             },
@@ -640,6 +724,9 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
                 if let End(Heading(_, _, _)) = event {
                     in_heading = false;
                 }
+                if let End(Item) = event {
+                    containers.pop();
+                }
                 if ticks_unbalanced && let Some(span) = fragments.span(cx, paragraph_range.clone()) {
                     span_lint_and_help(
                         cx,
@@ -658,8 +745,27 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
                 }
                 text_to_check = Vec::new();
             },
+            Start(FootnoteDefinition(..)) => in_footnote_definition = true,
+            End(FootnoteDefinition(..)) => in_footnote_definition = false,
             Start(_tag) | End(_tag) => (), // We don't care about other tags
-            SoftBreak | HardBreak | TaskListMarker(_) | Code(_) | Rule => (),
+            SoftBreak | HardBreak => {
+                if !containers.is_empty()
+                    && let Some((next_event, next_range)) = events.peek()
+                    && let Some(next_span) = fragments.span(cx, next_range.clone())
+                    && let Some(span) = fragments.span(cx, range.clone())
+                    && !in_footnote_definition
+                    && !matches!(next_event, End(_))
+                {
+                    lazy_continuation::check(
+                        cx,
+                        doc,
+                        range.end..next_range.start,
+                        Span::new(span.hi(), next_span.lo(), span.ctxt(), span.parent()),
+                        &containers[..],
+                    );
+                }
+            },
+            TaskListMarker(_) | Code(_) | Rule => (),
             FootnoteReference(text) | Text(text) => {
                 paragraph_range.end = range.end;
                 ticks_unbalanced |= text.contains('`') && !in_code;
@@ -701,6 +807,7 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
 
 struct FindPanicUnwrap<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
+    is_const: bool,
     panic_span: Option<Span>,
     typeck_results: &'tcx ty::TypeckResults<'tcx>,
 }
@@ -710,14 +817,15 @@ impl<'a, 'tcx> FindPanicUnwrap<'a, 'tcx> {
         cx: &'a LateContext<'tcx>,
         typeck_results: &'tcx ty::TypeckResults<'tcx>,
         body: impl Visitable<'tcx>,
-    ) -> Option<Span> {
+    ) -> Option<(Span, bool)> {
         let mut vis = Self {
             cx,
+            is_const: false,
             panic_span: None,
             typeck_results,
         };
         body.visit(&mut vis);
-        vis.panic_span
+        vis.panic_span.map(|el| (el, vis.is_const))
     }
 }
 
@@ -736,6 +844,7 @@ impl<'a, 'tcx> Visitor<'tcx> for FindPanicUnwrap<'a, 'tcx> {
                     "assert" | "assert_eq" | "assert_ne"
                 )
             {
+                self.is_const = in_constant(self.cx, expr.hir_id);
                 self.panic_span = Some(macro_call.span);
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs
index 6715de52649..8d6e27700d8 100644
--- a/src/tools/clippy/clippy_lints/src/escape.rs
+++ b/src/tools/clippy/clippy_lints/src/escape.rs
@@ -104,7 +104,9 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
             too_large_for_stack: self.too_large_for_stack,
         };
 
-        ExprUseVisitor::for_clippy(cx, fn_def_id, &mut v).consume_body(body).into_ok();
+        ExprUseVisitor::for_clippy(cx, fn_def_id, &mut v)
+            .consume_body(body)
+            .into_ok();
 
         for node in v.set {
             span_lint_hir(
diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs
index 724e1843359..3c4a043a732 100644
--- a/src/tools/clippy/clippy_lints/src/explicit_write.rs
+++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs
@@ -1,12 +1,12 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::macros::{find_format_args, format_args_inputs_span};
+use clippy_utils::macros::{format_args_inputs_span, FormatArgsStorage};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::{is_expn_of, path_def_id};
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::{BindingMode, Block, BlockCheckMode, Expr, ExprKind, Node, PatKind, QPath, Stmt, StmtKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::declare_lint_pass;
+use rustc_session::impl_lint_pass;
 use rustc_span::{sym, ExpnId};
 
 declare_clippy_lint! {
@@ -38,7 +38,17 @@ declare_clippy_lint! {
     "using the `write!()` family of functions instead of the `print!()` family of functions, when using the latter would work"
 }
 
-declare_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]);
+pub struct ExplicitWrite {
+    format_args: FormatArgsStorage,
+}
+
+impl ExplicitWrite {
+    pub fn new(format_args: FormatArgsStorage) -> Self {
+        Self { format_args }
+    }
+}
+
+impl_lint_pass!(ExplicitWrite => [EXPLICIT_WRITE]);
 
 impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
@@ -57,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
                 Some(sym::io_stderr) => ("stderr", "e"),
                 _ => return,
             };
-            let Some(format_args) = find_format_args(cx, write_arg, ExpnId::root()) else {
+            let Some(format_args) = self.format_args.get(cx, write_arg, ExpnId::root()) else {
                 return;
             };
 
@@ -83,7 +93,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
             };
             let mut applicability = Applicability::MachineApplicable;
             let inputs_snippet =
-                snippet_with_applicability(cx, format_args_inputs_span(&format_args), "..", &mut applicability);
+                snippet_with_applicability(cx, format_args_inputs_span(format_args), "..", &mut applicability);
             span_lint_and_sugg(
                 cx,
                 EXPLICIT_WRITE,
diff --git a/src/tools/clippy/clippy_lints/src/format.rs b/src/tools/clippy/clippy_lints/src/format.rs
index 8a0cd155d21..0b248f784b7 100644
--- a/src/tools/clippy/clippy_lints/src/format.rs
+++ b/src/tools/clippy/clippy_lints/src/format.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::macros::{find_format_arg_expr, find_format_args, root_macro_call_first_node};
+use clippy_utils::macros::{find_format_arg_expr, root_macro_call_first_node, FormatArgsStorage};
 use clippy_utils::source::{snippet_opt, snippet_with_context};
 use clippy_utils::sugg::Sugg;
 use rustc_ast::{FormatArgsPiece, FormatOptions, FormatTrait};
@@ -7,7 +7,7 @@ use rustc_errors::Applicability;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty;
-use rustc_session::declare_lint_pass;
+use rustc_session::impl_lint_pass;
 use rustc_span::{sym, Span};
 
 declare_clippy_lint! {
@@ -39,13 +39,24 @@ declare_clippy_lint! {
     "useless use of `format!`"
 }
 
-declare_lint_pass!(UselessFormat => [USELESS_FORMAT]);
+#[allow(clippy::module_name_repetitions)]
+pub struct UselessFormat {
+    format_args: FormatArgsStorage,
+}
+
+impl UselessFormat {
+    pub fn new(format_args: FormatArgsStorage) -> Self {
+        Self { format_args }
+    }
+}
+
+impl_lint_pass!(UselessFormat => [USELESS_FORMAT]);
 
 impl<'tcx> LateLintPass<'tcx> for UselessFormat {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if let Some(macro_call) = root_macro_call_first_node(cx, expr)
             && cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id)
-            && let Some(format_args) = find_format_args(cx, expr, macro_call.expn)
+            && let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn)
         {
             let mut applicability = Applicability::MachineApplicable;
             let call_site = macro_call.span;
diff --git a/src/tools/clippy/clippy_lints/src/format_args.rs b/src/tools/clippy/clippy_lints/src/format_args.rs
index 003a9995c15..86115807aa4 100644
--- a/src/tools/clippy/clippy_lints/src/format_args.rs
+++ b/src/tools/clippy/clippy_lints/src/format_args.rs
@@ -3,8 +3,8 @@ use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::is_diag_trait_item;
 use clippy_utils::macros::{
-    find_format_arg_expr, find_format_args, format_arg_removal_span, format_placeholder_format_span, is_assert_macro,
-    is_format_macro, is_panic, matching_root_macro_call, root_macro_call_first_node, FormatParamUsage, MacroCall,
+    find_format_arg_expr, format_arg_removal_span, format_placeholder_format_span, is_assert_macro, is_format_macro,
+    is_panic, matching_root_macro_call, root_macro_call_first_node, FormatArgsStorage, FormatParamUsage, MacroCall,
 };
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{implements_trait, is_type_lang_item};
@@ -167,15 +167,18 @@ impl_lint_pass!(FormatArgs => [
     UNUSED_FORMAT_SPECS,
 ]);
 
+#[allow(clippy::struct_field_names)]
 pub struct FormatArgs {
+    format_args: FormatArgsStorage,
     msrv: Msrv,
     ignore_mixed: bool,
 }
 
 impl FormatArgs {
     #[must_use]
-    pub fn new(msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self {
+    pub fn new(format_args: FormatArgsStorage, msrv: Msrv, allow_mixed_uninlined_format_args: bool) -> Self {
         Self {
+            format_args,
             msrv,
             ignore_mixed: allow_mixed_uninlined_format_args,
         }
@@ -186,13 +189,13 @@ impl<'tcx> LateLintPass<'tcx> for FormatArgs {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         if let Some(macro_call) = root_macro_call_first_node(cx, expr)
             && is_format_macro(cx, macro_call.def_id)
-            && let Some(format_args) = find_format_args(cx, expr, macro_call.expn)
+            && let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn)
         {
             let linter = FormatArgsExpr {
                 cx,
                 expr,
                 macro_call: &macro_call,
-                format_args: &format_args,
+                format_args,
                 ignore_mixed: self.ignore_mixed,
             };
 
diff --git a/src/tools/clippy/clippy_lints/src/format_impl.rs b/src/tools/clippy/clippy_lints/src/format_impl.rs
index 0a52347940a..09be7237b5c 100644
--- a/src/tools/clippy/clippy_lints/src/format_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/format_impl.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg};
-use clippy_utils::macros::{find_format_arg_expr, find_format_args, is_format_macro, root_macro_call_first_node};
+use clippy_utils::macros::{find_format_arg_expr, is_format_macro, root_macro_call_first_node, FormatArgsStorage};
 use clippy_utils::{get_parent_as_impl, is_diag_trait_item, path_to_local, peel_ref_operators};
 use rustc_ast::{FormatArgsPiece, FormatTrait};
 use rustc_errors::Applicability;
@@ -99,13 +99,15 @@ struct FormatTraitNames {
 
 #[derive(Default)]
 pub struct FormatImpl {
+    format_args: FormatArgsStorage,
     // Whether we are inside Display or Debug trait impl - None for neither
     format_trait_impl: Option<FormatTraitNames>,
 }
 
 impl FormatImpl {
-    pub fn new() -> Self {
+    pub fn new(format_args: FormatArgsStorage) -> Self {
         Self {
+            format_args,
             format_trait_impl: None,
         }
     }
@@ -129,6 +131,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl {
         if let Some(format_trait_impl) = self.format_trait_impl {
             let linter = FormatImplExpr {
                 cx,
+                format_args: &self.format_args,
                 expr,
                 format_trait_impl,
             };
@@ -141,6 +144,7 @@ impl<'tcx> LateLintPass<'tcx> for FormatImpl {
 
 struct FormatImplExpr<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
+    format_args: &'a FormatArgsStorage,
     expr: &'tcx Expr<'tcx>,
     format_trait_impl: FormatTraitNames,
 }
@@ -175,7 +179,7 @@ impl<'a, 'tcx> FormatImplExpr<'a, 'tcx> {
         if let Some(outer_macro) = root_macro_call_first_node(self.cx, self.expr)
             && let macro_def_id = outer_macro.def_id
             && is_format_macro(self.cx, macro_def_id)
-            && let Some(format_args) = find_format_args(self.cx, self.expr, outer_macro.expn)
+            && let Some(format_args) = self.format_args.get(self.cx, self.expr, outer_macro.expn)
         {
             for piece in &format_args.template {
                 if let FormatArgsPiece::Placeholder(placeholder) = piece
diff --git a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
index 633ed96d6a6..82ce501bac5 100644
--- a/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
+++ b/src/tools/clippy/clippy_lints/src/from_str_radix_10.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_integer_literal;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
+use clippy_utils::{in_constant, is_integer_literal};
 use rustc_errors::Applicability;
 use rustc_hir::{def, Expr, ExprKind, LangItem, PrimTy, QPath, TyKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -47,6 +47,9 @@ impl<'tcx> LateLintPass<'tcx> for FromStrRadix10 {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, exp: &Expr<'tcx>) {
         if let ExprKind::Call(maybe_path, [src, radix]) = &exp.kind
             && let ExprKind::Path(QPath::TypeRelative(ty, pathseg)) = &maybe_path.kind
+            // do not lint in constant context, because the suggestion won't work.
+            // NB: keep this check until a new `const_trait_impl` is available and stablized.
+            && !in_constant(cx, exp.hir_id)
 
             // check if the first part of the path is some integer primitive
             && let TyKind::Path(ty_qpath) = &ty.kind
diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs
index 9cc51fa8cd5..dfcaac9abef 100644
--- a/src/tools/clippy/clippy_lints/src/functions/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs
@@ -2,15 +2,17 @@ mod impl_trait_in_params;
 mod misnamed_getters;
 mod must_use;
 mod not_unsafe_ptr_arg_deref;
+mod renamed_function_params;
 mod result;
 mod too_many_arguments;
 mod too_many_lines;
 
+use clippy_utils::def_path_def_ids;
 use rustc_hir as hir;
 use rustc_hir::intravisit;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::def_id::LocalDefId;
+use rustc_span::def_id::{DefIdSet, LocalDefId};
 use rustc_span::Span;
 
 declare_clippy_lint! {
@@ -359,13 +361,51 @@ declare_clippy_lint! {
     "`impl Trait` is used in the function's parameters"
 }
 
-#[derive(Copy, Clone)]
-#[allow(clippy::struct_field_names)]
+declare_clippy_lint! {
+    /// ### What it does
+    /// 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.
+    ///
+    /// ### Example
+    /// ```rust
+    /// struct A(u32);
+    ///
+    /// impl PartialEq for A {
+    ///     fn eq(&self, b: &Self) -> bool {
+    ///         self.0 == b.0
+    ///     }
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// struct A(u32);
+    ///
+    /// impl PartialEq for A {
+    ///     fn eq(&self, other: &Self) -> bool {
+    ///         self.0 == other.0
+    ///     }
+    /// }
+    /// ```
+    #[clippy::version = "1.74.0"]
+    pub RENAMED_FUNCTION_PARAMS,
+    restriction,
+    "renamed function parameters in trait implementation"
+}
+
+#[derive(Clone)]
 pub struct Functions {
     too_many_arguments_threshold: u64,
     too_many_lines_threshold: u64,
     large_error_threshold: u64,
     avoid_breaking_exported_api: bool,
+    allow_renamed_params_for: Vec<String>,
+    /// A set of resolved `def_id` of traits that are configured to allow
+    /// function params renaming.
+    trait_ids: DefIdSet,
 }
 
 impl Functions {
@@ -374,12 +414,15 @@ impl Functions {
         too_many_lines_threshold: u64,
         large_error_threshold: u64,
         avoid_breaking_exported_api: bool,
+        allow_renamed_params_for: Vec<String>,
     ) -> Self {
         Self {
             too_many_arguments_threshold,
             too_many_lines_threshold,
             large_error_threshold,
             avoid_breaking_exported_api,
+            allow_renamed_params_for,
+            trait_ids: DefIdSet::default(),
         }
     }
 }
@@ -395,6 +438,7 @@ impl_lint_pass!(Functions => [
     RESULT_LARGE_ERR,
     MISNAMED_GETTERS,
     IMPL_TRAIT_IN_PARAMS,
+    RENAMED_FUNCTION_PARAMS,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Functions {
@@ -424,6 +468,7 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
         must_use::check_impl_item(cx, item);
         result::check_impl_item(cx, item, self.large_error_threshold);
         impl_trait_in_params::check_impl_item(cx, item);
+        renamed_function_params::check_impl_item(cx, item, &self.trait_ids);
     }
 
     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) {
@@ -433,4 +478,12 @@ impl<'tcx> LateLintPass<'tcx> for Functions {
         result::check_trait_item(cx, item, self.large_error_threshold);
         impl_trait_in_params::check_trait_item(cx, item, self.avoid_breaking_exported_api);
     }
+
+    fn check_crate(&mut self, cx: &LateContext<'tcx>) {
+        for path in &self.allow_renamed_params_for {
+            let path_segments: Vec<&str> = path.split("::").collect();
+            let ids = def_path_def_ids(cx, &path_segments);
+            self.trait_ids.extend(ids);
+        }
+    }
 }
diff --git a/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs
new file mode 100644
index 00000000000..c7de0385c02
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/functions/renamed_function_params.rs
@@ -0,0 +1,110 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_errors::{Applicability, MultiSpan};
+use rustc_hir::def_id::{DefId, DefIdSet};
+use rustc_hir::hir_id::OwnerId;
+use rustc_hir::{Impl, ImplItem, ImplItemKind, ImplItemRef, ItemKind, Node, TraitRef};
+use rustc_lint::LateContext;
+use rustc_span::symbol::{kw, Ident, Symbol};
+use rustc_span::Span;
+
+use super::RENAMED_FUNCTION_PARAMS;
+
+pub(super) fn check_impl_item(cx: &LateContext<'_>, item: &ImplItem<'_>, ignored_traits: &DefIdSet) {
+    if !item.span.from_expansion()
+        && let ImplItemKind::Fn(_, body_id) = item.kind
+        && let parent_node = cx.tcx.parent_hir_node(item.hir_id())
+        && let Node::Item(parent_item) = parent_node
+        && let ItemKind::Impl(Impl {
+            items,
+            of_trait: Some(trait_ref),
+            ..
+        }) = &parent_item.kind
+        && let Some(did) = trait_item_def_id_of_impl(items, item.owner_id)
+        && !is_from_ignored_trait(trait_ref, ignored_traits)
+    {
+        let mut param_idents_iter = cx.tcx.hir().body_param_names(body_id);
+        let mut default_param_idents_iter = cx.tcx.fn_arg_names(did).iter().copied();
+
+        let renames = RenamedFnArgs::new(&mut default_param_idents_iter, &mut param_idents_iter);
+        if !renames.0.is_empty() {
+            let multi_span = renames.multi_span();
+            let plural = if renames.0.len() == 1 { "" } else { "s" };
+            span_lint_and_then(
+                cx,
+                RENAMED_FUNCTION_PARAMS,
+                multi_span,
+                format!("renamed function parameter{plural} of trait impl"),
+                |diag| {
+                    diag.multipart_suggestion(
+                        format!("consider using the default name{plural}"),
+                        renames.0,
+                        Applicability::Unspecified,
+                    );
+                },
+            );
+        }
+    }
+}
+
+struct RenamedFnArgs(Vec<(Span, String)>);
+
+impl RenamedFnArgs {
+    /// Comparing between an iterator of default names and one with current names,
+    /// then collect the ones that got renamed.
+    fn new<I, T>(default_names: &mut I, current_names: &mut T) -> Self
+    where
+        I: Iterator<Item = Ident>,
+        T: Iterator<Item = Ident>,
+    {
+        let mut renamed: Vec<(Span, String)> = vec![];
+
+        debug_assert!(default_names.size_hint() == current_names.size_hint());
+        while let (Some(def_name), Some(cur_name)) = (default_names.next(), current_names.next()) {
+            let current_name = cur_name.name;
+            let default_name = def_name.name;
+            if is_unused_or_empty_symbol(current_name) || is_unused_or_empty_symbol(default_name) {
+                continue;
+            }
+            if current_name != default_name {
+                renamed.push((cur_name.span, default_name.to_string()));
+            }
+        }
+
+        Self(renamed)
+    }
+
+    fn multi_span(&self) -> MultiSpan {
+        self.0
+            .iter()
+            .map(|(span, _)| span)
+            .copied()
+            .collect::<Vec<Span>>()
+            .into()
+    }
+}
+
+fn is_unused_or_empty_symbol(symbol: Symbol) -> bool {
+    // FIXME: `body_param_names` currently returning empty symbols for `wild` as well,
+    // so we need to check if the symbol is empty first.
+    // Therefore the check of whether it's equal to [`kw::Underscore`] has no use for now,
+    // but it would be nice to keep it here just to be future-proof.
+    symbol.is_empty() || symbol == kw::Underscore || symbol.as_str().starts_with('_')
+}
+
+/// Get the [`trait_item_def_id`](ImplItemRef::trait_item_def_id) of a relevant impl item.
+fn trait_item_def_id_of_impl(items: &[ImplItemRef], target: OwnerId) -> Option<DefId> {
+    items.iter().find_map(|item| {
+        if item.id.owner_id == target {
+            item.trait_item_def_id
+        } else {
+            None
+        }
+    })
+}
+
+fn is_from_ignored_trait(of_trait: &TraitRef<'_>, ignored_traits: &DefIdSet) -> bool {
+    let Some(trait_did) = of_trait.trait_def_id() else {
+        return false;
+    };
+    ignored_traits.contains(&trait_did)
+}
diff --git a/src/tools/clippy/clippy_lints/src/future_not_send.rs b/src/tools/clippy/clippy_lints/src/future_not_send.rs
index 2c2daac0234..192fb611c2d 100644
--- a/src/tools/clippy/clippy_lints/src/future_not_send.rs
+++ b/src/tools/clippy/clippy_lints/src/future_not_send.rs
@@ -4,8 +4,8 @@ use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Body, FnDecl};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{self, AliasTy, ClauseKind, PredicateKind};
 use rustc_middle::ty::print::PrintTraitRefExt;
+use rustc_middle::ty::{self, AliasTy, ClauseKind, PredicateKind};
 use rustc_session::declare_lint_pass;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::{sym, Span};
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index a8bfbbdd9ec..3328d642bd8 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -61,11 +61,6 @@ extern crate clippy_utils;
 #[macro_use]
 extern crate declare_clippy_lint;
 
-use std::collections::BTreeMap;
-
-use rustc_data_structures::fx::FxHashSet;
-use rustc_lint::{Lint, LintId};
-
 #[cfg(feature = "internal")]
 pub mod deprecated_lints;
 #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
@@ -199,6 +194,7 @@ mod lifetimes;
 mod lines_filter_map_ok;
 mod literal_representation;
 mod loops;
+mod macro_metavars_in_unsafe;
 mod macro_use;
 mod main_recursion;
 mod manual_assert;
@@ -385,6 +381,10 @@ mod zero_sized_map_values;
 // end lints modules, do not remove this comment, it’s used in `update_lints`
 
 use clippy_config::{get_configuration_metadata, Conf};
+use clippy_utils::macros::FormatArgsStorage;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_lint::{Lint, LintId};
+use std::collections::BTreeMap;
 
 /// Register all pre expansion lints
 ///
@@ -533,6 +533,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
         allow_expect_in_tests,
         allow_mixed_uninlined_format_args,
         allow_one_hash_in_raw_strings,
+        allow_panic_in_tests,
         allow_print_in_tests,
         allow_private_module_inception,
         allow_unwrap_in_tests,
@@ -597,9 +598,11 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
         ref allowed_duplicate_crates,
         allow_comparison_to_zero,
         ref allowed_prefixes,
+        ref allow_renamed_params_for,
 
         blacklisted_names: _,
         cyclomatic_complexity_threshold: _,
+        warn_unsafe_macro_metavars_in_private_macros,
     } = *conf;
     let msrv = || msrv.clone();
 
@@ -616,6 +619,14 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
         }
     }
 
+    let format_args_storage = FormatArgsStorage::default();
+    let format_args = format_args_storage.clone();
+    store.register_early_pass(move || {
+        Box::new(utils::format_args_collector::FormatArgsCollector::new(
+            format_args.clone(),
+        ))
+    });
+
     // all the internal lints
     #[cfg(feature = "internal")]
     {
@@ -656,7 +667,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
                 .collect(),
         ))
     });
-    store.register_early_pass(|| Box::<utils::format_args_collector::FormatArgsCollector>::default());
     store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir));
     store.register_late_pass(|_| Box::new(utils::author::Author));
     store.register_late_pass(move |_| {
@@ -698,6 +708,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(|_| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
     store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
     store.register_late_pass(move |_| Box::new(approx_const::ApproxConstant::new(msrv())));
+    let format_args = format_args_storage.clone();
     store.register_late_pass(move |_| {
         Box::new(methods::Methods::new(
             avoid_breaking_exported_api,
@@ -705,6 +716,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
             allow_expect_in_tests,
             allow_unwrap_in_tests,
             allowed_dotfiles.clone(),
+            format_args.clone(),
         ))
     });
     store.register_late_pass(move |_| Box::new(matches::Matches::new(msrv())));
@@ -759,7 +771,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
             allow_in_test: allow_useless_vec_in_tests,
         })
     });
-    store.register_late_pass(|_| Box::new(panic_unimplemented::PanicUnimplemented));
+    store.register_late_pass(move |_| Box::new(panic_unimplemented::PanicUnimplemented { allow_panic_in_tests }));
     store.register_late_pass(|_| Box::new(strings::StringLitAsBytes));
     store.register_late_pass(|_| Box::new(derive::Derive));
     store.register_late_pass(move |_| Box::new(derivable_impls::DerivableImpls::new(msrv())));
@@ -769,7 +781,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(|_| Box::<regex::Regex>::default());
     store.register_late_pass(move |_| Box::new(copies::CopyAndPaste::new(ignore_interior_mutability.clone())));
     store.register_late_pass(|_| Box::new(copy_iterator::CopyIterator));
-    store.register_late_pass(|_| Box::new(format::UselessFormat));
+    let format_args = format_args_storage.clone();
+    store.register_late_pass(move |_| Box::new(format::UselessFormat::new(format_args.clone())));
     store.register_late_pass(|_| Box::new(swap::Swap));
     store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional));
     store.register_late_pass(|_| Box::<new_without_default::NewWithoutDefault>::default());
@@ -780,6 +793,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
             too_many_lines_threshold,
             large_error_threshold,
             avoid_breaking_exported_api,
+            allow_renamed_params_for.clone(),
         ))
     });
     store.register_late_pass(move |_| Box::new(doc::Documentation::new(doc_valid_idents, check_private_items)));
@@ -793,7 +807,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(|_| Box::new(partialeq_ne_impl::PartialEqNeImpl));
     store.register_late_pass(|_| Box::new(unused_io_amount::UnusedIoAmount));
     store.register_late_pass(move |_| Box::new(large_enum_variant::LargeEnumVariant::new(enum_variant_size_threshold)));
-    store.register_late_pass(|_| Box::new(explicit_write::ExplicitWrite));
+    let format_args = format_args_storage.clone();
+    store.register_late_pass(move |_| Box::new(explicit_write::ExplicitWrite::new(format_args.clone())));
     store.register_late_pass(|_| Box::new(needless_pass_by_value::NeedlessPassByValue));
     store.register_late_pass(move |tcx| {
         Box::new(pass_by_ref_or_value::PassByRefOrValue::new(
@@ -835,7 +850,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(move |_| Box::new(mut_key::MutableKeyType::new(ignore_interior_mutability.clone())));
     store.register_early_pass(|| Box::new(reference::DerefAddrOf));
     store.register_early_pass(|| Box::new(double_parens::DoubleParens));
-    store.register_late_pass(|_| Box::new(format_impl::FormatImpl::new()));
+    let format_args = format_args_storage.clone();
+    store.register_late_pass(move |_| Box::new(format_impl::FormatImpl::new(format_args.clone())));
     store.register_early_pass(|| Box::new(unsafe_removed_from_name::UnsafeNameRemoval));
     store.register_early_pass(|| Box::new(else_if_without_else::ElseIfWithoutElse));
     store.register_early_pass(|| Box::new(int_plus_one::IntPlusOne));
@@ -961,8 +977,14 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
             accept_comment_above_attributes,
         ))
     });
-    store
-        .register_late_pass(move |_| Box::new(format_args::FormatArgs::new(msrv(), allow_mixed_uninlined_format_args)));
+    let format_args = format_args_storage.clone();
+    store.register_late_pass(move |_| {
+        Box::new(format_args::FormatArgs::new(
+            format_args.clone(),
+            msrv(),
+            allow_mixed_uninlined_format_args,
+        ))
+    });
     store.register_late_pass(|_| Box::new(trailing_empty_array::TrailingEmptyArray));
     store.register_early_pass(|| Box::new(octal_escapes::OctalEscapes));
     store.register_late_pass(|_| Box::new(needless_late_init::NeedlessLateInit));
@@ -973,7 +995,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(|_| Box::new(default_union_representation::DefaultUnionRepresentation));
     store.register_late_pass(|_| Box::<only_used_in_recursion::OnlyUsedInRecursion>::default());
     store.register_late_pass(move |_| Box::new(dbg_macro::DbgMacro::new(allow_dbg_in_tests)));
-    store.register_late_pass(move |_| Box::new(write::Write::new(allow_print_in_tests)));
+    let format_args = format_args_storage.clone();
+    store.register_late_pass(move |_| Box::new(write::Write::new(format_args.clone(), allow_print_in_tests)));
     store.register_late_pass(move |_| {
         Box::new(cargo::Cargo {
             ignore_publish: cargo_ignore_publish,
@@ -1136,6 +1159,12 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(|_| Box::new(zero_repeat_side_effects::ZeroRepeatSideEffects));
     store.register_late_pass(|_| Box::new(manual_unwrap_or_default::ManualUnwrapOrDefault));
     store.register_late_pass(|_| Box::new(integer_division_remainder_used::IntegerDivisionRemainderUsed));
+    store.register_late_pass(move |_| {
+        Box::new(macro_metavars_in_unsafe::ExprMetavarsInUnsafe {
+            warn_unsafe_macro_metavars_in_private_macros,
+            ..Default::default()
+        })
+    });
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs
index b5e39b33c6a..3dcb050d77e 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs
@@ -17,6 +17,7 @@ mod same_item_push;
 mod single_element_loop;
 mod unused_enumerate_index;
 mod utils;
+mod while_float;
 mod while_immutable_condition;
 mod while_let_loop;
 mod while_let_on_iterator;
@@ -418,6 +419,39 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
+    /// Checks for while loops comparing floating point values.
+    ///
+    /// ### Why is this bad?
+    /// If you increment floating point values, errors can compound,
+    /// so, use integers instead if possible.
+    ///
+    /// ### Known problems
+    /// The lint will catch all while loops comparing floating point
+    /// values without regarding the increment.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// let mut x = 0.0;
+    /// while x < 42.0 {
+    ///     x += 1.0;
+    /// }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```no_run
+    /// let mut x = 0;
+    /// while x < 42 {
+    ///     x += 1;
+    /// }
+    /// ```
+    #[clippy::version = "1.80.0"]
+    pub WHILE_FLOAT,
+    nursery,
+    "while loops comaparing floating point values"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
     /// Checks whether a for loop is being used to push a constant
     /// value into a Vec.
     ///
@@ -706,6 +740,7 @@ impl_lint_pass!(Loops => [
     NEVER_LOOP,
     MUT_RANGE_BOUND,
     WHILE_IMMUTABLE_CONDITION,
+    WHILE_FLOAT,
     SAME_ITEM_PUSH,
     SINGLE_ELEMENT_LOOP,
     MISSING_SPIN_LOOP,
@@ -762,6 +797,7 @@ impl<'tcx> LateLintPass<'tcx> for Loops {
 
         if let Some(higher::While { condition, body, span }) = higher::While::hir(expr) {
             while_immutable_condition::check(cx, condition, body);
+            while_float::check(cx, condition);
             missing_spin_loop::check(cx, condition, body);
             manual_while_let_some::check(cx, condition, body, span);
         }
diff --git a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
index 6b9ecf5f141..6c6a9a1a2e0 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
@@ -60,12 +60,9 @@ fn check_for_mutation(
         span_low: None,
         span_high: None,
     };
-    ExprUseVisitor::for_clippy(
-        cx,
-        body.hir_id.owner.def_id,
-        &mut delegate,
-    )
-    .walk_expr(body).into_ok();
+    ExprUseVisitor::for_clippy(cx, body.hir_id.owner.def_id, &mut delegate)
+        .walk_expr(body)
+        .into_ok();
 
     delegate.mutation_span()
 }
diff --git a/src/tools/clippy/clippy_lints/src/loops/while_float.rs b/src/tools/clippy/clippy_lints/src/loops/while_float.rs
new file mode 100644
index 00000000000..cf62ce29f0c
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/loops/while_float.rs
@@ -0,0 +1,20 @@
+use clippy_utils::diagnostics::span_lint;
+use rustc_hir::ExprKind;
+
+pub(super) fn check(cx: &rustc_lint::LateContext<'_>, condition: &rustc_hir::Expr<'_>) {
+    if let ExprKind::Binary(_op, left, right) = condition.kind
+        && is_float_type(cx, left)
+        && is_float_type(cx, right)
+    {
+        span_lint(
+            cx,
+            super::WHILE_FLOAT,
+            condition.span,
+            "while condition comparing floats",
+        );
+    }
+}
+
+fn is_float_type(cx: &rustc_lint::LateContext<'_>, expr: &rustc_hir::Expr<'_>) -> bool {
+    cx.typeck_results().expr_ty(expr).is_floating_point()
+}
diff --git a/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs
new file mode 100644
index 00000000000..aea3d26e187
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/macro_metavars_in_unsafe.rs
@@ -0,0 +1,256 @@
+use std::collections::btree_map::Entry;
+use std::collections::BTreeMap;
+
+use clippy_utils::diagnostics::span_lint_hir_and_then;
+use clippy_utils::is_lint_allowed;
+use itertools::Itertools;
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::intravisit::{walk_block, walk_expr, walk_stmt, Visitor};
+use rustc_hir::{BlockCheckMode, Expr, ExprKind, HirId, Stmt, UnsafeSource};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::impl_lint_pass;
+use rustc_span::{sym, Span, SyntaxContext};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Looks for macros that expand metavariables in an unsafe block.
+    ///
+    /// ### Why is this bad?
+    /// This hides an unsafe block and allows the user of the macro to write unsafe code without an explicit
+    /// unsafe block at callsite, making it possible to perform unsafe operations in seemingly safe code.
+    ///
+    /// The macro should be restructured so that these metavariables are referenced outside of unsafe blocks
+    /// and that the usual unsafety checks apply to the macro argument.
+    ///
+    /// This is usually done by binding it to a variable outside of the unsafe block
+    /// and then using that variable inside of the block as shown in the example, or by referencing it a second time
+    /// in a safe context, e.g. `if false { $expr }`.
+    ///
+    /// ### Known limitations
+    /// Due to how macros are represented in the compiler at the time Clippy runs its lints,
+    /// it's not possible to look for metavariables in macro definitions directly.
+    ///
+    /// Instead, this lint looks at expansions of macros.
+    /// This leads to false negatives for macros that are never actually invoked.
+    ///
+    /// By default, this lint is rather conservative and will only emit warnings on publicly-exported
+    /// macros from the same crate, because oftentimes private internal macros are one-off macros where
+    /// this lint would just be noise (e.g. macros that generate `impl` blocks).
+    /// The default behavior should help with preventing a high number of such false positives,
+    /// however it can be configured to also emit warnings in private macros if desired.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// /// Gets the first element of a slice
+    /// macro_rules! first {
+    ///     ($slice:expr) => {
+    ///         unsafe {
+    ///             let slice = $slice; // ⚠️ expansion inside of `unsafe {}`
+    ///
+    ///             assert!(!slice.is_empty());
+    ///             // SAFETY: slice is checked to have at least one element
+    ///             slice.first().unwrap_unchecked()
+    ///         }
+    ///     }
+    /// }
+    ///
+    /// assert_eq!(*first!(&[1i32]), 1);
+    ///
+    /// // This will compile as a consequence (note the lack of `unsafe {}`)
+    /// assert_eq!(*first!(std::hint::unreachable_unchecked() as &[i32]), 1);
+    /// ```
+    /// Use instead:
+    /// ```compile_fail
+    /// macro_rules! first {
+    ///     ($slice:expr) => {{
+    ///         let slice = $slice; // ✅ outside of `unsafe {}`
+    ///         unsafe {
+    ///             assert!(!slice.is_empty());
+    ///             // SAFETY: slice is checked to have at least one element
+    ///             slice.first().unwrap_unchecked()
+    ///         }
+    ///     }}
+    /// }
+    ///
+    /// assert_eq!(*first!(&[1]), 1);
+    ///
+    /// // This won't compile:
+    /// assert_eq!(*first!(std::hint::unreachable_unchecked() as &[i32]), 1);
+    /// ```
+    #[clippy::version = "1.80.0"]
+    pub MACRO_METAVARS_IN_UNSAFE,
+    suspicious,
+    "expanding macro metavariables in an unsafe block"
+}
+impl_lint_pass!(ExprMetavarsInUnsafe => [MACRO_METAVARS_IN_UNSAFE]);
+
+#[derive(Clone, Debug)]
+pub enum MetavarState {
+    ReferencedInUnsafe { unsafe_blocks: Vec<HirId> },
+    ReferencedInSafe,
+}
+
+#[derive(Default)]
+pub struct ExprMetavarsInUnsafe {
+    pub warn_unsafe_macro_metavars_in_private_macros: bool,
+    /// A metavariable can be expanded more than once, potentially across multiple bodies, so it
+    /// requires some state kept across HIR nodes to make it possible to delay a warning
+    /// and later undo:
+    ///
+    /// ```ignore
+    /// macro_rules! x {
+    ///     ($v:expr) => {
+    ///         unsafe { $v; } // unsafe context, it might be possible to emit a warning here, so add it to the map
+    ///
+    ///         $v;            // `$v` expanded another time but in a safe context, set to ReferencedInSafe to suppress
+    ///     }
+    /// }
+    /// ```
+    pub metavar_expns: BTreeMap<Span, MetavarState>,
+}
+
+struct BodyVisitor<'a, 'tcx> {
+    /// Stack of unsafe blocks -- the top item always represents the last seen unsafe block from
+    /// within a relevant macro.
+    macro_unsafe_blocks: Vec<HirId>,
+    /// When this is >0, it means that the node currently being visited is "within" a
+    /// macro definition. This is not necessary for correctness, it merely helps reduce the number
+    /// of spans we need to insert into the map, since only spans from macros are relevant.
+    expn_depth: u32,
+    cx: &'a LateContext<'tcx>,
+    lint: &'a mut ExprMetavarsInUnsafe,
+}
+
+fn is_public_macro(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
+    (cx.effective_visibilities.is_exported(def_id) || cx.tcx.has_attr(def_id, sym::macro_export))
+        && !cx.tcx.is_doc_hidden(def_id)
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for BodyVisitor<'a, 'tcx> {
+    fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) {
+        let from_expn = s.span.from_expansion();
+        if from_expn {
+            self.expn_depth += 1;
+        }
+        walk_stmt(self, s);
+        if from_expn {
+            self.expn_depth -= 1;
+        }
+    }
+
+    fn visit_expr(&mut self, e: &'tcx Expr<'tcx>) {
+        let ctxt = e.span.ctxt();
+
+        if let ExprKind::Block(block, _) = e.kind
+            && let BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) = block.rules
+            && !ctxt.is_root()
+            && let Some(macro_def_id) = ctxt.outer_expn_data().macro_def_id
+            && let Some(macro_def_id) = macro_def_id.as_local()
+            && (self.lint.warn_unsafe_macro_metavars_in_private_macros || is_public_macro(self.cx, macro_def_id))
+        {
+            self.macro_unsafe_blocks.push(block.hir_id);
+            walk_block(self, block);
+            self.macro_unsafe_blocks.pop();
+        } else if ctxt.is_root() && self.expn_depth > 0 {
+            let unsafe_block = self.macro_unsafe_blocks.last().copied();
+
+            match (self.lint.metavar_expns.entry(e.span), unsafe_block) {
+                (Entry::Vacant(e), None) => {
+                    e.insert(MetavarState::ReferencedInSafe);
+                },
+                (Entry::Vacant(e), Some(unsafe_block)) => {
+                    e.insert(MetavarState::ReferencedInUnsafe {
+                        unsafe_blocks: vec![unsafe_block],
+                    });
+                },
+                (Entry::Occupied(mut e), None) => {
+                    if let MetavarState::ReferencedInUnsafe { .. } = *e.get() {
+                        e.insert(MetavarState::ReferencedInSafe);
+                    }
+                },
+                (Entry::Occupied(mut e), Some(unsafe_block)) => {
+                    if let MetavarState::ReferencedInUnsafe { unsafe_blocks } = e.get_mut()
+                        && !unsafe_blocks.contains(&unsafe_block)
+                    {
+                        unsafe_blocks.push(unsafe_block);
+                    }
+                },
+            }
+
+            // NB: No need to visit descendant nodes. They're guaranteed to represent the same
+            // metavariable
+        } else {
+            walk_expr(self, e);
+        }
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for ExprMetavarsInUnsafe {
+    fn check_body(&mut self, cx: &LateContext<'tcx>, body: &'tcx rustc_hir::Body<'tcx>) {
+        if is_lint_allowed(cx, MACRO_METAVARS_IN_UNSAFE, body.value.hir_id) {
+            return;
+        }
+
+        // This BodyVisitor is separate and not part of the lint pass because there is no
+        // `check_stmt_post` on `(Late)LintPass`, which we'd need to detect when we're leaving a macro span
+
+        let mut vis = BodyVisitor {
+            #[expect(clippy::bool_to_int_with_if)] // obfuscates the meaning
+            expn_depth: if body.value.span.from_expansion() { 1 } else { 0 },
+            macro_unsafe_blocks: Vec::new(),
+            lint: self,
+            cx
+        };
+        vis.visit_body(body);
+    }
+
+    fn check_crate_post(&mut self, cx: &LateContext<'tcx>) {
+        // Aggregate all unsafe blocks from all spans:
+        // ```
+        // macro_rules! x {
+        //   ($w:expr, $x:expr, $y:expr) => { $w; unsafe { $w; $x; }; unsafe { $x; $y; }; }
+        // }
+        // $w: []  (unsafe#0 is never added because it was referenced in a safe context)
+        // $x: [unsafe#0, unsafe#1]
+        // $y: [unsafe#1]
+        // ```
+        // We want to lint unsafe blocks #0 and #1
+        let bad_unsafe_blocks = self
+            .metavar_expns
+            .iter()
+            .filter_map(|(_, state)| match state {
+                MetavarState::ReferencedInUnsafe { unsafe_blocks } => Some(unsafe_blocks.as_slice()),
+                MetavarState::ReferencedInSafe => None,
+            })
+            .flatten()
+            .copied()
+            .map(|id| {
+                // Remove the syntax context to hide "in this macro invocation" in the diagnostic.
+                // The invocation doesn't matter. Also we want to dedupe by the unsafe block and not by anything
+                // related to the callsite.
+                let span = cx.tcx.hir().span(id);
+
+                (id, Span::new(span.lo(), span.hi(), SyntaxContext::root(), None))
+            })
+            .dedup_by(|&(_, a), &(_, b)| a == b);
+
+        for (id, span) in bad_unsafe_blocks {
+            span_lint_hir_and_then(
+                cx,
+                MACRO_METAVARS_IN_UNSAFE,
+                id,
+                span,
+                "this macro expands metavariables in an unsafe block",
+                |diag| {
+                    diag.note("this allows the user of the macro to write unsafe code outside of an unsafe block");
+                    diag.help(
+                            "consider expanding any metavariables outside of this block, e.g. by storing them in a variable",
+                        );
+                    diag.help(
+                            "... or also expand referenced metavariables in a safe context to require an unsafe block at callsite",
+                        );
+                },
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
index 1eadc200bed..e2ab4415518 100644
--- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
@@ -611,15 +611,22 @@ impl<'tcx> BinaryOp<'tcx> {
 
 /// The clamp meta pattern is a pattern shared between many (but not all) patterns.
 /// In summary, this pattern consists of two if statements that meet many criteria,
+///
 /// - binary operators that are one of [`>`, `<`, `>=`, `<=`].
+///
 /// - Both binary statements must have a shared argument
+///
 ///     - Which can appear on the left or right side of either statement
+///
 ///     - The binary operators must define a finite range for the shared argument. To put this in
 ///       the terms of Rust `std` library, the following ranges are acceptable
+///
 ///         - `Range`
 ///         - `RangeInclusive`
+///
 ///       And all other range types are not accepted. For the purposes of `clamp` it's irrelevant
 ///       whether the range is inclusive or not, the output is the same.
+///
 /// - The result of each if statement must be equal to the argument unique to that if statement. The
 ///   result can not be the shared argument in either case.
 fn is_clamp_meta_pattern<'tcx>(
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
index cd61e733694..da8c918a62b 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
-use clippy_utils::source::snippet;
+use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::{is_lint_allowed, path_to_local, search_same, SpanlessEq, SpanlessHash};
 use core::cmp::Ordering;
 use core::{iter, slice};
@@ -9,9 +9,9 @@ use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
 use rustc_hir::{Arm, Expr, ExprKind, HirId, HirIdMap, HirIdMapEntry, HirIdSet, Pat, PatKind, RangeEnd};
 use rustc_lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
-use rustc_lint::LateContext;
+use rustc_lint::{LateContext, LintContext};
 use rustc_middle::ty;
-use rustc_span::{ErrorGuaranteed, Symbol};
+use rustc_span::{ErrorGuaranteed, Span, Symbol};
 
 use super::MATCH_SAME_ARMS;
 
@@ -110,20 +110,22 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
             && check_same_body()
     };
 
+    let mut appl = Applicability::MaybeIncorrect;
     let indexed_arms: Vec<(usize, &Arm<'_>)> = arms.iter().enumerate().collect();
     for (&(i, arm1), &(j, arm2)) in search_same(&indexed_arms, hash, eq) {
         if matches!(arm2.pat.kind, PatKind::Wild) {
             if !cx.tcx.features().non_exhaustive_omitted_patterns_lint
                 || is_lint_allowed(cx, NON_EXHAUSTIVE_OMITTED_PATTERNS, arm2.hir_id)
             {
+                let arm_span = adjusted_arm_span(cx, arm1.span);
                 span_lint_hir_and_then(
                     cx,
                     MATCH_SAME_ARMS,
                     arm1.hir_id,
-                    arm1.span,
+                    arm_span,
                     "this match arm has an identical body to the `_` wildcard arm",
                     |diag| {
-                        diag.span_suggestion(arm1.span, "try removing the arm", "", Applicability::MaybeIncorrect)
+                        diag.span_suggestion(arm_span, "try removing the arm", "", appl)
                             .help("or try changing either arm body")
                             .span_note(arm2.span, "`_` wildcard arm here");
                     },
@@ -144,23 +146,36 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) {
                 keep_arm.span,
                 "this match arm has an identical body to another arm",
                 |diag| {
-                    let move_pat_snip = snippet(cx, move_arm.pat.span, "<pat2>");
-                    let keep_pat_snip = snippet(cx, keep_arm.pat.span, "<pat1>");
+                    let move_pat_snip = snippet_with_applicability(cx, move_arm.pat.span, "<pat2>", &mut appl);
+                    let keep_pat_snip = snippet_with_applicability(cx, keep_arm.pat.span, "<pat1>", &mut appl);
 
                     diag.span_suggestion(
                         keep_arm.pat.span,
-                        "try merging the arm patterns",
+                        "or try merging the arm patterns",
                         format!("{keep_pat_snip} | {move_pat_snip}"),
-                        Applicability::MaybeIncorrect,
+                        appl,
                     )
-                    .help("or try changing either arm body")
-                    .span_note(move_arm.span, "other arm here");
+                    .span_suggestion(
+                        adjusted_arm_span(cx, move_arm.span),
+                        "and remove this obsolete arm",
+                        "",
+                        appl,
+                    )
+                    .help("try changing either arm body");
                 },
             );
         }
     }
 }
 
+/// Extend arm's span to include the comma and whitespaces after it.
+fn adjusted_arm_span(cx: &LateContext<'_>, span: Span) -> Span {
+    let source_map = cx.sess().source_map();
+    source_map
+        .span_extend_while(span, |c| c == ',' || c.is_ascii_whitespace())
+        .unwrap_or(span)
+}
+
 #[derive(Clone, Copy)]
 enum NormalizedPat<'a> {
     Wild,
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 f775ea072e1..6ac0705abb2 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
@@ -115,45 +115,60 @@ impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
         }
     }
 
-    fn get_type(&self, ex: &'tcx Expr<'_>) -> Ty<'tcx> {
-        self.cx.typeck_results().expr_ty(ex)
+    fn is_sig_drop_expr(&mut self, ex: &'tcx Expr<'_>) -> bool {
+        !ex.is_syntactic_place_expr() && self.has_sig_drop_attr(self.cx.typeck_results().expr_ty(ex))
     }
 
-    fn has_seen_type(&mut self, ty: Ty<'tcx>) -> bool {
-        !self.seen_types.insert(ty)
+    fn has_sig_drop_attr(&mut self, ty: Ty<'tcx>) -> bool {
+        self.seen_types.clear();
+        self.has_sig_drop_attr_impl(ty)
     }
 
-    fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+    fn has_sig_drop_attr_impl(&mut self, ty: Ty<'tcx>) -> bool {
         if let Some(adt) = ty.ty_adt_def() {
-            if get_attr(cx.sess(), cx.tcx.get_attrs_unchecked(adt.did()), "has_significant_drop").count() > 0 {
+            if get_attr(
+                self.cx.sess(),
+                self.cx.tcx.get_attrs_unchecked(adt.did()),
+                "has_significant_drop",
+            )
+            .count()
+                > 0
+            {
                 return true;
             }
         }
 
-        match ty.kind() {
-            rustc_middle::ty::Adt(a, b) => {
-                for f in a.all_fields() {
-                    let ty = f.ty(cx.tcx, b);
-                    if !self.has_seen_type(ty) && self.has_sig_drop_attr(cx, ty) {
-                        return true;
-                    }
-                }
+        if !self.seen_types.insert(ty) {
+            return false;
+        }
 
-                for generic_arg in *b {
-                    if let GenericArgKind::Type(ty) = generic_arg.unpack() {
-                        if self.has_sig_drop_attr(cx, ty) {
-                            return true;
-                        }
-                    }
-                }
-                false
+        let result = match ty.kind() {
+            rustc_middle::ty::Adt(adt, args) => {
+                // if some field has significant drop,
+                adt.all_fields()
+                    .map(|field| field.ty(self.cx.tcx, args))
+                    .any(|ty| self.has_sig_drop_attr_impl(ty))
+                    // or if there is no generic lifetime and..
+                    // (to avoid false positive on `Ref<'a, MutexGuard<Foo>>`)
+                    || (args
+                        .iter()
+                        .all(|arg| !matches!(arg.unpack(), GenericArgKind::Lifetime(_)))
+                        // some generic parameter has significant drop
+                        // (to avoid false negative on `Box<MutexGuard<Foo>>`)
+                        && args
+                            .iter()
+                            .filter_map(|arg| match arg.unpack() {
+                                GenericArgKind::Type(ty) => Some(ty),
+                                _ => None,
+                            })
+                            .any(|ty| self.has_sig_drop_attr_impl(ty)))
             },
-            rustc_middle::ty::Array(ty, _)
-            | rustc_middle::ty::RawPtr(ty, _)
-            | rustc_middle::ty::Ref(_, ty, _)
-            | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty),
+            rustc_middle::ty::Tuple(tys) => tys.iter().any(|ty| self.has_sig_drop_attr_impl(ty)),
+            rustc_middle::ty::Array(ty, _) | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr_impl(*ty),
             _ => false,
-        }
+        };
+
+        result
     }
 }
 
@@ -232,7 +247,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
         if self.current_sig_drop.is_some() {
             return;
         }
-        let ty = self.sig_drop_checker.get_type(expr);
+        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.
@@ -279,11 +294,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
 
 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
-                .has_sig_drop_attr(self.cx, self.sig_drop_checker.get_type(ex))
-        {
+        if !self.is_chain_end && self.sig_drop_checker.is_sig_drop_expr(ex) {
             self.has_significant_drop = true;
             return;
         }
@@ -387,10 +398,7 @@ fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'
 
 impl<'a, 'tcx> Visitor<'tcx> for ArmSigDropHelper<'a, 'tcx> {
     fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) {
-        if self
-            .sig_drop_checker
-            .has_sig_drop_attr(self.sig_drop_checker.cx, self.sig_drop_checker.get_type(ex))
-        {
+        if self.sig_drop_checker.is_sig_drop_expr(ex) {
             self.found_sig_drop_spans.insert(ex.span);
             return;
         }
diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
index fba76852344..c9f56e1d980 100644
--- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::macros::{find_format_args, format_args_inputs_span, root_macro_call_first_node};
+use clippy_utils::macros::{format_args_inputs_span, root_macro_call_first_node, FormatArgsStorage};
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{is_type_diagnostic_item, is_type_lang_item};
 use rustc_errors::Applicability;
@@ -16,6 +16,7 @@ use super::EXPECT_FUN_CALL;
 #[allow(clippy::too_many_lines)]
 pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
+    format_args_storage: &FormatArgsStorage,
     expr: &hir::Expr<'_>,
     method_span: Span,
     name: &str,
@@ -134,9 +135,9 @@ pub(super) fn check<'tcx>(
     // Special handling for `format!` as arg_root
     if let Some(macro_call) = root_macro_call_first_node(cx, arg_root) {
         if cx.tcx.is_diagnostic_item(sym::format_macro, macro_call.def_id)
-            && let Some(format_args) = find_format_args(cx, arg_root, macro_call.expn)
+            && let Some(format_args) = format_args_storage.get(cx, arg_root, macro_call.expn)
         {
-            let span = format_args_inputs_span(&format_args);
+            let span = format_args_inputs_span(format_args);
             let sugg = snippet_with_applicability(cx, span, "..", &mut applicability);
             span_lint_and_sugg(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs
index 12647ea1ffc..b93d51eac64 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_filter.rs
@@ -126,15 +126,15 @@ enum FilterType {
 ///
 /// How this is done:
 /// 1. we know that this is invoked in a method call with `filter` as the method name via `mod.rs`
-/// 2. we check that we are in a trait method. Therefore we are in an
-/// `(x as Iterator).filter({filter_arg})` method call.
+/// 2. we check that we are in a trait method. Therefore we are in an `(x as
+///    Iterator).filter({filter_arg})` method call.
 /// 3. we check that the parent expression is not a map. This is because we don't want to lint
 ///    twice, and we already have a specialized lint for that.
 /// 4. we check that the span of the filter does not contain a comment.
 /// 5. we get the type of the `Item` in the `Iterator`, and compare against the type of Option and
-///   Result.
+///    Result.
 /// 6. we finally check the contents of the filter argument to see if it is a call to `is_some` or
-///   `is_ok`.
+///    `is_ok`.
 /// 7. if all of the above are true, then we return the `FilterType`
 fn expression_type(
     cx: &LateContext<'_>,
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
index 7c852a3768d..05e77386128 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_kv_map.rs
@@ -12,8 +12,10 @@ use rustc_middle::ty;
 use rustc_span::{sym, Span};
 
 /// lint use of:
+///
 /// - `hashmap.iter().map(|(_, v)| v)`
 /// - `hashmap.into_iter().map(|(_, v)| v)`
+///
 /// on `HashMaps` and `BTreeMaps` in std
 
 pub(super) fn check<'tcx>(
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
index a52d38790a2..5ccb5243e90 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_overeager_cloned.rs
@@ -69,12 +69,9 @@ pub(super) fn check<'tcx>(
                 used_move: HirIdSet::default(),
             };
 
-            ExprUseVisitor::for_clippy(
-                cx,
-                closure.def_id,
-                &mut delegate,
-            )
-            .consume_body(body).into_ok();
+            ExprUseVisitor::for_clippy(cx, closure.def_id, &mut delegate)
+                .consume_body(body)
+                .into_ok();
 
             let mut to_be_discarded = false;
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 2b92bff016d..9d67aa23379 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -133,6 +133,7 @@ use bind_instead_of_map::BindInsteadOfMap;
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help};
+use clippy_utils::macros::FormatArgsStorage;
 use clippy_utils::ty::{contains_ty_adt_constructor_opaque, implements_trait, is_copy, is_type_diagnostic_item};
 use clippy_utils::{contains_return, is_bool, is_trait_method, iter_input_pats, peel_blocks, return_ty};
 pub use path_ends_with_ext::DEFAULT_ALLOWED_DOTFILES;
@@ -4087,12 +4088,14 @@ declare_clippy_lint! {
     suspicious,
     "is_empty() called on strings known at compile time"
 }
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Msrv,
     allow_expect_in_tests: bool,
     allow_unwrap_in_tests: bool,
     allowed_dotfiles: FxHashSet<String>,
+    format_args: FormatArgsStorage,
 }
 
 impl Methods {
@@ -4103,6 +4106,7 @@ impl Methods {
         allow_expect_in_tests: bool,
         allow_unwrap_in_tests: bool,
         mut allowed_dotfiles: FxHashSet<String>,
+        format_args: FormatArgsStorage,
     ) -> Self {
         allowed_dotfiles.extend(DEFAULT_ALLOWED_DOTFILES.iter().map(ToString::to_string));
 
@@ -4112,6 +4116,7 @@ impl Methods {
             allow_expect_in_tests,
             allow_unwrap_in_tests,
             allowed_dotfiles,
+            format_args,
         }
     }
 }
@@ -4281,7 +4286,15 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
             ExprKind::MethodCall(method_call, receiver, args, _) => {
                 let method_span = method_call.ident.span;
                 or_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args);
-                expect_fun_call::check(cx, expr, method_span, method_call.ident.as_str(), receiver, args);
+                expect_fun_call::check(
+                    cx,
+                    &self.format_args,
+                    expr,
+                    method_span,
+                    method_call.ident.as_str(),
+                    receiver,
+                    args,
+                );
                 clone_on_copy::check(cx, expr, method_call.ident.name, receiver, args);
                 clone_on_ref_ptr::check(cx, expr, method_call.ident.name, receiver, args);
                 inefficient_to_string::check(cx, expr, method_call.ident.name, receiver, args);
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 520dcb2d52d..7431dc1cf0b 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
@@ -3,10 +3,12 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::higher::ForLoop;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{get_iterator_item_ty, implements_trait};
-use clippy_utils::{fn_def_id, get_parent_expr};
+use clippy_utils::visitors::for_each_expr;
+use clippy_utils::{can_mut_borrow_both, fn_def_id, get_parent_expr, path_to_local};
+use core::ops::ControlFlow;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::DefId;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{BindingMode, Expr, ExprKind, Node, PatKind};
 use rustc_lint::LateContext;
 use rustc_span::{sym, Symbol};
 
@@ -40,6 +42,53 @@ pub fn check_for_loop_iter(
         && !clone_or_copy_needed
         && let Some(receiver_snippet) = snippet_opt(cx, receiver.span)
     {
+        // Issue 12098
+        // https://github.com/rust-lang/rust-clippy/issues/12098
+        // if the assignee have `mut borrow` conflict with the iteratee
+        // the lint should not execute, former didn't consider the mut case
+
+        // check whether `expr` is mutable
+        fn is_mutable(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+            if let Some(hir_id) = path_to_local(expr)
+                && let Node::Pat(pat) = cx.tcx.hir_node(hir_id)
+            {
+                matches!(pat.kind, PatKind::Binding(BindingMode::MUT, ..))
+            } else {
+                true
+            }
+        }
+
+        fn is_caller_or_fields_change(cx: &LateContext<'_>, body: &Expr<'_>, caller: &Expr<'_>) -> bool {
+            let mut change = false;
+            if let ExprKind::Block(block, ..) = body.kind {
+                for_each_expr(block, |e| {
+                    match e.kind {
+                        ExprKind::Assign(assignee, _, _) | ExprKind::AssignOp(_, assignee, _) => {
+                            change |= !can_mut_borrow_both(cx, caller, assignee);
+                        },
+                        _ => {},
+                    }
+                    // the return value has no effect but the function need one return value
+                    ControlFlow::<()>::Continue(())
+                });
+            }
+            change
+        }
+
+        if let ExprKind::Call(_, [child, ..]) = expr.kind {
+            // filter first layer of iterator
+            let mut child = child;
+            // get inner real caller requests for clone
+            while let ExprKind::MethodCall(_, caller, _, _) = child.kind {
+                child = caller;
+            }
+            if is_mutable(cx, child) && is_caller_or_fields_change(cx, body, child) {
+                // skip lint
+                return true;
+            }
+        };
+
+        // the lint should not be executed if no violation happens
         let snippet = if let ExprKind::MethodCall(maybe_iter_method_name, collection, [], _) = receiver.kind
             && maybe_iter_method_name.ident.name == sym::iter
             && let Some(iterator_trait_id) = cx.tcx.get_diagnostic_item(sym::Iterator)
diff --git a/src/tools/clippy/clippy_lints/src/methods/utils.rs b/src/tools/clippy/clippy_lints/src/methods/utils.rs
index c50f24f824a..34d7b9acbe4 100644
--- a/src/tools/clippy/clippy_lints/src/methods/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/utils.rs
@@ -120,6 +120,7 @@ fn pat_bindings(pat: &Pat<'_>) -> Vec<HirId> {
 /// operations performed on `binding_hir_ids` are:
 /// * to take non-mutable references to them
 /// * to use them as non-mutable `&self` in method calls
+///
 /// If any of `binding_hir_ids` is used in any other way, then `clone_or_copy_needed` will be true
 /// when `CloneOrCopyVisitor` is done visiting.
 struct CloneOrCopyVisitor<'cx, 'tcx> {
diff --git a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
index ae6cf992ef7..daf166bad90 100644
--- a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
@@ -1,6 +1,6 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::mir::{enclosing_mir, expr_local, local_assignments, used_exactly_once, PossibleBorrowerMap};
+use clippy_utils::mir::PossibleBorrowerMap;
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::{implements_trait, is_copy};
 use clippy_utils::{expr_use_ctxt, peel_n_hir_expr_refs, DefinedTy, ExprUseNode};
@@ -11,7 +11,6 @@ use rustc_hir::{Body, Expr, ExprKind, Mutability, Path, QPath};
 use rustc_index::bit_set::BitSet;
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::mir::{Rvalue, StatementKind};
 use rustc_middle::ty::{
     self, ClauseKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, ParamTy, ProjectionPredicate, Ty,
 };
@@ -106,7 +105,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
             }
             && let count = needless_borrow_count(
                 cx,
-                &mut self.possible_borrowers,
                 fn_id,
                 cx.typeck_results().node_args(hir_id),
                 i,
@@ -155,11 +153,9 @@ fn path_has_args(p: &QPath<'_>) -> bool {
 /// The following constraints will be checked:
 /// * The borrowed expression meets all the generic type's constraints.
 /// * The generic type appears only once in the functions signature.
-/// * The borrowed value will not be moved if it is used later in the function.
-#[expect(clippy::too_many_arguments)]
+/// * The borrowed value is Copy itself OR not a variable (created by a function call)
 fn needless_borrow_count<'tcx>(
     cx: &LateContext<'tcx>,
-    possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
     fn_id: DefId,
     callee_args: ty::GenericArgsRef<'tcx>,
     arg_index: usize,
@@ -234,9 +230,9 @@ fn needless_borrow_count<'tcx>(
 
         let referent_ty = cx.typeck_results().expr_ty(referent);
 
-        if !is_copy(cx, referent_ty)
-            && (referent_ty.has_significant_drop(cx.tcx, cx.param_env)
-                || !referent_used_exactly_once(cx, possible_borrowers, reference))
+        if (!is_copy(cx, referent_ty) && !referent_ty.is_ref())
+            && let ExprKind::AddrOf(_, _, inner) = reference.kind
+            && !matches!(inner.kind, ExprKind::Call(..) | ExprKind::MethodCall(..))
         {
             return false;
         }
@@ -339,37 +335,6 @@ fn is_mixed_projection_predicate<'tcx>(
     }
 }
 
-fn referent_used_exactly_once<'tcx>(
-    cx: &LateContext<'tcx>,
-    possible_borrowers: &mut Vec<(LocalDefId, PossibleBorrowerMap<'tcx, 'tcx>)>,
-    reference: &Expr<'tcx>,
-) -> bool {
-    if let Some(mir) = enclosing_mir(cx.tcx, reference.hir_id)
-        && let Some(local) = expr_local(cx.tcx, reference)
-        && let [location] = *local_assignments(mir, local).as_slice()
-        && let block_data = &mir.basic_blocks[location.block]
-        && let Some(statement) = block_data.statements.get(location.statement_index)
-        && let StatementKind::Assign(box (_, Rvalue::Ref(_, _, place))) = statement.kind
-        && !place.is_indirect_first_projection()
-    {
-        let body_owner_local_def_id = cx.tcx.hir().enclosing_body_owner(reference.hir_id);
-        if possible_borrowers
-            .last()
-            .map_or(true, |&(local_def_id, _)| local_def_id != body_owner_local_def_id)
-        {
-            possible_borrowers.push((body_owner_local_def_id, PossibleBorrowerMap::new(cx, mir)));
-        }
-        let possible_borrower = &mut possible_borrowers.last_mut().unwrap().1;
-        // If `only_borrowers` were used here, the `copyable_iterator::warn` test would fail. The reason is
-        // that `PossibleBorrowerVisitor::visit_terminator` considers `place.local` a possible borrower of
-        // itself. See the comment in that method for an explanation as to why.
-        possible_borrower.bounded_borrowers(&[local], &[local, place.local], place.local, location)
-            && used_exactly_once(mir, place.local).unwrap_or(false)
-    } else {
-        false
-    }
-}
-
 // Iteratively replaces `param_ty` with `new_ty` in `args`, and similarly for each resulting
 // projected type that is a type parameter. Returns `false` if replacing the types would have an
 // effect on the function signature beyond substituting `new_ty` for `param_ty`.
@@ -408,7 +373,11 @@ fn replace_types<'tcx>(
                     && let Some(term_ty) = projection_predicate.term.ty()
                     && let ty::Param(term_param_ty) = term_ty.kind()
                 {
-                    let projection = projection_predicate.projection_term.with_self_ty(cx.tcx, new_ty).expect_ty(cx.tcx).to_ty(cx.tcx);
+                    let projection = projection_predicate
+                        .projection_term
+                        .with_self_ty(cx.tcx, new_ty)
+                        .expect_ty(cx.tcx)
+                        .to_ty(cx.tcx);
 
                     if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
                         && args[term_param_ty.index as usize] != GenericArg::from(projected_ty)
diff --git a/src/tools/clippy/clippy_lints/src/needless_continue.rs b/src/tools/clippy/clippy_lints/src/needless_continue.rs
index 8b4a12bb766..b97cb4579ca 100644
--- a/src/tools/clippy/clippy_lints/src/needless_continue.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_continue.rs
@@ -178,8 +178,7 @@ impl EarlyLintPass for NeedlessContinue {
 /// Given an expression, returns true if either of the following is true
 ///
 /// - The expression is a `continue` node.
-/// - The expression node is a block with the first statement being a
-/// `continue`.
+/// - The expression node is a block with the first statement being a `continue`.
 fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>) -> bool {
     match else_expr.kind {
         ast::ExprKind::Block(ref else_block, _) => is_first_block_stmt_continue(else_block, label),
diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs
index 6605d1fa51a..5a0ae1a4d6d 100644
--- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs
@@ -273,24 +273,16 @@ fn check<'tcx>(
                 msg_span,
                 "unneeded late initialization",
                 |diag| {
-                    diag.tool_only_span_suggestion(
-                        local_stmt.span,
-                        "remove the local",
-                        "",
-                        Applicability::MachineApplicable,
-                    );
-
-                    diag.span_suggestion(
-                        assign.lhs_span,
-                        format!("declare `{binding_name}` here"),
-                        let_snippet,
+                    diag.multipart_suggestion(
+                        format!("move the declaration `{binding_name}` here"),
+                        vec![(local_stmt.span, String::new()), (assign.lhs_span, let_snippet)],
                         Applicability::MachineApplicable,
                     );
                 },
             );
         },
         ExprKind::If(cond, then_expr, Some(else_expr)) if !contains_let(cond) => {
-            let (applicability, suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?;
+            let (applicability, mut suggestions) = assignment_suggestions(cx, binding_id, [then_expr, else_expr])?;
 
             span_lint_and_then(
                 cx,
@@ -298,30 +290,26 @@ fn check<'tcx>(
                 local_stmt.span,
                 "unneeded late initialization",
                 |diag| {
-                    diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
-
-                    diag.span_suggestion_verbose(
-                        usage.stmt.span.shrink_to_lo(),
-                        format!("declare `{binding_name}` here"),
-                        format!("{let_snippet} = "),
-                        applicability,
-                    );
-
-                    diag.multipart_suggestion("remove the assignments from the branches", suggestions, applicability);
+                    suggestions.push((local_stmt.span, String::new()));
+                    suggestions.push((usage.stmt.span.shrink_to_lo(), format!("{let_snippet} = ")));
 
                     if usage.needs_semi {
-                        diag.span_suggestion(
-                            usage.stmt.span.shrink_to_hi(),
-                            "add a semicolon after the `if` expression",
-                            ";",
-                            applicability,
-                        );
+                        suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned()));
                     }
+
+                    diag.multipart_suggestion(
+                        format!(
+                            "move the declaration `{binding_name}` here and remove the assignments from the branches"
+                        ),
+                        suggestions,
+                        applicability,
+                    );
                 },
             );
         },
         ExprKind::Match(_, arms, MatchSource::Normal) => {
-            let (applicability, suggestions) = assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?;
+            let (applicability, mut suggestions) =
+                assignment_suggestions(cx, binding_id, arms.iter().map(|arm| arm.body))?;
 
             span_lint_and_then(
                 cx,
@@ -329,29 +317,18 @@ fn check<'tcx>(
                 local_stmt.span,
                 "unneeded late initialization",
                 |diag| {
-                    diag.tool_only_span_suggestion(local_stmt.span, "remove the local", String::new(), applicability);
+                    suggestions.push((local_stmt.span, String::new()));
+                    suggestions.push((usage.stmt.span.shrink_to_lo(), format!("{let_snippet} = ")));
 
-                    diag.span_suggestion_verbose(
-                        usage.stmt.span.shrink_to_lo(),
-                        format!("declare `{binding_name}` here"),
-                        format!("{let_snippet} = "),
-                        applicability,
-                    );
+                    if usage.needs_semi {
+                        suggestions.push((usage.stmt.span.shrink_to_hi(), ";".to_owned()));
+                    }
 
                     diag.multipart_suggestion(
-                        "remove the assignments from the `match` arms",
+                        format!("move the declaration `{binding_name}` here and remove the assignments from the `match` arms"),
                         suggestions,
                         applicability,
                     );
-
-                    if usage.needs_semi {
-                        diag.span_suggestion(
-                            usage.stmt.span.shrink_to_hi(),
-                            "add a semicolon after the `match` expression",
-                            ";",
-                            applicability,
-                        );
-                    }
                 },
             );
         },
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
index 9b852f52ea1..da6ed5fb96f 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
@@ -117,7 +117,9 @@ fn check_closures<'tcx>(
             .associated_body()
             .map(|(_, body_id)| hir.body(body_id))
         {
-            euv::ExprUseVisitor::for_clippy(cx, closure, &mut *ctx).consume_body(body).into_ok();
+            euv::ExprUseVisitor::for_clippy(cx, closure, &mut *ctx)
+                .consume_body(body)
+                .into_ok();
         }
     }
 }
@@ -194,7 +196,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
                 async_closures: FxHashSet::default(),
                 tcx: cx.tcx,
             };
-            euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx).consume_body(body).into_ok();
+            euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx)
+                .consume_body(body)
+                .into_ok();
 
             let mut checked_closures = FxHashSet::default();
 
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index 0986571d0f2..f2e00cef7e9 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -133,7 +133,9 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
         // function body.
         let MovedVariablesCtxt { moved_vars } = {
             let mut ctx = MovedVariablesCtxt::default();
-            euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx).consume_body(body).into_ok();
+            euv::ExprUseVisitor::for_clippy(cx, fn_def_id, &mut ctx)
+                .consume_body(body)
+                .into_ok();
             ctx
         };
 
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index f915145e794..87f886b1128 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -94,7 +94,6 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect {
 
     fn check_block_post(&mut self, cx: &LateContext<'tcx>, _: &'tcx rustc_hir::Block<'tcx>) {
         for hir_id in self.local_bindings.pop().unwrap() {
-            // FIXME(rust/#120456) - is `swap_remove` correct?
             if let Some(span) = self.underscore_bindings.swap_remove(&hir_id) {
                 span_lint_hir(
                     cx,
@@ -109,7 +108,6 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect {
 
     fn check_expr(&mut self, _: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
         if let Some(def_id) = path_to_local(expr) {
-            // FIXME(rust/#120456) - is `swap_remove` correct?
             self.underscore_bindings.swap_remove(&def_id);
         }
     }
@@ -118,7 +116,11 @@ impl<'tcx> LateLintPass<'tcx> for NoEffect {
 impl NoEffect {
     fn check_no_effect(&mut self, cx: &LateContext<'_>, stmt: &Stmt<'_>) -> bool {
         if let StmtKind::Semi(expr) = stmt.kind {
-            // move `expr.span.from_expansion()` ahead
+            // Covered by rustc `path_statements` lint
+            if matches!(expr.kind, ExprKind::Path(_)) {
+                return true;
+            }
+
             if expr.span.from_expansion() {
                 return false;
             }
diff --git a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
index ef51a9a9a1c..75066c1f0d2 100644
--- a/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
+++ b/src/tools/clippy/clippy_lints/src/panic_unimplemented.rs
@@ -1,8 +1,14 @@
 use clippy_utils::diagnostics::span_lint;
+use clippy_utils::is_in_test;
 use clippy_utils::macros::{is_panic, root_macro_call_first_node};
 use rustc_hir::Expr;
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::declare_lint_pass;
+use rustc_session::impl_lint_pass;
+
+#[derive(Clone)]
+pub struct PanicUnimplemented {
+    pub allow_panic_in_tests: bool,
+}
 
 declare_clippy_lint! {
     /// ### What it does
@@ -77,7 +83,7 @@ declare_clippy_lint! {
     "usage of the `unreachable!` macro"
 }
 
-declare_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]);
+impl_lint_pass!(PanicUnimplemented => [UNIMPLEMENTED, UNREACHABLE, TODO, PANIC]);
 
 impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
@@ -85,7 +91,9 @@ impl<'tcx> LateLintPass<'tcx> for PanicUnimplemented {
             return;
         };
         if is_panic(cx, macro_call.def_id) {
-            if cx.tcx.hir().is_inside_const_context(expr.hir_id) {
+            if cx.tcx.hir().is_inside_const_context(expr.hir_id)
+                || self.allow_panic_in_tests && is_in_test(cx.tcx, expr.hir_id)
+            {
                 return;
             }
 
diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs
index 87a3c3874d7..292124196ff 100644
--- a/src/tools/clippy/clippy_lints/src/strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strings.rs
@@ -389,6 +389,10 @@ declare_lint_pass!(StrToString => [STR_TO_STRING]);
 
 impl<'tcx> LateLintPass<'tcx> for StrToString {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
+        if expr.span.from_expansion() {
+            return;
+        }
+
         if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind
             && path.ident.name == sym::to_string
             && let ty = cx.typeck_results().expr_ty(self_arg)
@@ -437,6 +441,10 @@ declare_lint_pass!(StringToString => [STRING_TO_STRING]);
 
 impl<'tcx> LateLintPass<'tcx> for StringToString {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
+        if expr.span.from_expansion() {
+            return;
+        }
+
         if let ExprKind::MethodCall(path, self_arg, ..) = &expr.kind
             && path.ident.name == sym::to_string
             && let ty = cx.typeck_results().expr_ty(self_arg)
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs
index 7d824ef2139..3729dfd3e86 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_int_to_non_zero.rs
@@ -28,7 +28,7 @@ pub(super) fn check<'tcx>(
 
     let int_ty = substs.type_at(0);
     if from_ty != int_ty {
-      return false;
+        return false;
     }
 
     span_lint_and_then(
diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs
index 5b2841dcd83..c0d9bcdd259 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap.rs
@@ -251,11 +251,7 @@ impl<'a, 'tcx> UnwrappableVariablesVisitor<'a, 'tcx> {
                 local_id: unwrap_info.local_id,
             };
 
-            let vis = ExprUseVisitor::for_clippy(
-                self.cx,
-                cond.hir_id.owner.def_id,
-                &mut delegate,
-            );
+            let vis = ExprUseVisitor::for_clippy(self.cx, cond.hir_id.owner.def_id, &mut delegate);
             vis.walk_expr(cond).into_ok();
             vis.walk_expr(branch).into_ok();
 
diff --git a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
index 58e66c9f9b9..5acfd35fd6a 100644
--- a/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/format_args_collector.rs
@@ -1,4 +1,4 @@
-use clippy_utils::macros::AST_FORMAT_ARGS;
+use clippy_utils::macros::FormatArgsStorage;
 use clippy_utils::source::snippet_opt;
 use itertools::Itertools;
 use rustc_ast::{Crate, Expr, ExprKind, FormatArgs};
@@ -9,13 +9,20 @@ use rustc_session::impl_lint_pass;
 use rustc_span::{hygiene, Span};
 use std::iter::once;
 use std::mem;
-use std::rc::Rc;
 
-/// Collects [`rustc_ast::FormatArgs`] so that future late passes can call
-/// [`clippy_utils::macros::find_format_args`]
-#[derive(Default)]
+/// Populates [`FormatArgsStorage`] with AST [`FormatArgs`] nodes
 pub struct FormatArgsCollector {
-    format_args: FxHashMap<Span, Rc<FormatArgs>>,
+    format_args: FxHashMap<Span, FormatArgs>,
+    storage: FormatArgsStorage,
+}
+
+impl FormatArgsCollector {
+    pub fn new(storage: FormatArgsStorage) -> Self {
+        Self {
+            format_args: FxHashMap::default(),
+            storage,
+        }
+    }
 }
 
 impl_lint_pass!(FormatArgsCollector => []);
@@ -27,16 +34,12 @@ impl EarlyLintPass for FormatArgsCollector {
                 return;
             }
 
-            self.format_args
-                .insert(expr.span.with_parent(None), Rc::new((**args).clone()));
+            self.format_args.insert(expr.span.with_parent(None), (**args).clone());
         }
     }
 
     fn check_crate_post(&mut self, _: &EarlyContext<'_>, _: &Crate) {
-        AST_FORMAT_ARGS.with(|ast_format_args| {
-            let result = ast_format_args.set(mem::take(&mut self.format_args));
-            debug_assert!(result.is_ok());
-        });
+        self.storage.set(mem::take(&mut self.format_args));
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index 26c6859233d..ff6ee0d10ad 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
-use clippy_utils::macros::{find_format_args, format_arg_removal_span, root_macro_call_first_node, MacroCall};
+use clippy_utils::macros::{format_arg_removal_span, root_macro_call_first_node, FormatArgsStorage, MacroCall};
 use clippy_utils::source::{expand_past_previous_comma, snippet_opt};
 use clippy_utils::{is_in_cfg_test, is_in_test_function};
 use rustc_ast::token::LitKind;
@@ -236,13 +236,15 @@ declare_clippy_lint! {
 
 #[derive(Default)]
 pub struct Write {
+    format_args: FormatArgsStorage,
     in_debug_impl: bool,
     allow_print_in_tests: bool,
 }
 
 impl Write {
-    pub fn new(allow_print_in_tests: bool) -> Self {
+    pub fn new(format_args: FormatArgsStorage, allow_print_in_tests: bool) -> Self {
         Self {
+            format_args,
             allow_print_in_tests,
             ..Default::default()
         }
@@ -307,7 +309,7 @@ impl<'tcx> LateLintPass<'tcx> for Write {
             _ => return,
         }
 
-        if let Some(format_args) = find_format_args(cx, expr, macro_call.expn) {
+        if let Some(format_args) = self.format_args.get(cx, expr, macro_call.expn) {
             // ignore `writeln!(w)` and `write!(v, some_macro!())`
             if format_args.span.from_expansion() {
                 return;
@@ -315,15 +317,15 @@ impl<'tcx> LateLintPass<'tcx> for Write {
 
             match diag_name {
                 sym::print_macro | sym::eprint_macro | sym::write_macro => {
-                    check_newline(cx, &format_args, &macro_call, name);
+                    check_newline(cx, format_args, &macro_call, name);
                 },
                 sym::println_macro | sym::eprintln_macro | sym::writeln_macro => {
-                    check_empty_string(cx, &format_args, &macro_call, name);
+                    check_empty_string(cx, format_args, &macro_call, name);
                 },
                 _ => {},
             }
 
-            check_literal(cx, &format_args, name);
+            check_literal(cx, format_args, name);
 
             if !self.in_debug_impl {
                 for piece in &format_args.template {
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 99d7aba2f7a..4c603bda770 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -193,6 +193,21 @@ pub fn find_binding_init<'tcx>(cx: &LateContext<'tcx>, hir_id: HirId) -> Option<
     None
 }
 
+/// Checks if the given local has an initializer or is from something other than a `let` statement
+///
+/// e.g. returns true for `x` in `fn f(x: usize) { .. }` and `let x = 1;` but false for `let x;`
+pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
+    for (_, node) in cx.tcx.hir().parent_iter(local) {
+        match node {
+            Node::Pat(..) | Node::PatField(..) => {},
+            Node::LetStmt(let_stmt) => return let_stmt.init.is_some(),
+            _ => return true,
+        }
+    }
+
+    false
+}
+
 /// Returns `true` if the given `NodeId` is inside a constant context
 ///
 /// # Example
@@ -1499,15 +1514,18 @@ pub fn is_else_clause_in_let_else(tcx: TyCtxt<'_>, expr: &Expr<'_>) -> bool {
 }
 
 /// Checks whether the given `Expr` is a range equivalent to a `RangeFull`.
+///
 /// For the lower bound, this means that:
 /// - either there is none
 /// - or it is the smallest value that can be represented by the range's integer type
+///
 /// For the upper bound, this means that:
 /// - either there is none
 /// - or it is the largest value that can be represented by the range's integer type and is
 ///   inclusive
 /// - or it is a call to some container's `len` method and is exclusive, and the range is passed to
 ///   a method call on that same container (e.g. `v.drain(..v.len())`)
+///
 /// If the given `Expr` is not some kind of range, the function returns `false`.
 pub fn is_range_full(cx: &LateContext<'_>, expr: &Expr<'_>, container_path: Option<&Path<'_>>) -> bool {
     let ty = cx.typeck_results().expr_ty(expr);
diff --git a/src/tools/clippy/clippy_utils/src/macros.rs b/src/tools/clippy/clippy_utils/src/macros.rs
index 257dd76ab15..8daab9b0d92 100644
--- a/src/tools/clippy/clippy_utils/src/macros.rs
+++ b/src/tools/clippy/clippy_utils/src/macros.rs
@@ -5,15 +5,13 @@ use crate::visitors::{for_each_expr, Descend};
 use arrayvec::ArrayVec;
 use rustc_ast::{FormatArgs, FormatArgument, FormatPlaceholder};
 use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::sync::{Lrc, OnceLock};
 use rustc_hir::{self as hir, Expr, ExprKind, HirId, Node, QPath};
 use rustc_lint::LateContext;
 use rustc_span::def_id::DefId;
 use rustc_span::hygiene::{self, MacroKind, SyntaxContext};
 use rustc_span::{sym, BytePos, ExpnData, ExpnId, ExpnKind, Span, SpanData, Symbol};
-use std::cell::OnceCell;
 use std::ops::ControlFlow;
-use std::rc::Rc;
-use std::sync::atomic::{AtomicBool, Ordering};
 
 const FORMAT_MACRO_DIAG_ITEMS: &[Symbol] = &[
     sym::assert_eq_macro,
@@ -388,50 +386,44 @@ fn is_assert_arg(cx: &LateContext<'_>, expr: &Expr<'_>, assert_expn: ExpnId) ->
     }
 }
 
-thread_local! {
-    /// We preserve the [`FormatArgs`] structs from the early pass for use in the late pass to be
-    /// able to access the many features of a [`LateContext`].
-    ///
-    /// A thread local is used because [`FormatArgs`] is `!Send` and `!Sync`, we are making an
-    /// assumption that the early pass that populates the map and the later late passes will all be
-    /// running on the same thread.
-    #[doc(hidden)]
-    pub static AST_FORMAT_ARGS: OnceCell<FxHashMap<Span, Rc<FormatArgs>>> = {
-        static CALLED: AtomicBool = AtomicBool::new(false);
-        debug_assert!(
-            !CALLED.swap(true, Ordering::SeqCst),
-            "incorrect assumption: `AST_FORMAT_ARGS` should only be accessed by a single thread",
-        );
-
-        OnceCell::new()
-    };
-}
+/// Stores AST [`FormatArgs`] nodes for use in late lint passes, as they are in a desugared form in
+/// the HIR
+#[derive(Default, Clone)]
+pub struct FormatArgsStorage(Lrc<OnceLock<FxHashMap<Span, FormatArgs>>>);
 
-/// Returns an AST [`FormatArgs`] node if a `format_args` expansion is found as a descendant of
-/// `expn_id`
-pub fn find_format_args(cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option<Rc<FormatArgs>> {
-    let format_args_expr = for_each_expr(start, |expr| {
-        let ctxt = expr.span.ctxt();
-        if ctxt.outer_expn().is_descendant_of(expn_id) {
-            if macro_backtrace(expr.span)
-                .map(|macro_call| cx.tcx.item_name(macro_call.def_id))
-                .any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))
-            {
-                ControlFlow::Break(expr)
+impl FormatArgsStorage {
+    /// Returns an AST [`FormatArgs`] node if a `format_args` expansion is found as a descendant of
+    /// `expn_id`
+    ///
+    /// See also [`find_format_arg_expr`]
+    pub fn get(&self, cx: &LateContext<'_>, start: &Expr<'_>, expn_id: ExpnId) -> Option<&FormatArgs> {
+        let format_args_expr = for_each_expr(start, |expr| {
+            let ctxt = expr.span.ctxt();
+            if ctxt.outer_expn().is_descendant_of(expn_id) {
+                if macro_backtrace(expr.span)
+                    .map(|macro_call| cx.tcx.item_name(macro_call.def_id))
+                    .any(|name| matches!(name, sym::const_format_args | sym::format_args | sym::format_args_nl))
+                {
+                    ControlFlow::Break(expr)
+                } else {
+                    ControlFlow::Continue(Descend::Yes)
+                }
             } else {
-                ControlFlow::Continue(Descend::Yes)
+                ControlFlow::Continue(Descend::No)
             }
-        } else {
-            ControlFlow::Continue(Descend::No)
-        }
-    })?;
+        })?;
 
-    AST_FORMAT_ARGS.with(|ast_format_args| {
-        ast_format_args
-            .get()?
-            .get(&format_args_expr.span.with_parent(None))
-            .cloned()
-    })
+        debug_assert!(self.0.get().is_some(), "`FormatArgsStorage` not yet populated");
+
+        self.0.get()?.get(&format_args_expr.span.with_parent(None))
+    }
+
+    /// Should only be called by `FormatArgsCollector`
+    pub fn set(&self, format_args: FxHashMap<Span, FormatArgs>) {
+        self.0
+            .set(format_args)
+            .expect("`FormatArgsStorage::set` should only be called once");
+    }
 }
 
 /// Attempt to find the [`rustc_hir::Expr`] that corresponds to the [`FormatArgument`]'s value, if
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index ff9f06531ea..8ee7d87acb3 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -3,7 +3,7 @@
 // of terminologies might not be relevant in the context of Clippy. Note that its behavior might
 // differ from the time of `rustc` even if the name stays the same.
 
-use clippy_config::msrvs::Msrv;
+use clippy_config::msrvs::{self, Msrv};
 use hir::LangItem;
 use rustc_attr::StableSince;
 use rustc_const_eval::transform::check_consts::ConstCx;
@@ -42,7 +42,7 @@ pub fn is_min_const_fn<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, msrv: &Msrv)
     for bb in &*body.basic_blocks {
         check_terminator(tcx, body, bb.terminator(), msrv)?;
         for stmt in &bb.statements {
-            check_statement(tcx, body, def_id, stmt)?;
+            check_statement(tcx, body, def_id, stmt, msrv)?;
         }
     }
     Ok(())
@@ -102,13 +102,14 @@ fn check_rvalue<'tcx>(
     def_id: DefId,
     rvalue: &Rvalue<'tcx>,
     span: Span,
+    msrv: &Msrv,
 ) -> McfResult {
     match rvalue {
         Rvalue::ThreadLocalRef(_) => Err((span, "cannot access thread local storage in const fn".into())),
         Rvalue::Len(place) | Rvalue::Discriminant(place) | Rvalue::Ref(_, _, place) | Rvalue::AddressOf(_, place) => {
-            check_place(tcx, *place, span, body)
+            check_place(tcx, *place, span, body, msrv)
         },
-        Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body),
+        Rvalue::CopyForDeref(place) => check_place(tcx, *place, span, body, msrv),
         Rvalue::Repeat(operand, _)
         | Rvalue::Use(operand)
         | Rvalue::Cast(
@@ -122,7 +123,7 @@ fn check_rvalue<'tcx>(
             | CastKind::PointerCoercion(PointerCoercion::MutToConstPointer | PointerCoercion::ArrayToPointer),
             operand,
             _,
-        ) => check_operand(tcx, operand, span, body),
+        ) => check_operand(tcx, operand, span, body, msrv),
         Rvalue::Cast(
             CastKind::PointerCoercion(
                 PointerCoercion::UnsafeFnPointer
@@ -133,15 +134,13 @@ fn check_rvalue<'tcx>(
             _,
         ) => Err((span, "function pointer casts are not allowed in const fn".into())),
         Rvalue::Cast(CastKind::PointerCoercion(PointerCoercion::Unsize), op, cast_ty) => {
-            let pointee_ty = if let Some(deref_ty) = cast_ty.builtin_deref(true) {
-                deref_ty
-            } else {
+            let Some(pointee_ty) = cast_ty.builtin_deref(true) else {
                 // We cannot allow this for now.
                 return Err((span, "unsizing casts are only allowed for references right now".into()));
             };
             let unsized_ty = tcx.struct_tail_erasing_lifetimes(pointee_ty, tcx.param_env(def_id));
             if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
-                check_operand(tcx, op, span, body)?;
+                check_operand(tcx, op, span, body, msrv)?;
                 // Casting/coercing things to slices is fine.
                 Ok(())
             } else {
@@ -162,8 +161,8 @@ fn check_rvalue<'tcx>(
         )),
         // binops are fine on integers
         Rvalue::BinaryOp(_, box (lhs, rhs)) => {
-            check_operand(tcx, lhs, span, body)?;
-            check_operand(tcx, rhs, span, body)?;
+            check_operand(tcx, lhs, span, body, msrv)?;
+            check_operand(tcx, rhs, span, body, msrv)?;
             let ty = lhs.ty(body, tcx);
             if ty.is_integral() || ty.is_bool() || ty.is_char() {
                 Ok(())
@@ -179,14 +178,14 @@ fn check_rvalue<'tcx>(
         Rvalue::UnaryOp(_, operand) => {
             let ty = operand.ty(body, tcx);
             if ty.is_integral() || ty.is_bool() {
-                check_operand(tcx, operand, span, body)
+                check_operand(tcx, operand, span, body, msrv)
             } else {
                 Err((span, "only int and `bool` operations are stable in const fn".into()))
             }
         },
         Rvalue::Aggregate(_, operands) => {
             for operand in operands {
-                check_operand(tcx, operand, span, body)?;
+                check_operand(tcx, operand, span, body, msrv)?;
             }
             Ok(())
         },
@@ -198,28 +197,29 @@ fn check_statement<'tcx>(
     body: &Body<'tcx>,
     def_id: DefId,
     statement: &Statement<'tcx>,
+    msrv: &Msrv,
 ) -> McfResult {
     let span = statement.source_info.span;
     match &statement.kind {
         StatementKind::Assign(box (place, rval)) => {
-            check_place(tcx, *place, span, body)?;
-            check_rvalue(tcx, body, def_id, rval, span)
+            check_place(tcx, *place, span, body, msrv)?;
+            check_rvalue(tcx, body, def_id, rval, span, msrv)
         },
 
-        StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body),
+        StatementKind::FakeRead(box (_, place)) => check_place(tcx, *place, span, body, msrv),
         // just an assignment
         StatementKind::SetDiscriminant { place, .. } | StatementKind::Deinit(place) => {
-            check_place(tcx, **place, span, body)
+            check_place(tcx, **place, span, body, msrv)
         },
 
-        StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => check_operand(tcx, op, span, body),
+        StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(op)) => check_operand(tcx, op, span, body, msrv),
 
         StatementKind::Intrinsic(box NonDivergingIntrinsic::CopyNonOverlapping(
             rustc_middle::mir::CopyNonOverlapping { dst, src, count },
         )) => {
-            check_operand(tcx, dst, span, body)?;
-            check_operand(tcx, src, span, body)?;
-            check_operand(tcx, count, span, body)
+            check_operand(tcx, dst, span, body, msrv)?;
+            check_operand(tcx, src, span, body, msrv)?;
+            check_operand(tcx, count, span, body, msrv)
         },
         // These are all NOPs
         StatementKind::StorageLive(_)
@@ -233,7 +233,13 @@ fn check_statement<'tcx>(
     }
 }
 
-fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
+fn check_operand<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    operand: &Operand<'tcx>,
+    span: Span,
+    body: &Body<'tcx>,
+    msrv: &Msrv,
+) -> McfResult {
     match operand {
         Operand::Move(place) => {
             if !place.projection.as_ref().is_empty()
@@ -245,9 +251,9 @@ fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, b
                 ));
             }
 
-            check_place(tcx, *place, span, body)
+            check_place(tcx, *place, span, body, msrv)
         },
-        Operand::Copy(place) => check_place(tcx, *place, span, body),
+        Operand::Copy(place) => check_place(tcx, *place, span, body, msrv),
         Operand::Constant(c) => match c.check_static_ptr(tcx) {
             Some(_) => Err((span, "cannot access `static` items in const fn".into())),
             None => Ok(()),
@@ -255,23 +261,27 @@ fn check_operand<'tcx>(tcx: TyCtxt<'tcx>, operand: &Operand<'tcx>, span: Span, b
     }
 }
 
-fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>) -> McfResult {
+fn check_place<'tcx>(tcx: TyCtxt<'tcx>, place: Place<'tcx>, span: Span, body: &Body<'tcx>, msrv: &Msrv) -> McfResult {
     for (base, elem) in place.as_ref().iter_projections() {
         match elem {
             ProjectionElem::Field(..) => {
-                let base_ty = base.ty(body, tcx).ty;
-                if let Some(def) = base_ty.ty_adt_def() {
-                    // No union field accesses in `const fn`
-                    if def.is_union() {
-                        return Err((span, "accessing union fields is unstable".into()));
-                    }
+                if base.ty(body, tcx).ty.is_union() && !msrv.meets(msrvs::CONST_FN_UNION) {
+                    return Err((span, "accessing union fields is unstable".into()));
                 }
             },
+            ProjectionElem::Deref => match base.ty(body, tcx).ty.kind() {
+                ty::RawPtr(_, hir::Mutability::Mut) => {
+                    return Err((span, "dereferencing raw mut pointer in const fn is unstable".into()));
+                },
+                ty::RawPtr(_, hir::Mutability::Not) if !msrv.meets(msrvs::CONST_RAW_PTR_DEREF) => {
+                    return Err((span, "dereferencing raw const pointer in const fn is unstable".into()));
+                },
+                _ => (),
+            },
             ProjectionElem::ConstantIndex { .. }
             | ProjectionElem::OpaqueCast(..)
             | ProjectionElem::Downcast(..)
             | ProjectionElem::Subslice { .. }
-            | ProjectionElem::Deref
             | ProjectionElem::Subtype(_)
             | ProjectionElem::Index(_) => {},
         }
@@ -304,7 +314,7 @@ fn check_terminator<'tcx>(
             }
             Ok(())
         },
-        TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body),
+        TerminatorKind::SwitchInt { discr, targets: _ } => check_operand(tcx, discr, span, body, msrv),
         TerminatorKind::CoroutineDrop | TerminatorKind::Yield { .. } => {
             Err((span, "const fn coroutines are unstable".into()))
         },
@@ -341,10 +351,10 @@ fn check_terminator<'tcx>(
                     ));
                 }
 
-                check_operand(tcx, func, span, body)?;
+                check_operand(tcx, func, span, body, msrv)?;
 
                 for arg in args {
-                    check_operand(tcx, &arg.node, span, body)?;
+                    check_operand(tcx, &arg.node, span, body, msrv)?;
                 }
                 Ok(())
             } else {
@@ -357,7 +367,7 @@ fn check_terminator<'tcx>(
             msg: _,
             target: _,
             unwind: _,
-        } => check_operand(tcx, cond, span, body),
+        } => check_operand(tcx, cond, span, body, msrv),
         TerminatorKind::InlineAsm { .. } => Err((span, "cannot use inline assembly in const fn".into())),
     }
 }
diff --git a/src/tools/clippy/clippy_utils/src/source.rs b/src/tools/clippy/clippy_utils/src/source.rs
index e72467edeeb..fd67e039c29 100644
--- a/src/tools/clippy/clippy_utils/src/source.rs
+++ b/src/tools/clippy/clippy_utils/src/source.rs
@@ -250,7 +250,7 @@ pub fn snippet<'a, T: LintContext>(cx: &T, span: Span, default: &'a str) -> Cow<
 /// - Applicability level `Unspecified` will never be changed.
 /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
 /// - If the default value is used and the applicability level is `MachineApplicable`, change it to
-/// `HasPlaceholders`
+///   `HasPlaceholders`
 pub fn snippet_with_applicability<'a, T: LintContext>(
     cx: &T,
     span: Span,
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index bf03c6c1601..6319c7bfa6b 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -67,8 +67,7 @@ impl<'a> Sugg<'a> {
     /// - Applicability level `Unspecified` will never be changed.
     /// - If the span is inside a macro, change the applicability level to `MaybeIncorrect`.
     /// - If the default value is used and the applicability level is `MachineApplicable`, change it
-    ///   to
-    /// `HasPlaceholders`
+    ///   to `HasPlaceholders`
     pub fn hir_with_applicability(
         cx: &LateContext<'_>,
         expr: &hir::Expr<'_>,
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index c29e3feac9a..2dacc34867f 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -273,11 +273,7 @@ pub fn implements_trait_with_env_from_iter<'tcx>(
     let infcx = tcx.infer_ctxt().build();
     let args = args
         .into_iter()
-        .map(|arg| {
-            arg.into().unwrap_or_else(|| {
-                infcx.next_ty_var(DUMMY_SP).into()
-            })
-        })
+        .map(|arg| arg.into().unwrap_or_else(|| infcx.next_ty_var(DUMMY_SP).into()))
         .collect::<Vec<_>>();
 
     // If an effect arg was not specified, we need to specify it.
@@ -795,7 +791,8 @@ fn sig_from_bounds<'tcx>(
                 inputs = Some(i);
             },
             ty::ClauseKind::Projection(p)
-                if Some(p.projection_term.def_id) == lang_items.fn_once_output() && p.projection_term.self_ty() == ty =>
+                if Some(p.projection_term.def_id) == lang_items.fn_once_output()
+                    && p.projection_term.self_ty() == ty =>
             {
                 if output.is_some() {
                     // Multiple different fn trait impls. Is this even allowed?
@@ -956,11 +953,7 @@ pub struct AdtVariantInfo {
 
 impl AdtVariantInfo {
     /// Returns ADT variants ordered by size
-    pub fn new<'tcx>(
-        cx: &LateContext<'tcx>,
-        adt: AdtDef<'tcx>,
-        subst: GenericArgsRef<'tcx>
-    ) -> Vec<Self> {
+    pub fn new<'tcx>(cx: &LateContext<'tcx>, adt: AdtDef<'tcx>, subst: GenericArgsRef<'tcx>) -> Vec<Self> {
         let mut variants_size = adt
             .variants()
             .iter()
diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs
index 9abb4ef9b8d..2a25d51d8e5 100644
--- a/src/tools/clippy/clippy_utils/src/usage.rs
+++ b/src/tools/clippy/clippy_utils/src/usage.rs
@@ -16,13 +16,9 @@ pub fn mutated_variables<'tcx>(expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) ->
         used_mutably: HirIdSet::default(),
         skip: false,
     };
-    ExprUseVisitor::for_clippy(
-        cx,
-        expr.hir_id.owner.def_id,
-        &mut delegate,
-    )
-    .walk_expr(expr)
-    .into_ok();
+    ExprUseVisitor::for_clippy(cx, expr.hir_id.owner.def_id, &mut delegate)
+        .walk_expr(expr)
+        .into_ok();
 
     if delegate.skip {
         return None;
diff --git a/src/tools/clippy/lintcheck/Cargo.toml b/src/tools/clippy/lintcheck/Cargo.toml
index a828d123704..8c5a409e25b 100644
--- a/src/tools/clippy/lintcheck/Cargo.toml
+++ b/src/tools/clippy/lintcheck/Cargo.toml
@@ -13,7 +13,7 @@ default-run = "lintcheck"
 [dependencies]
 anyhow = "1.0.69"
 cargo_metadata = "0.15.3"
-clap = { version = "4.1.8", features = ["derive", "env"] }
+clap = { version = "4.4", features = ["derive", "env"] }
 crates_io_api = "0.8.1"
 crossbeam-channel = "0.5.6"
 flate2 = "1.0"
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index 055f305eb8e..a0585ffdb45 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2024-05-02"
+channel = "nightly-2024-05-16"
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
diff --git a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr
index 103e60d8484..9177e99f8e6 100644
--- a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr
+++ b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr
@@ -42,4 +42,32 @@ help: to have lints override the group set `pedantic` to a lower priority
 19 | pedantic = { level = "warn", priority = -2 }
    |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
-error: could not compile `fail` (lib) due to 3 previous errors
+error: lint group `rust_2018_idioms` has the same priority (0) as a lint
+  --> Cargo.toml:23:1
+   |
+23 | rust_2018_idioms = "warn"
+   | ^^^^^^^^^^^^^^^^   ------ has an implicit priority of 0
+24 | bare_trait_objects = "allow"
+   | ------------------ has the same priority as this lint
+   |
+   = note: the order of the lints in the table is ignored by Cargo
+help: to have lints override the group set `rust_2018_idioms` to a lower priority
+   |
+23 | rust_2018_idioms = { level = "warn", priority = -1 }
+   |                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: lint group `pedantic` has the same priority (0) as a lint
+  --> Cargo.toml:27:1
+   |
+27 | pedantic = "warn"
+   | ^^^^^^^^   ------ has an implicit priority of 0
+28 | similar_names = "allow"
+   | ------------- has the same priority as this lint
+   |
+   = note: the order of the lints in the table is ignored by Cargo
+help: to have lints override the group set `pedantic` to a lower priority
+   |
+27 | pedantic = { level = "warn", priority = -1 }
+   |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: could not compile `fail` (lib) due to 5 previous errors
diff --git a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml
index 4ce41f78171..e4d4af9cd23 100644
--- a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml
+++ b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml
@@ -18,3 +18,11 @@ deprecated  = "allow"
 [lints.clippy]
 pedantic = { level = "warn", priority = -1 }
 similar_names = { level = "allow", priority = -1 }
+
+[workspace.lints.rust]
+rust_2018_idioms = "warn"
+bare_trait_objects = "allow"
+
+[workspace.lints.clippy]
+pedantic = "warn"
+similar_names = "allow"
diff --git a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs
new file mode 100644
index 00000000000..f5e01b431ad
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.rs
@@ -0,0 +1,260 @@
+//! Tests macro_metavars_in_unsafe with default configuration
+#![feature(decl_macro, lint_reasons)]
+#![warn(clippy::macro_metavars_in_unsafe)]
+#![allow(clippy::no_effect)]
+
+#[macro_export]
+macro_rules! allow_works {
+    ($v:expr) => {
+        #[expect(clippy::macro_metavars_in_unsafe)]
+        unsafe {
+            $v;
+        };
+    };
+}
+
+#[macro_export]
+macro_rules! simple {
+    ($v:expr) => {
+        unsafe {
+            //~^ ERROR: this macro expands metavariables in an unsafe block
+            dbg!($v);
+        }
+    };
+}
+
+#[macro_export]
+#[rustfmt::skip] // for some reason rustfmt rewrites $r#unsafe to r#u$nsafe, bug?
+macro_rules! raw_symbol {
+    ($r#mod:expr, $r#unsafe:expr) => {
+        unsafe {
+            //~^ ERROR: this macro expands metavariables in an unsafe block
+            $r#mod;
+        }
+        $r#unsafe;
+    };
+}
+
+#[macro_export]
+macro_rules! multilevel_unsafe {
+    ($v:expr) => {
+        unsafe {
+            unsafe {
+                //~^ ERROR: this macro expands metavariables in an unsafe block
+                $v;
+            }
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! in_function {
+    ($v:expr) => {
+        unsafe {
+            fn f() {
+                // function introduces a new body, so don't lint.
+                $v;
+            }
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! in_function_with_unsafe {
+    ($v:expr) => {
+        unsafe {
+            fn f() {
+                unsafe {
+                    //~^ ERROR: this macro expands metavariables in an unsafe block
+                    $v;
+                }
+            }
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! const_static {
+    ($c:expr, $s:expr) => {
+        unsafe {
+            // const and static introduces new body, don't lint
+            const _X: i32 = $c;
+            static _Y: i32 = $s;
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! const_generic_in_struct {
+    ($inside_unsafe:expr, $outside_unsafe:expr) => {
+        unsafe {
+            struct Ty<
+                const L: i32 = 1,
+                const M: i32 = {
+                    1;
+                    unsafe { $inside_unsafe }
+                    //~^ ERROR: this macro expands metavariables in an unsafe block
+                },
+                const N: i32 = { $outside_unsafe },
+            >;
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! fn_with_const_generic {
+    ($inside_unsafe:expr, $outside_unsafe:expr) => {
+        unsafe {
+            fn f<const N: usize>() {
+                $outside_unsafe;
+                unsafe {
+                    //~^ ERROR: this macro expands metavariables in an unsafe block
+                    $inside_unsafe;
+                }
+            }
+        }
+    };
+}
+
+#[macro_export]
+macro_rules! variables {
+    ($inside_unsafe:expr, $outside_unsafe:expr) => {
+        unsafe {
+            //~^ ERROR: this macro expands metavariables in an unsafe block
+            $inside_unsafe;
+            let inside_unsafe = 1;
+            inside_unsafe;
+        }
+        $outside_unsafe;
+        let outside_unsafe = 1;
+        outside_unsafe;
+    };
+}
+
+#[macro_export]
+macro_rules! multiple_matchers {
+    ($inside_unsafe:expr, $outside_unsafe:expr) => {
+        unsafe {
+            //~^ ERROR: this macro expands metavariables in an unsafe block
+            $inside_unsafe;
+        }
+        $outside_unsafe;
+    };
+    ($($v:expr, $x:expr),+) => {
+        $(
+            $v;
+            unsafe {
+                //~^ ERROR: this macro expands metavariables in an unsafe block
+                $x;
+            }
+        );+
+    };
+}
+
+#[macro_export]
+macro_rules! multiple_unsafe_blocks {
+    ($w:expr, $x:expr, $y:expr) => {
+        $w;
+        unsafe {
+            //~^ ERROR: this macro expands metavariables in an unsafe block
+            $x;
+        }
+        unsafe {
+            //~^ ERROR: this macro expands metavariables in an unsafe block
+            $x;
+            $y;
+        }
+    };
+}
+
+pub macro macro2_0($v:expr) {
+    unsafe {
+        //~^ ERROR: this macro expands metavariables in an unsafe block
+        $v;
+    }
+}
+
+// don't lint private macros with the default configuration
+macro_rules! private_mac {
+    ($v:expr) => {
+        unsafe {
+            $v;
+        }
+    };
+}
+
+// don't lint exported macros that are doc(hidden) because they also aren't part of the public API
+#[macro_export]
+#[doc(hidden)]
+macro_rules! exported_but_hidden {
+    ($v:expr) => {
+        unsafe {
+            $v;
+        }
+    };
+}
+
+// don't lint if the same metavariable is expanded in an unsafe block and then outside of one:
+// unsafe {} is still needed at callsite so not problematic
+#[macro_export]
+macro_rules! does_require_unsafe {
+    ($v:expr) => {
+        unsafe {
+            $v;
+        }
+        $v;
+    };
+}
+
+#[macro_export]
+macro_rules! unsafe_from_root_ctxt {
+    ($v:expr) => {
+        // Expands to unsafe { 1 }, but the unsafe block is from the root ctxt and not this macro,
+        // so no warning.
+        $v;
+    };
+}
+
+// invoked from another macro, should still generate a warning
+#[macro_export]
+macro_rules! nested_macro_helper {
+    ($v:expr) => {{
+        unsafe {
+            //~^ ERROR: this macro expands metavariables in an unsafe block
+            $v;
+        }
+    }};
+}
+
+#[macro_export]
+macro_rules! nested_macros {
+    ($v:expr, $v2:expr) => {{
+        unsafe {
+            //~^ ERROR: this macro expands metavariables in an unsafe block
+            nested_macro_helper!($v);
+            $v;
+        }
+    }};
+}
+
+fn main() {
+    allow_works!(1);
+    simple!(1);
+    raw_symbol!(1, 1);
+    multilevel_unsafe!(1);
+    in_function!(1);
+    in_function_with_unsafe!(1);
+    const_static!(1, 1);
+    const_generic_in_struct!(1, 1);
+    fn_with_const_generic!(1, 1);
+    variables!(1, 1);
+    multiple_matchers!(1, 1);
+    multiple_matchers!(1, 1, 1, 1);
+    macro2_0!(1);
+    private_mac!(1);
+    exported_but_hidden!(1);
+    does_require_unsafe!(1);
+    multiple_unsafe_blocks!(1, 1, 1);
+    unsafe_from_root_ctxt!(unsafe { 1 });
+    nested_macros!(1, 1);
+}
diff --git a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr
new file mode 100644
index 00000000000..d6b97f6fde1
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/default/test.stderr
@@ -0,0 +1,187 @@
+error: this macro expands metavariables in an unsafe block
+  --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:19:9
+   |
+LL | /         unsafe {
+LL | |
+LL | |             dbg!($v);
+LL | |         }
+   | |_________^
+   |
+   = note: this allows the user of the macro to write unsafe code outside of an unsafe block
+   = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
+   = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
+   = note: `-D clippy::macro-metavars-in-unsafe` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::macro_metavars_in_unsafe)]`
+
+error: this macro expands metavariables in an unsafe block
+  --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:30:9
+   |
+LL | /         unsafe {
+LL | |
+LL | |             $r#mod;
+LL | |         }
+   | |_________^
+   |
+   = note: this allows the user of the macro to write unsafe code outside of an unsafe block
+   = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
+   = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
+
+error: this macro expands metavariables in an unsafe block
+  --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:42:13
+   |
+LL | /             unsafe {
+LL | |
+LL | |                 $v;
+LL | |             }
+   | |_____________^
+   |
+   = note: this allows the user of the macro to write unsafe code outside of an unsafe block
+   = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
+   = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
+
+error: this macro expands metavariables in an unsafe block
+  --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:67:17
+   |
+LL | /                 unsafe {
+LL | |
+LL | |                     $v;
+LL | |                 }
+   | |_________________^
+   |
+   = note: this allows the user of the macro to write unsafe code outside of an unsafe block
+   = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
+   = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
+
+error: this macro expands metavariables in an unsafe block
+  --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:95:21
+   |
+LL |                     unsafe { $inside_unsafe }
+   |                     ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: this allows the user of the macro to write unsafe code outside of an unsafe block
+   = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
+   = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
+
+error: this macro expands metavariables in an unsafe block
+  --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:110:17
+   |
+LL | /                 unsafe {
+LL | |
+LL | |                     $inside_unsafe;
+LL | |                 }
+   | |_________________^
+   |
+   = note: this allows the user of the macro to write unsafe code outside of an unsafe block
+   = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
+   = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
+
+error: this macro expands metavariables in an unsafe block
+  --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:122:9
+   |
+LL | /         unsafe {
+LL | |
+LL | |             $inside_unsafe;
+LL | |             let inside_unsafe = 1;
+LL | |             inside_unsafe;
+LL | |         }
+   | |_________^
+   |
+   = note: this allows the user of the macro to write unsafe code outside of an unsafe block
+   = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
+   = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
+
+error: this macro expands metavariables in an unsafe block
+  --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:137:9
+   |
+LL | /         unsafe {
+LL | |
+LL | |             $inside_unsafe;
+LL | |         }
+   | |_________^
+   |
+   = note: this allows the user of the macro to write unsafe code outside of an unsafe block
+   = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
+   = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
+
+error: this macro expands metavariables in an unsafe block
+  --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:146:13
+   |
+LL | /             unsafe {
+LL | |
+LL | |                 $x;
+LL | |             }
+   | |_____________^
+   |
+   = note: this allows the user of the macro to write unsafe code outside of an unsafe block
+   = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
+   = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
+
+error: this macro expands metavariables in an unsafe block
+  --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:171:5
+   |
+LL | /     unsafe {
+LL | |
+LL | |         $v;
+LL | |     }
+   | |_____^
+   |
+   = note: this allows the user of the macro to write unsafe code outside of an unsafe block
+   = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
+   = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
+
+error: this macro expands metavariables in an unsafe block
+  --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:158:9
+   |
+LL | /         unsafe {
+LL | |
+LL | |             $x;
+LL | |         }
+   | |_________^
+   |
+   = note: this allows the user of the macro to write unsafe code outside of an unsafe block
+   = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
+   = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
+
+error: this macro expands metavariables in an unsafe block
+  --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:162:9
+   |
+LL | /         unsafe {
+LL | |
+LL | |             $x;
+LL | |             $y;
+LL | |         }
+   | |_________^
+   |
+   = note: this allows the user of the macro to write unsafe code outside of an unsafe block
+   = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
+   = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
+
+error: this macro expands metavariables in an unsafe block
+  --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:222:9
+   |
+LL | /         unsafe {
+LL | |
+LL | |             $v;
+LL | |         }
+   | |_________^
+   |
+   = note: this allows the user of the macro to write unsafe code outside of an unsafe block
+   = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
+   = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
+
+error: this macro expands metavariables in an unsafe block
+  --> tests/ui-toml/macro_metavars_in_unsafe/default/test.rs:232:9
+   |
+LL | /         unsafe {
+LL | |
+LL | |             nested_macro_helper!($v);
+LL | |             $v;
+LL | |         }
+   | |_________^
+   |
+   = note: this allows the user of the macro to write unsafe code outside of an unsafe block
+   = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
+   = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
+
+error: aborting due to 14 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/clippy.toml b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/clippy.toml
new file mode 100644
index 00000000000..d4bbc2a1be8
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/clippy.toml
@@ -0,0 +1 @@
+warn-unsafe-macro-metavars-in-private-macros = true
diff --git a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.rs b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.rs
new file mode 100644
index 00000000000..2bbe1fa7b7f
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.rs
@@ -0,0 +1,15 @@
+//! Tests macro_metavars_in_unsafe with private (non-exported) macros
+#![warn(clippy::macro_metavars_in_unsafe)]
+
+macro_rules! mac {
+    ($v:expr) => {
+        unsafe {
+            //~^ ERROR: this macro expands metavariables in an unsafe block
+            dbg!($v);
+        }
+    };
+}
+
+fn main() {
+    mac!(1);
+}
diff --git a/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr
new file mode 100644
index 00000000000..f9c418b2218
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/macro_metavars_in_unsafe/private/test.stderr
@@ -0,0 +1,17 @@
+error: this macro expands metavariables in an unsafe block
+  --> tests/ui-toml/macro_metavars_in_unsafe/private/test.rs:6:9
+   |
+LL | /         unsafe {
+LL | |
+LL | |             dbg!($v);
+LL | |         }
+   | |_________^
+   |
+   = note: this allows the user of the macro to write unsafe code outside of an unsafe block
+   = help: consider expanding any metavariables outside of this block, e.g. by storing them in a variable
+   = help: ... or also expand referenced metavariables in a safe context to require an unsafe block at callsite
+   = note: `-D clippy::macro-metavars-in-unsafe` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::macro_metavars_in_unsafe)]`
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/panic/clippy.toml b/src/tools/clippy/tests/ui-toml/panic/clippy.toml
new file mode 100644
index 00000000000..5d6230d092c
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/panic/clippy.toml
@@ -0,0 +1 @@
+allow-panic-in-tests = true
diff --git a/src/tools/clippy/tests/ui-toml/panic/panic.rs b/src/tools/clippy/tests/ui-toml/panic/panic.rs
new file mode 100644
index 00000000000..618a37ddfc5
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/panic/panic.rs
@@ -0,0 +1,54 @@
+//@compile-flags: --test
+#![warn(clippy::panic)]
+
+fn main() {
+    enum Enam {
+        A,
+    }
+    let a = Enam::A;
+    match a {
+        Enam::A => {},
+        _ => panic!(""),
+    }
+}
+
+#[test]
+fn lonely_test() {
+    enum Enam {
+        A,
+    }
+    let a = Enam::A;
+    match a {
+        Enam::A => {},
+        _ => panic!(""),
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    // should not lint in `#[cfg(test)]` modules
+    #[test]
+    fn test_fn() {
+        enum Enam {
+            A,
+        }
+        let a = Enam::A;
+        match a {
+            Enam::A => {},
+            _ => panic!(""),
+        }
+
+        bar();
+    }
+
+    fn bar() {
+        enum Enam {
+            A,
+        }
+        let a = Enam::A;
+        match a {
+            Enam::A => {},
+            _ => panic!(""),
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui-toml/panic/panic.stderr b/src/tools/clippy/tests/ui-toml/panic/panic.stderr
new file mode 100644
index 00000000000..bf7503e086c
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/panic/panic.stderr
@@ -0,0 +1,11 @@
+error: `panic` should not be present in production code
+  --> tests/ui-toml/panic/panic.rs:11:14
+   |
+LL |         _ => panic!(""),
+   |              ^^^^^^^^^^
+   |
+   = note: `-D clippy::panic` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::panic)]`
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/renamed_function_params/default/clippy.toml b/src/tools/clippy/tests/ui-toml/renamed_function_params/default/clippy.toml
new file mode 100644
index 00000000000..5381e70a939
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/renamed_function_params/default/clippy.toml
@@ -0,0 +1,2 @@
+# Ignore `From`, `TryFrom`, `FromStr` by default
+# allow-renamed-params-for = []
diff --git a/src/tools/clippy/tests/ui-toml/renamed_function_params/extend/clippy.toml b/src/tools/clippy/tests/ui-toml/renamed_function_params/extend/clippy.toml
new file mode 100644
index 00000000000..9b3853e7696
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/renamed_function_params/extend/clippy.toml
@@ -0,0 +1,2 @@
+# Ignore `From`, `TryFrom`, `FromStr` by default
+allow-renamed-params-for = [ "..", "std::ops::Add", "renamed_function_params::MyTrait" ]
diff --git a/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.default.stderr b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.default.stderr
new file mode 100644
index 00000000000..2d700f60759
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.default.stderr
@@ -0,0 +1,46 @@
+error: renamed function parameter of trait impl
+  --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:30:18
+   |
+LL |     fn eq(&self, rhs: &Self) -> bool {
+   |                  ^^^ help: consider using the default name: `other`
+   |
+   = note: `-D clippy::renamed-function-params` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::renamed_function_params)]`
+
+error: renamed function parameter of trait impl
+  --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:34:18
+   |
+LL |     fn ne(&self, rhs: &Self) -> bool {
+   |                  ^^^ help: consider using the default name: `other`
+
+error: renamed function parameter of trait impl
+  --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:48:19
+   |
+LL |     fn foo(&self, i_dont_wanna_use_your_name: u8) {} // only lint in `extend`
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using the default name: `val`
+
+error: renamed function parameter of trait impl
+  --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:55:31
+   |
+LL |     fn hash<H: Hasher>(&self, states: &mut H) {
+   |                               ^^^^^^ help: consider using the default name: `state`
+
+error: renamed function parameters of trait impl
+  --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:59:30
+   |
+LL |     fn hash_slice<H: Hasher>(date: &[Self], states: &mut H) {
+   |                              ^^^^           ^^^^^^
+   |
+help: consider using the default names
+   |
+LL |     fn hash_slice<H: Hasher>(data: &[Self], state: &mut H) {
+   |                              ~~~~           ~~~~~
+
+error: renamed function parameter of trait impl
+  --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:80:18
+   |
+LL |     fn add(self, b: B) -> C {
+   |                  ^ help: consider using the default name: `rhs`
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.extend.stderr b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.extend.stderr
new file mode 100644
index 00000000000..e57554fa613
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.extend.stderr
@@ -0,0 +1,34 @@
+error: renamed function parameter of trait impl
+  --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:30:18
+   |
+LL |     fn eq(&self, rhs: &Self) -> bool {
+   |                  ^^^ help: consider using the default name: `other`
+   |
+   = note: `-D clippy::renamed-function-params` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::renamed_function_params)]`
+
+error: renamed function parameter of trait impl
+  --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:34:18
+   |
+LL |     fn ne(&self, rhs: &Self) -> bool {
+   |                  ^^^ help: consider using the default name: `other`
+
+error: renamed function parameter of trait impl
+  --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:55:31
+   |
+LL |     fn hash<H: Hasher>(&self, states: &mut H) {
+   |                               ^^^^^^ help: consider using the default name: `state`
+
+error: renamed function parameters of trait impl
+  --> tests/ui-toml/renamed_function_params/renamed_function_params.rs:59:30
+   |
+LL |     fn hash_slice<H: Hasher>(date: &[Self], states: &mut H) {
+   |                              ^^^^           ^^^^^^
+   |
+help: consider using the default names
+   |
+LL |     fn hash_slice<H: Hasher>(data: &[Self], state: &mut H) {
+   |                              ~~~~           ~~~~~
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.rs b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.rs
new file mode 100644
index 00000000000..f3eb910abbd
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/renamed_function_params/renamed_function_params.rs
@@ -0,0 +1,110 @@
+//@no-rustfix
+//@revisions: default extend
+//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/renamed_function_params/default
+//@[extend] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/renamed_function_params/extend
+#![warn(clippy::renamed_function_params)]
+#![allow(clippy::partialeq_ne_impl, clippy::to_string_trait_impl)]
+#![allow(unused)]
+
+use std::hash::{Hash, Hasher};
+
+struct A;
+impl From<A> for String {
+    fn from(_value: A) -> Self {
+        String::new()
+    }
+}
+impl ToString for A {
+    fn to_string(&self) -> String {
+        String::new()
+    }
+}
+
+struct B(u32);
+impl std::convert::From<B> for String {
+    fn from(b: B) -> Self {
+        b.0.to_string()
+    }
+}
+impl PartialEq for B {
+    fn eq(&self, rhs: &Self) -> bool {
+        //~^ ERROR: renamed function parameter of trait impl
+        self.0 == rhs.0
+    }
+    fn ne(&self, rhs: &Self) -> bool {
+        //~^ ERROR: renamed function parameter of trait impl
+        self.0 != rhs.0
+    }
+}
+
+trait MyTrait {
+    fn foo(&self, val: u8);
+    fn bar(a: u8, b: u8);
+    fn baz(self, _val: u8);
+    fn quz(&self, _: u8);
+}
+
+impl MyTrait for B {
+    fn foo(&self, i_dont_wanna_use_your_name: u8) {} // only lint in `extend`
+    fn bar(_a: u8, _: u8) {}
+    fn baz(self, val: u8) {}
+    fn quz(&self, val: u8) {}
+}
+
+impl Hash for B {
+    fn hash<H: Hasher>(&self, states: &mut H) {
+        //~^ ERROR: renamed function parameter of trait impl
+        self.0.hash(states);
+    }
+    fn hash_slice<H: Hasher>(date: &[Self], states: &mut H) {
+        //~^ ERROR: renamed function parameters of trait impl
+        for d in date {
+            d.hash(states);
+        }
+    }
+}
+
+impl B {
+    fn totally_irrelevant(&self, right: bool) {}
+    fn some_fn(&self, other: impl MyTrait) {}
+}
+
+#[derive(Copy, Clone)]
+enum C {
+    A,
+    B(u32),
+}
+
+impl std::ops::Add<B> for C {
+    type Output = C;
+    fn add(self, b: B) -> C {
+        // only lint in `extend`
+        C::B(b.0)
+    }
+}
+
+impl From<A> for C {
+    fn from(_: A) -> C {
+        C::A
+    }
+}
+
+trait CustomTraitA {
+    fn foo(&self, other: u32);
+}
+trait CustomTraitB {
+    fn bar(&self, value: u8);
+}
+
+macro_rules! impl_trait {
+    ($impl_for:ident, $tr:ty, $fn_name:ident, $t:ty) => {
+        impl $tr for $impl_for {
+            fn $fn_name(&self, v: $t) {}
+        }
+    };
+}
+
+impl_trait!(C, CustomTraitA, foo, u32);
+impl_trait!(C, CustomTraitB, bar, u8);
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs
index 7f28efd676f..f02bd07cfe7 100644
--- a/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs
+++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_types/conf_disallowed_types.rs
@@ -40,3 +40,9 @@ fn main() {
     let _ = HashMap;
     let _: usize = 64_usize;
 }
+
+mod useless_attribute {
+    // Regression test for https://github.com/rust-lang/rust-clippy/issues/12753
+    #[allow(clippy::disallowed_types)]
+    use std::collections::HashMap;
+}
diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
index 722e9b3bc8d..5cf9c0fb271 100644
--- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
+++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
@@ -8,8 +8,10 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
            allow-expect-in-tests
            allow-mixed-uninlined-format-args
            allow-one-hash-in-raw-strings
+           allow-panic-in-tests
            allow-print-in-tests
            allow-private-module-inception
+           allow-renamed-params-for
            allow-unwrap-in-tests
            allow-useless-vec-in-tests
            allowed-dotfiles
@@ -74,6 +76,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
            vec-box-size-threshold
            verbose-bit-mask-threshold
            warn-on-all-wildcard-imports
+           warn-unsafe-macro-metavars-in-private-macros
   --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:2:1
    |
 LL | foobar = 42
@@ -89,8 +92,10 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
            allow-expect-in-tests
            allow-mixed-uninlined-format-args
            allow-one-hash-in-raw-strings
+           allow-panic-in-tests
            allow-print-in-tests
            allow-private-module-inception
+           allow-renamed-params-for
            allow-unwrap-in-tests
            allow-useless-vec-in-tests
            allowed-dotfiles
@@ -155,6 +160,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
            vec-box-size-threshold
            verbose-bit-mask-threshold
            warn-on-all-wildcard-imports
+           warn-unsafe-macro-metavars-in-private-macros
   --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:4:1
    |
 LL | barfoo = 53
@@ -170,8 +176,10 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
            allow-expect-in-tests
            allow-mixed-uninlined-format-args
            allow-one-hash-in-raw-strings
+           allow-panic-in-tests
            allow-print-in-tests
            allow-private-module-inception
+           allow-renamed-params-for
            allow-unwrap-in-tests
            allow-useless-vec-in-tests
            allowed-dotfiles
@@ -236,6 +244,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
            vec-box-size-threshold
            verbose-bit-mask-threshold
            warn-on-all-wildcard-imports
+           warn-unsafe-macro-metavars-in-private-macros
   --> $DIR/tests/ui-toml/toml_unknown_key/clippy.toml:7:1
    |
 LL | allow_mixed_uninlined_format_args = true
diff --git a/src/tools/clippy/tests/ui/assigning_clones.fixed b/src/tools/clippy/tests/ui/assigning_clones.fixed
index 8387c7d6156..70ab43b49b3 100644
--- a/src/tools/clippy/tests/ui/assigning_clones.fixed
+++ b/src/tools/clippy/tests/ui/assigning_clones.fixed
@@ -62,6 +62,16 @@ fn clone_method_rhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFr
     mut_thing.clone_from(ref_thing + ref_thing);
 }
 
+fn clone_method_macro() {
+    let mut s = String::from("");
+    s.clone_from(&format!("{} {}", "hello", "world"));
+}
+
+fn clone_function_macro() {
+    let mut s = String::from("");
+    Clone::clone_from(&mut s, &format!("{} {}", "hello", "world"));
+}
+
 fn assign_to_init_mut_var(b: HasCloneFrom) -> HasCloneFrom {
     let mut a = HasCloneFrom;
     for _ in 1..10 {
@@ -86,6 +96,12 @@ fn assign_to_uninit_mut_var(b: HasCloneFrom) {
     a = b.clone();
 }
 
+fn late_init_let_tuple() {
+    let (p, q): (String, String);
+    p = "ghi".to_string();
+    q = p.clone();
+}
+
 #[derive(Clone)]
 pub struct HasDeriveClone;
 
@@ -208,6 +224,16 @@ fn owned_function_val(mut mut_thing: String, ref_str: &str) {
     ToOwned::clone_into(ref_str, &mut mut_thing);
 }
 
+fn owned_method_macro() {
+    let mut s = String::from("");
+    format!("{} {}", "hello", "world").clone_into(&mut s);
+}
+
+fn owned_function_macro() {
+    let mut s = String::from("");
+    ToOwned::clone_into(&format!("{} {}", "hello", "world"), &mut s);
+}
+
 struct FakeToOwned;
 impl FakeToOwned {
     /// This looks just like `ToOwned::to_owned`
diff --git a/src/tools/clippy/tests/ui/assigning_clones.rs b/src/tools/clippy/tests/ui/assigning_clones.rs
index 6f4da9f652c..9699fed100c 100644
--- a/src/tools/clippy/tests/ui/assigning_clones.rs
+++ b/src/tools/clippy/tests/ui/assigning_clones.rs
@@ -62,6 +62,16 @@ fn clone_method_rhs_complex(mut_thing: &mut HasCloneFrom, ref_thing: &HasCloneFr
     *mut_thing = (ref_thing + ref_thing).clone();
 }
 
+fn clone_method_macro() {
+    let mut s = String::from("");
+    s = format!("{} {}", "hello", "world").clone();
+}
+
+fn clone_function_macro() {
+    let mut s = String::from("");
+    s = Clone::clone(&format!("{} {}", "hello", "world"));
+}
+
 fn assign_to_init_mut_var(b: HasCloneFrom) -> HasCloneFrom {
     let mut a = HasCloneFrom;
     for _ in 1..10 {
@@ -86,6 +96,12 @@ fn assign_to_uninit_mut_var(b: HasCloneFrom) {
     a = b.clone();
 }
 
+fn late_init_let_tuple() {
+    let (p, q): (String, String);
+    p = "ghi".to_string();
+    q = p.clone();
+}
+
 #[derive(Clone)]
 pub struct HasDeriveClone;
 
@@ -208,6 +224,16 @@ fn owned_function_val(mut mut_thing: String, ref_str: &str) {
     mut_thing = ToOwned::to_owned(ref_str);
 }
 
+fn owned_method_macro() {
+    let mut s = String::from("");
+    s = format!("{} {}", "hello", "world").to_owned();
+}
+
+fn owned_function_macro() {
+    let mut s = String::from("");
+    s = ToOwned::to_owned(&format!("{} {}", "hello", "world"));
+}
+
 struct FakeToOwned;
 impl FakeToOwned {
     /// This looks just like `ToOwned::to_owned`
diff --git a/src/tools/clippy/tests/ui/assigning_clones.stderr b/src/tools/clippy/tests/ui/assigning_clones.stderr
index 793927bd1cb..a68516376ab 100644
--- a/src/tools/clippy/tests/ui/assigning_clones.stderr
+++ b/src/tools/clippy/tests/ui/assigning_clones.stderr
@@ -62,64 +62,88 @@ LL |     *mut_thing = (ref_thing + ref_thing).clone();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `mut_thing.clone_from(ref_thing + ref_thing)`
 
 error: assigning the result of `Clone::clone()` may be inefficient
-  --> tests/ui/assigning_clones.rs:68:9
+  --> tests/ui/assigning_clones.rs:67:5
+   |
+LL |     s = format!("{} {}", "hello", "world").clone();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `s.clone_from(&format!("{} {}", "hello", "world"))`
+
+error: assigning the result of `Clone::clone()` may be inefficient
+  --> tests/ui/assigning_clones.rs:72:5
+   |
+LL |     s = Clone::clone(&format!("{} {}", "hello", "world"));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_from()`: `Clone::clone_from(&mut s, &format!("{} {}", "hello", "world"))`
+
+error: assigning the result of `Clone::clone()` may be inefficient
+  --> tests/ui/assigning_clones.rs:78:9
    |
 LL |         a = b.clone();
    |         ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)`
 
 error: assigning the result of `Clone::clone()` may be inefficient
-  --> tests/ui/assigning_clones.rs:133:5
+  --> tests/ui/assigning_clones.rs:149:5
    |
 LL |     a = b.clone();
    |     ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)`
 
 error: assigning the result of `Clone::clone()` may be inefficient
-  --> tests/ui/assigning_clones.rs:140:5
+  --> tests/ui/assigning_clones.rs:156:5
    |
 LL |     a = b.clone();
    |     ^^^^^^^^^^^^^ help: use `clone_from()`: `a.clone_from(&b)`
 
 error: assigning the result of `ToOwned::to_owned()` may be inefficient
-  --> tests/ui/assigning_clones.rs:141:5
+  --> tests/ui/assigning_clones.rs:157:5
    |
 LL |     a = c.to_owned();
    |     ^^^^^^^^^^^^^^^^ help: use `clone_into()`: `c.clone_into(&mut a)`
 
 error: assigning the result of `ToOwned::to_owned()` may be inefficient
-  --> tests/ui/assigning_clones.rs:171:5
+  --> tests/ui/assigning_clones.rs:187:5
    |
 LL |     *mut_string = ref_str.to_owned();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(mut_string)`
 
 error: assigning the result of `ToOwned::to_owned()` may be inefficient
-  --> tests/ui/assigning_clones.rs:175:5
+  --> tests/ui/assigning_clones.rs:191:5
    |
 LL |     mut_string = ref_str.to_owned();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut mut_string)`
 
 error: assigning the result of `ToOwned::to_owned()` may be inefficient
-  --> tests/ui/assigning_clones.rs:196:5
+  --> tests/ui/assigning_clones.rs:212:5
    |
 LL |     **mut_box_string = ref_str.to_owned();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))`
 
 error: assigning the result of `ToOwned::to_owned()` may be inefficient
-  --> tests/ui/assigning_clones.rs:200:5
+  --> tests/ui/assigning_clones.rs:216:5
    |
 LL |     **mut_box_string = ref_str.to_owned();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ref_str.clone_into(&mut (*mut_box_string))`
 
 error: assigning the result of `ToOwned::to_owned()` may be inefficient
-  --> tests/ui/assigning_clones.rs:204:5
+  --> tests/ui/assigning_clones.rs:220:5
    |
 LL |     *mut_thing = ToOwned::to_owned(ref_str);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, mut_thing)`
 
 error: assigning the result of `ToOwned::to_owned()` may be inefficient
-  --> tests/ui/assigning_clones.rs:208:5
+  --> tests/ui/assigning_clones.rs:224:5
    |
 LL |     mut_thing = ToOwned::to_owned(ref_str);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(ref_str, &mut mut_thing)`
 
-error: aborting due to 20 previous errors
+error: assigning the result of `ToOwned::to_owned()` may be inefficient
+  --> tests/ui/assigning_clones.rs:229:5
+   |
+LL |     s = format!("{} {}", "hello", "world").to_owned();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `format!("{} {}", "hello", "world").clone_into(&mut s)`
+
+error: assigning the result of `ToOwned::to_owned()` may be inefficient
+  --> tests/ui/assigning_clones.rs:234:5
+   |
+LL |     s = ToOwned::to_owned(&format!("{} {}", "hello", "world"));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `clone_into()`: `ToOwned::clone_into(&format!("{} {}", "hello", "world"), &mut s)`
+
+error: aborting due to 24 previous errors
 
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.fixed b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.fixed
new file mode 100644
index 00000000000..9877991f183
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.fixed
@@ -0,0 +1,47 @@
+#![warn(clippy::doc_lazy_continuation)]
+
+/// > blockquote with
+/// > lazy continuation
+//~^ ERROR: doc quote missing `>` marker
+fn first() {}
+
+/// > blockquote with no
+/// > lazy continuation
+fn first_nowarn() {}
+
+/// > blockquote with no
+///
+/// lazy continuation
+fn two_nowarn() {}
+
+/// > nest here
+/// >
+/// > > nest here
+/// > > lazy continuation
+//~^ ERROR: doc quote missing `>` marker
+fn two() {}
+
+/// > nest here
+/// >
+/// > > nest here
+/// > > lazy continuation
+//~^ ERROR: doc quote missing `>` marker
+fn three() {}
+
+/// >   * > nest here
+/// >     > lazy continuation
+//~^ ERROR: doc quote missing `>` marker
+fn four() {}
+
+/// > * > nest here
+/// >   > lazy continuation
+//~^ ERROR: doc quote missing `>` marker
+fn four_point_1() {}
+
+/// * > nest here lazy continuation
+fn five() {}
+
+/// 1. > nest here
+///    > lazy continuation (this results in strange indentation, but still works)
+//~^ ERROR: doc quote missing `>` marker
+fn six() {}
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.rs b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.rs
new file mode 100644
index 00000000000..587b2fdd533
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.rs
@@ -0,0 +1,47 @@
+#![warn(clippy::doc_lazy_continuation)]
+
+/// > blockquote with
+/// lazy continuation
+//~^ ERROR: doc quote missing `>` marker
+fn first() {}
+
+/// > blockquote with no
+/// > lazy continuation
+fn first_nowarn() {}
+
+/// > blockquote with no
+///
+/// lazy continuation
+fn two_nowarn() {}
+
+/// > nest here
+/// >
+/// > > nest here
+/// > lazy continuation
+//~^ ERROR: doc quote missing `>` marker
+fn two() {}
+
+/// > nest here
+/// >
+/// > > nest here
+/// lazy continuation
+//~^ ERROR: doc quote missing `>` marker
+fn three() {}
+
+/// >   * > nest here
+/// lazy continuation
+//~^ ERROR: doc quote missing `>` marker
+fn four() {}
+
+/// > * > nest here
+/// lazy continuation
+//~^ ERROR: doc quote missing `>` marker
+fn four_point_1() {}
+
+/// * > nest here lazy continuation
+fn five() {}
+
+/// 1. > nest here
+///  lazy continuation (this results in strange indentation, but still works)
+//~^ ERROR: doc quote missing `>` marker
+fn six() {}
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.stderr b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.stderr
new file mode 100644
index 00000000000..975184a01c3
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.stderr
@@ -0,0 +1,76 @@
+error: doc quote missing `>` marker
+  --> tests/ui/doc/doc_lazy_blockquote.rs:4:5
+   |
+LL | /// lazy continuation
+   |     ^
+   |
+   = help: if this not intended to be a quote at all, escape it with `\>`
+   = note: `-D clippy::doc-lazy-continuation` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::doc_lazy_continuation)]`
+help: add markers to start of line
+   |
+LL | /// > lazy continuation
+   |     +
+
+error: doc quote missing `>` marker
+  --> tests/ui/doc/doc_lazy_blockquote.rs:20:5
+   |
+LL | /// > lazy continuation
+   |     ^^
+   |
+   = help: if this not intended to be a quote at all, escape it with `\>`
+help: add markers to start of line
+   |
+LL | /// > > lazy continuation
+   |       +
+
+error: doc quote missing `>` marker
+  --> tests/ui/doc/doc_lazy_blockquote.rs:27:5
+   |
+LL | /// lazy continuation
+   |     ^
+   |
+   = help: if this not intended to be a quote at all, escape it with `\>`
+help: add markers to start of line
+   |
+LL | /// > > lazy continuation
+   |     +++
+
+error: doc quote missing `>` marker
+  --> tests/ui/doc/doc_lazy_blockquote.rs:32:5
+   |
+LL | /// lazy continuation
+   |     ^
+   |
+   = help: if this not intended to be a quote at all, escape it with `\>`
+help: add markers to start of line
+   |
+LL | /// >     > lazy continuation
+   |     +++++++
+
+error: doc quote missing `>` marker
+  --> tests/ui/doc/doc_lazy_blockquote.rs:37:5
+   |
+LL | /// lazy continuation
+   |     ^
+   |
+   = help: if this not intended to be a quote at all, escape it with `\>`
+help: add markers to start of line
+   |
+LL | /// >   > lazy continuation
+   |     +++++
+
+error: doc quote missing `>` marker
+  --> tests/ui/doc/doc_lazy_blockquote.rs:45:5
+   |
+LL | ///  lazy continuation (this results in strange indentation, but still works)
+   |     ^
+   |
+   = help: if this not intended to be a quote at all, escape it with `\>`
+help: add markers to start of line
+   |
+LL | ///    > lazy continuation (this results in strange indentation, but still works)
+   |        +
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed b/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed
new file mode 100644
index 00000000000..409e6b0bc22
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed
@@ -0,0 +1,77 @@
+#![warn(clippy::doc_lazy_continuation)]
+
+/// 1. nest here
+///    lazy continuation
+//~^ ERROR: doc list item missing indentation
+fn one() {}
+
+/// 1. first line
+///    lazy list continuations don't make warnings with this lint
+//~^ ERROR: doc list item missing indentation
+///    because they don't have the
+//~^ ERROR: doc list item missing indentation
+fn two() {}
+
+///   - nest here
+///     lazy continuation
+//~^ ERROR: doc list item missing indentation
+fn three() {}
+
+///   - first line
+///     lazy list continuations don't make warnings with this lint
+//~^ ERROR: doc list item missing indentation
+///     because they don't have the
+//~^ ERROR: doc list item missing indentation
+fn four() {}
+
+///   - nest here
+///     lazy continuation
+//~^ ERROR: doc list item missing indentation
+fn five() {}
+
+///   - - first line
+///       this will warn on the lazy continuation
+//~^ ERROR: doc list item missing indentation
+///       and so should this
+//~^ ERROR: doc list item missing indentation
+fn six() {}
+
+///   - - first line
+///
+///     this is not a lazy continuation
+fn seven() {}
+
+#[rustfmt::skip]
+// https://github.com/rust-lang/rust-clippy/pull/12770#issuecomment-2118601768
+/// Returns a list of ProtocolDescriptors from a Serde JSON input.
+///
+/// Defined Protocol Identifiers for the Protocol Descriptor
+/// We intentionally omit deprecated profile identifiers.
+/// From Bluetooth Assigned Numbers:
+/// https://www.bluetooth.com/specifications/assigned-numbers/service-discovery
+///
+/// # Arguments
+/// * `protocol_descriptors`: A Json Representation of the ProtocolDescriptors
+///     to set up. Example:
+///   'protocol_descriptors': [
+//~^ ERROR: doc list item missing indentation
+///      {
+///          'protocol': 25,  # u64 Representation of ProtocolIdentifier::AVDTP
+///          'params': [
+///              {
+///                 'data': 0x0103  # to indicate 1.3
+///              },
+///              {
+///                  'data': 0x0105  # to indicate 1.5
+///              }
+///          ]
+///      },
+///      {
+///          'protocol': 1,  # u64 Representation of ProtocolIdentifier::SDP
+///          'params': [{
+///              'data': 0x0019
+///          }]
+///      }
+///   ]
+//~^ ERROR: doc list item missing indentation
+fn eight() {}
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs b/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs
new file mode 100644
index 00000000000..30ab448a113
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs
@@ -0,0 +1,77 @@
+#![warn(clippy::doc_lazy_continuation)]
+
+/// 1. nest here
+/// lazy continuation
+//~^ ERROR: doc list item missing indentation
+fn one() {}
+
+/// 1. first line
+/// lazy list continuations don't make warnings with this lint
+//~^ ERROR: doc list item missing indentation
+/// because they don't have the
+//~^ ERROR: doc list item missing indentation
+fn two() {}
+
+///   - nest here
+/// lazy continuation
+//~^ ERROR: doc list item missing indentation
+fn three() {}
+
+///   - first line
+/// lazy list continuations don't make warnings with this lint
+//~^ ERROR: doc list item missing indentation
+/// because they don't have the
+//~^ ERROR: doc list item missing indentation
+fn four() {}
+
+///   - nest here
+/// lazy continuation
+//~^ ERROR: doc list item missing indentation
+fn five() {}
+
+///   - - first line
+/// this will warn on the lazy continuation
+//~^ ERROR: doc list item missing indentation
+///     and so should this
+//~^ ERROR: doc list item missing indentation
+fn six() {}
+
+///   - - first line
+///
+///     this is not a lazy continuation
+fn seven() {}
+
+#[rustfmt::skip]
+// https://github.com/rust-lang/rust-clippy/pull/12770#issuecomment-2118601768
+/// Returns a list of ProtocolDescriptors from a Serde JSON input.
+///
+/// Defined Protocol Identifiers for the Protocol Descriptor
+/// We intentionally omit deprecated profile identifiers.
+/// From Bluetooth Assigned Numbers:
+/// https://www.bluetooth.com/specifications/assigned-numbers/service-discovery
+///
+/// # Arguments
+/// * `protocol_descriptors`: A Json Representation of the ProtocolDescriptors
+///     to set up. Example:
+///  'protocol_descriptors': [
+//~^ ERROR: doc list item missing indentation
+///      {
+///          'protocol': 25,  # u64 Representation of ProtocolIdentifier::AVDTP
+///          'params': [
+///              {
+///                 'data': 0x0103  # to indicate 1.3
+///              },
+///              {
+///                  'data': 0x0105  # to indicate 1.5
+///              }
+///          ]
+///      },
+///      {
+///          'protocol': 1,  # u64 Representation of ProtocolIdentifier::SDP
+///          'params': [{
+///              'data': 0x0019
+///          }]
+///      }
+///  ]
+//~^ ERROR: doc list item missing indentation
+fn eight() {}
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr b/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr
new file mode 100644
index 00000000000..ddfdc49340c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr
@@ -0,0 +1,136 @@
+error: doc list item missing indentation
+  --> tests/ui/doc/doc_lazy_list.rs:4:5
+   |
+LL | /// lazy continuation
+   |     ^
+   |
+   = help: if this is supposed to be its own paragraph, add a blank line
+   = note: `-D clippy::doc-lazy-continuation` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::doc_lazy_continuation)]`
+help: indent this line
+   |
+LL | ///    lazy continuation
+   |     +++
+
+error: doc list item missing indentation
+  --> tests/ui/doc/doc_lazy_list.rs:9:5
+   |
+LL | /// lazy list continuations don't make warnings with this lint
+   |     ^
+   |
+   = help: if this is supposed to be its own paragraph, add a blank line
+help: indent this line
+   |
+LL | ///    lazy list continuations don't make warnings with this lint
+   |     +++
+
+error: doc list item missing indentation
+  --> tests/ui/doc/doc_lazy_list.rs:11:5
+   |
+LL | /// because they don't have the
+   |     ^
+   |
+   = help: if this is supposed to be its own paragraph, add a blank line
+help: indent this line
+   |
+LL | ///    because they don't have the
+   |     +++
+
+error: doc list item missing indentation
+  --> tests/ui/doc/doc_lazy_list.rs:16:5
+   |
+LL | /// lazy continuation
+   |     ^
+   |
+   = help: if this is supposed to be its own paragraph, add a blank line
+help: indent this line
+   |
+LL | ///     lazy continuation
+   |     ++++
+
+error: doc list item missing indentation
+  --> tests/ui/doc/doc_lazy_list.rs:21:5
+   |
+LL | /// lazy list continuations don't make warnings with this lint
+   |     ^
+   |
+   = help: if this is supposed to be its own paragraph, add a blank line
+help: indent this line
+   |
+LL | ///     lazy list continuations don't make warnings with this lint
+   |     ++++
+
+error: doc list item missing indentation
+  --> tests/ui/doc/doc_lazy_list.rs:23:5
+   |
+LL | /// because they don't have the
+   |     ^
+   |
+   = help: if this is supposed to be its own paragraph, add a blank line
+help: indent this line
+   |
+LL | ///     because they don't have the
+   |     ++++
+
+error: doc list item missing indentation
+  --> tests/ui/doc/doc_lazy_list.rs:28:5
+   |
+LL | /// lazy continuation
+   |     ^
+   |
+   = help: if this is supposed to be its own paragraph, add a blank line
+help: indent this line
+   |
+LL | ///     lazy continuation
+   |     ++++
+
+error: doc list item missing indentation
+  --> tests/ui/doc/doc_lazy_list.rs:33:5
+   |
+LL | /// this will warn on the lazy continuation
+   |     ^
+   |
+   = help: if this is supposed to be its own paragraph, add a blank line
+help: indent this line
+   |
+LL | ///       this will warn on the lazy continuation
+   |     ++++++
+
+error: doc list item missing indentation
+  --> tests/ui/doc/doc_lazy_list.rs:35:5
+   |
+LL | ///     and so should this
+   |     ^^^^
+   |
+   = help: if this is supposed to be its own paragraph, add a blank line
+help: indent this line
+   |
+LL | ///       and so should this
+   |         ++
+
+error: doc list item missing indentation
+  --> tests/ui/doc/doc_lazy_list.rs:56:5
+   |
+LL | ///  'protocol_descriptors': [
+   |     ^
+   |
+   = help: if this is supposed to be its own paragraph, add a blank line
+help: indent this line
+   |
+LL | ///   'protocol_descriptors': [
+   |      +
+
+error: doc list item missing indentation
+  --> tests/ui/doc/doc_lazy_list.rs:75:5
+   |
+LL | ///  ]
+   |     ^
+   |
+   = help: if this is supposed to be its own paragraph, add a blank line
+help: indent this line
+   |
+LL | ///   ]
+   |      +
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/duplicated_attributes.rs b/src/tools/clippy/tests/ui/duplicated_attributes.rs
index d51e7e37beb..97cf4a69682 100644
--- a/src/tools/clippy/tests/ui/duplicated_attributes.rs
+++ b/src/tools/clippy/tests/ui/duplicated_attributes.rs
@@ -1,5 +1,5 @@
 //@aux-build:proc_macro_attr.rs
-
+#![feature(rustc_attrs)]
 #![warn(clippy::duplicated_attributes)]
 #![cfg(any(unix, windows))]
 #![allow(dead_code)]
@@ -20,6 +20,10 @@ fn foo() {}
 #[cfg(unix)] // cfgs are not handled
 fn bar() {}
 
+// No warning:
+#[rustc_on_unimplemented(on(_Self = "&str", label = "`a"), on(_Self = "alloc::string::String", label = "a"))]
+trait Abc {}
+
 #[proc_macro_attr::duplicated_attr()] // Should not warn!
 fn babar() {}
 
diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.fixed b/src/tools/clippy/tests/ui/from_str_radix_10.fixed
index 8c253bfd99a..f9ce1defda1 100644
--- a/src/tools/clippy/tests/ui/from_str_radix_10.fixed
+++ b/src/tools/clippy/tests/ui/from_str_radix_10.fixed
@@ -1,3 +1,4 @@
+#![feature(const_int_from_str)]
 #![warn(clippy::from_str_radix_10)]
 
 mod some_mod {
@@ -59,3 +60,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
 
     Ok(())
 }
+
+fn issue_12732() {
+    const A: Result<u32, std::num::ParseIntError> = u32::from_str_radix("123", 10);
+    const B: () = {
+        let _ = u32::from_str_radix("123", 10);
+    };
+    const fn foo() {
+        let _ = u32::from_str_radix("123", 10);
+    }
+}
diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.rs b/src/tools/clippy/tests/ui/from_str_radix_10.rs
index e9d02215710..2d5b351f8da 100644
--- a/src/tools/clippy/tests/ui/from_str_radix_10.rs
+++ b/src/tools/clippy/tests/ui/from_str_radix_10.rs
@@ -1,3 +1,4 @@
+#![feature(const_int_from_str)]
 #![warn(clippy::from_str_radix_10)]
 
 mod some_mod {
@@ -59,3 +60,13 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
 
     Ok(())
 }
+
+fn issue_12732() {
+    const A: Result<u32, std::num::ParseIntError> = u32::from_str_radix("123", 10);
+    const B: () = {
+        let _ = u32::from_str_radix("123", 10);
+    };
+    const fn foo() {
+        let _ = u32::from_str_radix("123", 10);
+    }
+}
diff --git a/src/tools/clippy/tests/ui/from_str_radix_10.stderr b/src/tools/clippy/tests/ui/from_str_radix_10.stderr
index 4aa84eca261..01a1bf8940a 100644
--- a/src/tools/clippy/tests/ui/from_str_radix_10.stderr
+++ b/src/tools/clippy/tests/ui/from_str_radix_10.stderr
@@ -1,5 +1,5 @@
 error: this call to `from_str_radix` can be replaced with a call to `str::parse`
-  --> tests/ui/from_str_radix_10.rs:28:5
+  --> tests/ui/from_str_radix_10.rs:29:5
    |
 LL |     u32::from_str_radix("30", 10)?;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"30".parse::<u32>()`
@@ -8,43 +8,43 @@ LL |     u32::from_str_radix("30", 10)?;
    = help: to override `-D warnings` add `#[allow(clippy::from_str_radix_10)]`
 
 error: this call to `from_str_radix` can be replaced with a call to `str::parse`
-  --> tests/ui/from_str_radix_10.rs:31:5
+  --> tests/ui/from_str_radix_10.rs:32:5
    |
 LL |     i64::from_str_radix("24", 10)?;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"24".parse::<i64>()`
 
 error: this call to `from_str_radix` can be replaced with a call to `str::parse`
-  --> tests/ui/from_str_radix_10.rs:33:5
+  --> tests/ui/from_str_radix_10.rs:34:5
    |
 LL |     isize::from_str_radix("100", 10)?;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"100".parse::<isize>()`
 
 error: this call to `from_str_radix` can be replaced with a call to `str::parse`
-  --> tests/ui/from_str_radix_10.rs:35:5
+  --> tests/ui/from_str_radix_10.rs:36:5
    |
 LL |     u8::from_str_radix("7", 10)?;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `"7".parse::<u8>()`
 
 error: this call to `from_str_radix` can be replaced with a call to `str::parse`
-  --> tests/ui/from_str_radix_10.rs:37:5
+  --> tests/ui/from_str_radix_10.rs:38:5
    |
 LL |     u16::from_str_radix(&("10".to_owned() + "5"), 10)?;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `("10".to_owned() + "5").parse::<u16>()`
 
 error: this call to `from_str_radix` can be replaced with a call to `str::parse`
-  --> tests/ui/from_str_radix_10.rs:39:5
+  --> tests/ui/from_str_radix_10.rs:40:5
    |
 LL |     i128::from_str_radix(Test + Test, 10)?;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `(Test + Test).parse::<i128>()`
 
 error: this call to `from_str_radix` can be replaced with a call to `str::parse`
-  --> tests/ui/from_str_radix_10.rs:43:5
+  --> tests/ui/from_str_radix_10.rs:44:5
    |
 LL |     i32::from_str_radix(string, 10)?;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `string.parse::<i32>()`
 
 error: this call to `from_str_radix` can be replaced with a call to `str::parse`
-  --> tests/ui/from_str_radix_10.rs:47:5
+  --> tests/ui/from_str_radix_10.rs:48:5
    |
 LL |     i32::from_str_radix(&stringier, 10)?;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `stringier.parse::<i32>()`
diff --git a/src/tools/clippy/tests/ui/match_same_arms.stderr b/src/tools/clippy/tests/ui/match_same_arms.stderr
index a926570b60a..3c0382767c3 100644
--- a/src/tools/clippy/tests/ui/match_same_arms.stderr
+++ b/src/tools/clippy/tests/ui/match_same_arms.stderr
@@ -2,7 +2,7 @@ error: this match arm has an identical body to the `_` wildcard arm
   --> tests/ui/match_same_arms.rs:12:9
    |
 LL |         Abc::A => 0,
-   |         ^^^^^^^^^^^ help: try removing the arm
+   |         ^^^^^^^^^^^^^ help: try removing the arm
    |
    = help: or try changing either arm body
 note: `_` wildcard arm here
@@ -17,106 +17,114 @@ error: this match arm has an identical body to another arm
   --> tests/ui/match_same_arms.rs:18:9
    |
 LL |         (1, .., 3) => 42,
-   |         ----------^^^^^^
-   |         |
-   |         help: try merging the arm patterns: `(1, .., 3) | (.., 3)`
+   |         ^^^^^^^^^^^^^^^^
    |
-   = help: or try changing either arm body
-note: other arm here
-  --> tests/ui/match_same_arms.rs:19:9
+   = help: try changing either arm body
+help: or try merging the arm patterns
+   |
+LL |         (1, .., 3) | (.., 3) => 42,
+   |         ~~~~~~~~~~~~~~~~~~~~
+help: and remove this obsolete arm
+   |
+LL -         (.., 3) => 42,
    |
-LL |         (.., 3) => 42,
-   |         ^^^^^^^^^^^^^
 
 error: this match arm has an identical body to another arm
   --> tests/ui/match_same_arms.rs:25:9
    |
 LL |         51 => 1,
-   |         --^^^^^
-   |         |
-   |         help: try merging the arm patterns: `51 | 42`
+   |         ^^^^^^^
    |
-   = help: or try changing either arm body
-note: other arm here
-  --> tests/ui/match_same_arms.rs:24:9
+   = help: try changing either arm body
+help: or try merging the arm patterns
+   |
+LL |         51 | 42 => 1,
+   |         ~~~~~~~
+help: and remove this obsolete arm
+   |
+LL -         42 => 1,
    |
-LL |         42 => 1,
-   |         ^^^^^^^
 
 error: this match arm has an identical body to another arm
   --> tests/ui/match_same_arms.rs:26:9
    |
 LL |         41 => 2,
-   |         --^^^^^
-   |         |
-   |         help: try merging the arm patterns: `41 | 52`
+   |         ^^^^^^^
    |
-   = help: or try changing either arm body
-note: other arm here
-  --> tests/ui/match_same_arms.rs:27:9
+   = help: try changing either arm body
+help: or try merging the arm patterns
+   |
+LL |         41 | 52 => 2,
+   |         ~~~~~~~
+help: and remove this obsolete arm
+   |
+LL -         52 => 2,
    |
-LL |         52 => 2,
-   |         ^^^^^^^
 
 error: this match arm has an identical body to another arm
   --> tests/ui/match_same_arms.rs:33:9
    |
 LL |         2 => 2,
-   |         -^^^^^
-   |         |
-   |         help: try merging the arm patterns: `2 | 1`
+   |         ^^^^^^
    |
-   = help: or try changing either arm body
-note: other arm here
-  --> tests/ui/match_same_arms.rs:32:9
+   = help: try changing either arm body
+help: or try merging the arm patterns
+   |
+LL |         2 | 1 => 2,
+   |         ~~~~~
+help: and remove this obsolete arm
+   |
+LL -         1 => 2,
    |
-LL |         1 => 2,
-   |         ^^^^^^
 
 error: this match arm has an identical body to another arm
   --> tests/ui/match_same_arms.rs:35:9
    |
 LL |         3 => 2,
-   |         -^^^^^
-   |         |
-   |         help: try merging the arm patterns: `3 | 1`
+   |         ^^^^^^
    |
-   = help: or try changing either arm body
-note: other arm here
-  --> tests/ui/match_same_arms.rs:32:9
+   = help: try changing either arm body
+help: or try merging the arm patterns
+   |
+LL |         3 | 1 => 2,
+   |         ~~~~~
+help: and remove this obsolete arm
+   |
+LL -         1 => 2,
    |
-LL |         1 => 2,
-   |         ^^^^^^
 
 error: this match arm has an identical body to another arm
   --> tests/ui/match_same_arms.rs:33:9
    |
 LL |         2 => 2,
-   |         -^^^^^
-   |         |
-   |         help: try merging the arm patterns: `2 | 3`
+   |         ^^^^^^
    |
-   = help: or try changing either arm body
-note: other arm here
-  --> tests/ui/match_same_arms.rs:35:9
+   = help: try changing either arm body
+help: or try merging the arm patterns
+   |
+LL |         2 | 3 => 2,
+   |         ~~~~~
+help: and remove this obsolete arm
+   |
+LL -         3 => 2,
+LL +
    |
-LL |         3 => 2,
-   |         ^^^^^^
 
 error: this match arm has an identical body to another arm
   --> tests/ui/match_same_arms.rs:52:17
    |
 LL |                 CommandInfo::External { name, .. } => name.to_string(),
-   |                 ----------------------------------^^^^^^^^^^^^^^^^^^^^
-   |                 |
-   |                 help: try merging the arm patterns: `CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. }`
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: or try changing either arm body
-note: other arm here
-  --> tests/ui/match_same_arms.rs:51:17
+   = help: try changing either arm body
+help: or try merging the arm patterns
+   |
+LL |                 CommandInfo::External { name, .. } | CommandInfo::BuiltIn { name, .. } => name.to_string(),
+   |                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: and remove this obsolete arm
+   |
+LL -                 CommandInfo::BuiltIn { name, .. } => name.to_string(),
    |
-LL |                 CommandInfo::BuiltIn { name, .. } => name.to_string(),
-   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/match_same_arms2.fixed b/src/tools/clippy/tests/ui/match_same_arms2.fixed
new file mode 100644
index 00000000000..fba0cf33b3c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_same_arms2.fixed
@@ -0,0 +1,241 @@
+#![warn(clippy::match_same_arms)]
+#![allow(
+    clippy::disallowed_names,
+    clippy::diverging_sub_expression,
+    clippy::uninlined_format_args,
+    clippy::match_single_binding,
+    clippy::match_like_matches_macro
+)]
+fn bar<T>(_: T) {}
+fn foo() -> bool {
+    unimplemented!()
+}
+
+fn match_same_arms() {
+    let _ = match 42 {
+        _ => {
+            foo();
+            let mut a = 42 + [23].len() as i32;
+            if true {
+                a += 7;
+            }
+            a = -31 - a;
+            a
+        },
+    };
+    //~^^^^^^^^^^^^^^^^^^^ ERROR: this match arm has an identical body to the `_` wildcard arm
+
+    let _ = match 42 {
+        51 | 42 => foo(), //~ ERROR: this match arm has an identical body to another arm
+        _ => true,
+    };
+
+    let _ = match Some(42) {
+        None | Some(_) => 24, //~ ERROR: this match arm has an identical body to another arm
+    };
+
+    let _ = match Some(42) {
+        Some(foo) => 24,
+        None => 24,
+    };
+
+    let _ = match Some(42) {
+        Some(42) => 24,
+        Some(a) => 24, // bindings are different
+        None => 0,
+    };
+
+    let _ = match Some(42) {
+        Some(a) if a > 0 => 24,
+        Some(a) => 24, // one arm has a guard
+        None => 0,
+    };
+
+    match (Some(42), Some(42)) {
+        (None, Some(a)) | (Some(a), None) => bar(a), //~ ERROR: this match arm has an identical body to another arm
+        _ => (),
+    }
+
+    // No warning because guards are different
+    let _ = match Some(42) {
+        Some(a) if a == 42 => a,
+        Some(a) if a == 24 => a,
+        Some(_) => 24,
+        None => 0,
+    };
+
+    let _ = match (Some(42), Some(42)) {
+        (None, Some(a)) | (Some(a), None) if a == 42 => a, //~ ERROR: this match arm has an identical body to another arm
+        _ => 0,
+    };
+
+    match (Some(42), Some(42)) {
+        (Some(a), ..) | (.., Some(a)) => bar(a), //~ ERROR: this match arm has an identical body to another arm
+        _ => (),
+    }
+
+    let _ = match Some(()) {
+        Some(()) => 0.0,
+        None => -0.0,
+    };
+
+    match (Some(42), Some("")) {
+        (Some(a), None) => bar(a),
+        (None, Some(a)) => bar(a), // bindings have different types
+        _ => (),
+    }
+
+    let x: Result<i32, &str> = Ok(3);
+
+    // No warning because of the guard.
+    match x {
+        Ok(x) if x * x == 64 => println!("ok"),
+        Ok(_) => println!("ok"),
+        Err(_) => println!("err"),
+    }
+
+    // This used to be a false positive; see issue #1996.
+    match x {
+        Ok(3) => println!("ok"),
+        Ok(x) if x * x == 64 => println!("ok 64"),
+        Ok(_) => println!("ok"),
+        Err(_) => println!("err"),
+    }
+
+    match (x, Some(1i32)) {
+        (Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x), //~ ERROR: this match arm has an identical body to another arm
+        _ => println!("err"),
+    }
+
+    // No warning; different types for `x`.
+    match (x, Some(1.0f64)) {
+        (Ok(x), Some(_)) => println!("ok {}", x),
+        (Ok(_), Some(x)) => println!("ok {}", x),
+        _ => println!("err"),
+    }
+
+    // False negative #2251.
+    match x {
+        Ok(_tmp) => println!("ok"),
+        Ok(_) | Ok(3) => println!("ok"), //~ ERROR: this match arm has an identical body to another arm
+        Err(_) => {
+            unreachable!();
+        },
+    }
+
+    // False positive #1390
+    macro_rules! empty {
+        ($e:expr) => {};
+    }
+    match 0 {
+        0 => {
+            empty!(0);
+        },
+        1 => {
+            empty!(1);
+        },
+        x => {
+            empty!(x);
+        },
+    };
+
+    // still lint if the tokens are the same
+    match 0 {
+        1 | 0 => {
+            empty!(0);
+        },
+        x => {
+            empty!(x);
+        },
+    }
+    //~^^^^^^^ ERROR: this match arm has an identical body to another arm
+
+    match_expr_like_matches_macro_priority();
+}
+
+fn match_expr_like_matches_macro_priority() {
+    enum E {
+        A,
+        B,
+        C,
+    }
+    let x = E::A;
+    let _ans = match x {
+        E::A => false,
+        E::B => false,
+        _ => true,
+    };
+}
+
+fn main() {
+    let _ = match Some(0) {
+        Some(0) => 0,
+        Some(1) => 1,
+        #[cfg(feature = "foo")]
+        Some(2) => 2,
+        _ => 1,
+    };
+
+    enum Foo {
+        X(u32),
+        Y(u32),
+        Z(u32),
+    }
+
+    // Don't lint. `Foo::X(0)` and `Foo::Z(_)` overlap with the arm in between.
+    let _ = match Foo::X(0) {
+        Foo::X(0) => 1,
+        Foo::X(_) | Foo::Y(_) | Foo::Z(0) => 2,
+        Foo::Z(_) => 1,
+        _ => 0,
+    };
+
+    // Suggest moving `Foo::Z(_)` up.
+    let _ = match Foo::X(0) {
+        Foo::X(0) | Foo::Z(_) => 1, //~ ERROR: this match arm has an identical body to another arm
+        Foo::X(_) | Foo::Y(_) => 2,
+        _ => 0,
+    };
+
+    // Suggest moving `Foo::X(0)` down.
+    let _ = match Foo::X(0) {
+        Foo::Y(_) | Foo::Z(0) => 2,
+        Foo::Z(_) | Foo::X(0) => 1, //~ ERROR: this match arm has an identical body to another arm
+        _ => 0,
+    };
+
+    // Don't lint.
+    let _ = match 0 {
+        -2 => 1,
+        -5..=50 => 2,
+        -150..=88 => 1,
+        _ => 3,
+    };
+
+    struct Bar {
+        x: u32,
+        y: u32,
+        z: u32,
+    }
+
+    // Lint.
+    let _ = match None {
+        Some(Bar { y: 10, z: 0, .. }) => 2,
+        None => 50,
+        Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1, //~ ERROR: this match arm has an identical body to another arm
+        _ => 200,
+    };
+
+    let _ = match 0 {
+        0 => todo!(),
+        1 => todo!(),
+        2 => core::convert::identity::<u32>(todo!()),
+        3 => core::convert::identity::<u32>(todo!()),
+        _ => 5,
+    };
+
+    let _ = match 0 {
+        1 | 0 => cfg!(not_enable),
+        _ => false,
+    };
+}
diff --git a/src/tools/clippy/tests/ui/match_same_arms2.rs b/src/tools/clippy/tests/ui/match_same_arms2.rs
index 85ad0962eb4..8a4e3b325bb 100644
--- a/src/tools/clippy/tests/ui/match_same_arms2.rs
+++ b/src/tools/clippy/tests/ui/match_same_arms2.rs
@@ -2,9 +2,10 @@
 #![allow(
     clippy::disallowed_names,
     clippy::diverging_sub_expression,
-    clippy::uninlined_format_args
+    clippy::uninlined_format_args,
+    clippy::match_single_binding,
+    clippy::match_like_matches_macro
 )]
-//@no-rustfix
 fn bar<T>(_: T) {}
 fn foo() -> bool {
     unimplemented!()
diff --git a/src/tools/clippy/tests/ui/match_same_arms2.stderr b/src/tools/clippy/tests/ui/match_same_arms2.stderr
index f4c38c1af89..3d15176ccf9 100644
--- a/src/tools/clippy/tests/ui/match_same_arms2.stderr
+++ b/src/tools/clippy/tests/ui/match_same_arms2.stderr
@@ -1,18 +1,18 @@
 error: this match arm has an identical body to the `_` wildcard arm
-  --> tests/ui/match_same_arms2.rs:15:9
+  --> tests/ui/match_same_arms2.rs:16:9
    |
 LL | /         42 => {
 LL | |             foo();
 LL | |             let mut a = 42 + [23].len() as i32;
 LL | |             if true {
 ...  |
-LL | |             a
 LL | |         },
-   | |_________^ help: try removing the arm
+LL | |         _ => {
+   | |________^ help: try removing the arm
    |
    = help: or try changing either arm body
 note: `_` wildcard arm here
-  --> tests/ui/match_same_arms2.rs:24:9
+  --> tests/ui/match_same_arms2.rs:25:9
    |
 LL | /         _ => {
 LL | |             foo();
@@ -26,203 +26,200 @@ LL | |         },
    = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]`
 
 error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:38:9
+  --> tests/ui/match_same_arms2.rs:39:9
    |
 LL |         51 => foo(),
-   |         --^^^^^^^^^
-   |         |
-   |         help: try merging the arm patterns: `51 | 42`
+   |         ^^^^^^^^^^^
    |
-   = help: or try changing either arm body
-note: other arm here
-  --> tests/ui/match_same_arms2.rs:37:9
+   = help: try changing either arm body
+help: or try merging the arm patterns
+   |
+LL |         51 | 42 => foo(),
+   |         ~~~~~~~
+help: and remove this obsolete arm
+   |
+LL -         42 => foo(),
    |
-LL |         42 => foo(),
-   |         ^^^^^^^^^^^
 
 error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:44:9
+  --> tests/ui/match_same_arms2.rs:45:9
    |
 LL |         None => 24,
-   |         ----^^^^^^
-   |         |
-   |         help: try merging the arm patterns: `None | Some(_)`
+   |         ^^^^^^^^^^
    |
-   = help: or try changing either arm body
-note: other arm here
-  --> tests/ui/match_same_arms2.rs:43:9
+   = help: try changing either arm body
+help: or try merging the arm patterns
+   |
+LL |         None | Some(_) => 24,
+   |         ~~~~~~~~~~~~~~
+help: and remove this obsolete arm
+   |
+LL -         Some(_) => 24,
    |
-LL |         Some(_) => 24,
-   |         ^^^^^^^^^^^^^
 
 error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:66:9
+  --> tests/ui/match_same_arms2.rs:67:9
    |
 LL |         (None, Some(a)) => bar(a),
-   |         ---------------^^^^^^^^^^
-   |         |
-   |         help: try merging the arm patterns: `(None, Some(a)) | (Some(a), None)`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: or try changing either arm body
-note: other arm here
-  --> tests/ui/match_same_arms2.rs:65:9
+   = help: try changing either arm body
+help: or try merging the arm patterns
+   |
+LL |         (None, Some(a)) | (Some(a), None) => bar(a),
+   |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: and remove this obsolete arm
+   |
+LL -         (Some(a), None) => bar(a),
    |
-LL |         (Some(a), None) => bar(a),
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:80:9
+  --> tests/ui/match_same_arms2.rs:81:9
    |
 LL |         (None, Some(a)) if a == 42 => a,
-   |         ---------------^^^^^^^^^^^^^^^^
-   |         |
-   |         help: try merging the arm patterns: `(None, Some(a)) | (Some(a), None)`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: or try changing either arm body
-note: other arm here
-  --> tests/ui/match_same_arms2.rs:79:9
+   = help: try changing either arm body
+help: or try merging the arm patterns
+   |
+LL |         (None, Some(a)) | (Some(a), None) if a == 42 => a,
+   |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: and remove this obsolete arm
+   |
+LL -         (Some(a), None) if a == 42 => a,
    |
-LL |         (Some(a), None) if a == 42 => a,
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:85:9
+  --> tests/ui/match_same_arms2.rs:86:9
    |
 LL |         (Some(a), ..) => bar(a),
-   |         -------------^^^^^^^^^^
-   |         |
-   |         help: try merging the arm patterns: `(Some(a), ..) | (.., Some(a))`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: or try changing either arm body
-note: other arm here
-  --> tests/ui/match_same_arms2.rs:86:9
+   = help: try changing either arm body
+help: or try merging the arm patterns
+   |
+LL |         (Some(a), ..) | (.., Some(a)) => bar(a),
+   |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: and remove this obsolete arm
+   |
+LL -         (.., Some(a)) => bar(a),
    |
-LL |         (.., Some(a)) => bar(a),
-   |         ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:119:9
+  --> tests/ui/match_same_arms2.rs:120:9
    |
 LL |         (Ok(x), Some(_)) => println!("ok {}", x),
-   |         ----------------^^^^^^^^^^^^^^^^^^^^^^^^
-   |         |
-   |         help: try merging the arm patterns: `(Ok(x), Some(_)) | (Ok(_), Some(x))`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: or try changing either arm body
-note: other arm here
-  --> tests/ui/match_same_arms2.rs:120:9
+   = help: try changing either arm body
+help: or try merging the arm patterns
+   |
+LL |         (Ok(x), Some(_)) | (Ok(_), Some(x)) => println!("ok {}", x),
+   |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: and remove this obsolete arm
+   |
+LL -         (Ok(_), Some(x)) => println!("ok {}", x),
    |
-LL |         (Ok(_), Some(x)) => println!("ok {}", x),
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:135:9
+  --> tests/ui/match_same_arms2.rs:136:9
    |
 LL |         Ok(_) => println!("ok"),
-   |         -----^^^^^^^^^^^^^^^^^^
-   |         |
-   |         help: try merging the arm patterns: `Ok(_) | Ok(3)`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: or try changing either arm body
-note: other arm here
-  --> tests/ui/match_same_arms2.rs:134:9
+   = help: try changing either arm body
+help: or try merging the arm patterns
+   |
+LL |         Ok(_) | Ok(3) => println!("ok"),
+   |         ~~~~~~~~~~~~~
+help: and remove this obsolete arm
+   |
+LL -         Ok(3) => println!("ok"),
    |
-LL |         Ok(3) => println!("ok"),
-   |         ^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:162:9
+  --> tests/ui/match_same_arms2.rs:163:9
    |
-LL |           1 => {
-   |           ^ help: try merging the arm patterns: `1 | 0`
-   |  _________|
-   | |
+LL | /         1 => {
 LL | |             empty!(0);
 LL | |         },
    | |_________^
    |
-   = help: or try changing either arm body
-note: other arm here
-  --> tests/ui/match_same_arms2.rs:159:9
+   = help: try changing either arm body
+help: or try merging the arm patterns
+   |
+LL |         1 | 0 => {
+   |         ~~~~~
+help: and remove this obsolete arm
+   |
+LL -         0 => {
+LL -             empty!(0);
+LL -         },
    |
-LL | /         0 => {
-LL | |             empty!(0);
-LL | |         },
-   | |_________^
-
-error: match expression looks like `matches!` macro
-  --> tests/ui/match_same_arms2.rs:181:16
-   |
-LL |       let _ans = match x {
-   |  ________________^
-LL | |         E::A => false,
-LL | |         E::B => false,
-LL | |         _ => true,
-LL | |     };
-   | |_____^ help: try: `!matches!(x, E::A | E::B)`
-   |
-   = note: `-D clippy::match-like-matches-macro` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::match_like_matches_macro)]`
 
 error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:213:9
+  --> tests/ui/match_same_arms2.rs:214:9
    |
 LL |         Foo::X(0) => 1,
-   |         ---------^^^^^
-   |         |
-   |         help: try merging the arm patterns: `Foo::X(0) | Foo::Z(_)`
+   |         ^^^^^^^^^^^^^^
    |
-   = help: or try changing either arm body
-note: other arm here
-  --> tests/ui/match_same_arms2.rs:215:9
+   = help: try changing either arm body
+help: or try merging the arm patterns
+   |
+LL |         Foo::X(0) | Foo::Z(_) => 1,
+   |         ~~~~~~~~~~~~~~~~~~~~~
+help: and remove this obsolete arm
+   |
+LL -         Foo::Z(_) => 1,
    |
-LL |         Foo::Z(_) => 1,
-   |         ^^^^^^^^^^^^^^
 
 error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:223:9
+  --> tests/ui/match_same_arms2.rs:224:9
    |
 LL |         Foo::Z(_) => 1,
-   |         ---------^^^^^
-   |         |
-   |         help: try merging the arm patterns: `Foo::Z(_) | Foo::X(0)`
+   |         ^^^^^^^^^^^^^^
    |
-   = help: or try changing either arm body
-note: other arm here
-  --> tests/ui/match_same_arms2.rs:221:9
+   = help: try changing either arm body
+help: or try merging the arm patterns
+   |
+LL |         Foo::Z(_) | Foo::X(0) => 1,
+   |         ~~~~~~~~~~~~~~~~~~~~~
+help: and remove this obsolete arm
+   |
+LL -         Foo::X(0) => 1,
    |
-LL |         Foo::X(0) => 1,
-   |         ^^^^^^^^^^^^^^
 
 error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:246:9
+  --> tests/ui/match_same_arms2.rs:247:9
    |
 LL |         Some(Bar { y: 0, x: 5, .. }) => 1,
-   |         ----------------------------^^^^^
-   |         |
-   |         help: try merging the arm patterns: `Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. })`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: or try changing either arm body
-note: other arm here
-  --> tests/ui/match_same_arms2.rs:243:9
+   = help: try changing either arm body
+help: or try merging the arm patterns
+   |
+LL |         Some(Bar { y: 0, x: 5, .. }) | Some(Bar { x: 0, y: 5, .. }) => 1,
+   |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+help: and remove this obsolete arm
+   |
+LL -         Some(Bar { x: 0, y: 5, .. }) => 1,
    |
-LL |         Some(Bar { x: 0, y: 5, .. }) => 1,
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this match arm has an identical body to another arm
-  --> tests/ui/match_same_arms2.rs:260:9
+  --> tests/ui/match_same_arms2.rs:261:9
    |
 LL |         1 => cfg!(not_enable),
-   |         -^^^^^^^^^^^^^^^^^^^^
-   |         |
-   |         help: try merging the arm patterns: `1 | 0`
+   |         ^^^^^^^^^^^^^^^^^^^^^
    |
-   = help: or try changing either arm body
-note: other arm here
-  --> tests/ui/match_same_arms2.rs:259:9
+   = help: try changing either arm body
+help: or try merging the arm patterns
+   |
+LL |         1 | 0 => cfg!(not_enable),
+   |         ~~~~~
+help: and remove this obsolete arm
+   |
+LL -         0 => cfg!(not_enable),
    |
-LL |         0 => cfg!(not_enable),
-   |         ^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 14 previous errors
+error: aborting due to 13 previous errors
 
diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed
new file mode 100644
index 00000000000..804c0a869a9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.fixed
@@ -0,0 +1,61 @@
+#![feature(non_exhaustive_omitted_patterns_lint)]
+#![warn(clippy::match_same_arms)]
+#![no_main]
+use std::sync::atomic::Ordering; // #[non_exhaustive] enum
+
+fn repeat() -> ! {
+    panic!()
+}
+
+pub fn f(x: Ordering) {
+    #[deny(non_exhaustive_omitted_patterns)]
+    match x {
+        Ordering::Relaxed => println!("relaxed"),
+        Ordering::Release => println!("release"),
+        Ordering::Acquire => println!("acquire"),
+        Ordering::AcqRel | Ordering::SeqCst => repeat(),
+        _ => repeat(),
+    }
+}
+
+mod f {
+    #![deny(non_exhaustive_omitted_patterns)]
+
+    use super::*;
+
+    pub fn f(x: Ordering) {
+        match x {
+            Ordering::Relaxed => println!("relaxed"),
+            Ordering::Release => println!("release"),
+            Ordering::Acquire => println!("acquire"),
+            Ordering::AcqRel | Ordering::SeqCst => repeat(),
+            _ => repeat(),
+        }
+    }
+}
+
+// Below should still lint
+
+pub fn g(x: Ordering) {
+    match x {
+        Ordering::Relaxed => println!("relaxed"),
+        Ordering::Release => println!("release"),
+        Ordering::Acquire => println!("acquire"),
+        //~^ ERROR: this match arm has an identical body to the `_` wildcard arm
+        _ => repeat(),
+    }
+}
+
+mod g {
+    use super::*;
+
+    pub fn g(x: Ordering) {
+        match x {
+            Ordering::Relaxed => println!("relaxed"),
+            Ordering::Release => println!("release"),
+            Ordering::Acquire => println!("acquire"),
+            //~^ ERROR: this match arm has an identical body to the `_` wildcard arm
+            _ => repeat(),
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs
index 5c277f925a8..e50663932a1 100644
--- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs
+++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.rs
@@ -1,7 +1,6 @@
 #![feature(non_exhaustive_omitted_patterns_lint)]
 #![warn(clippy::match_same_arms)]
 #![no_main]
-//@no-rustfix
 use std::sync::atomic::Ordering; // #[non_exhaustive] enum
 
 fn repeat() -> ! {
diff --git a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr
index cf2a75354e1..aa7f8c95dce 100644
--- a/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr
+++ b/src/tools/clippy/tests/ui/match_same_arms_non_exhaustive.stderr
@@ -1,12 +1,13 @@
 error: this match arm has an identical body to the `_` wildcard arm
-  --> tests/ui/match_same_arms_non_exhaustive.rs:45:9
+  --> tests/ui/match_same_arms_non_exhaustive.rs:44:9
    |
-LL |         Ordering::AcqRel | Ordering::SeqCst => repeat(),
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm
+LL | /         Ordering::AcqRel | Ordering::SeqCst => repeat(),
+LL | |
+   | |________^ help: try removing the arm
    |
    = help: or try changing either arm body
 note: `_` wildcard arm here
-  --> tests/ui/match_same_arms_non_exhaustive.rs:47:9
+  --> tests/ui/match_same_arms_non_exhaustive.rs:46:9
    |
 LL |         _ => repeat(),
    |         ^^^^^^^^^^^^^
@@ -14,14 +15,15 @@ LL |         _ => repeat(),
    = help: to override `-D warnings` add `#[allow(clippy::match_same_arms)]`
 
 error: this match arm has an identical body to the `_` wildcard arm
-  --> tests/ui/match_same_arms_non_exhaustive.rs:59:13
+  --> tests/ui/match_same_arms_non_exhaustive.rs:58:13
    |
-LL |             Ordering::AcqRel | Ordering::SeqCst => repeat(),
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try removing the arm
+LL | /             Ordering::AcqRel | Ordering::SeqCst => repeat(),
+LL | |
+   | |____________^ help: try removing the arm
    |
    = help: or try changing either arm body
 note: `_` wildcard arm here
-  --> tests/ui/match_same_arms_non_exhaustive.rs:61:13
+  --> tests/ui/match_same_arms_non_exhaustive.rs:60:13
    |
 LL |             _ => repeat(),
    |             ^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
index d026e009684..2750e0cdf3f 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
@@ -161,7 +161,23 @@ union U {
     f: u32,
 }
 
-// Do not lint because accessing union fields from const functions is unstable
+// Do not lint because accessing union fields from const functions is unstable in 1.55
+#[clippy::msrv = "1.55"]
 fn h(u: U) -> u32 {
     unsafe { u.f }
 }
+
+mod msrv {
+    struct Foo(*const u8, *mut u8);
+
+    impl Foo {
+        #[clippy::msrv = "1.57"]
+        fn deref_ptr_cannot_be_const(self) -> usize {
+            unsafe { *self.0 as usize }
+        }
+        #[clippy::msrv = "1.58"]
+        fn deref_mut_ptr_cannot_be_const(self) -> usize {
+            unsafe { *self.1 as usize }
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
index 12a8320c8f3..06dbbeb31c0 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
@@ -113,3 +113,31 @@ impl const Drop for D {
 // Lint this, since it can be dropped in const contexts
 // FIXME(effects)
 fn d(this: D) {}
+
+mod msrv {
+    struct Foo(*const u8, &'static u8);
+
+    impl Foo {
+        #[clippy::msrv = "1.58"]
+        fn deref_ptr_can_be_const(self) -> usize {
+            //~^ ERROR: this could be a `const fn`
+            unsafe { *self.0 as usize }
+        }
+
+        fn deref_copied_val(self) -> usize {
+            //~^ ERROR: this could be a `const fn`
+            *self.1 as usize
+        }
+    }
+
+    union Bar {
+        val: u8,
+    }
+
+    #[clippy::msrv = "1.56"]
+    fn union_access_can_be_const() {
+        //~^ ERROR: this could be a `const fn`
+        let bar = Bar { val: 1 };
+        let _ = unsafe { bar.val };
+    }
+}
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
index 082459fd821..b2cade30563 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
@@ -102,5 +102,33 @@ LL | |     46
 LL | | }
    | |_^
 
-error: aborting due to 11 previous errors
+error: this could be a `const fn`
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:122:9
+   |
+LL | /         fn deref_ptr_can_be_const(self) -> usize {
+LL | |
+LL | |             unsafe { *self.0 as usize }
+LL | |         }
+   | |_________^
+
+error: this could be a `const fn`
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:127:9
+   |
+LL | /         fn deref_copied_val(self) -> usize {
+LL | |
+LL | |             *self.1 as usize
+LL | |         }
+   | |_________^
+
+error: this could be a `const fn`
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:138:5
+   |
+LL | /     fn union_access_can_be_const() {
+LL | |
+LL | |         let bar = Bar { val: 1 };
+LL | |         let _ = unsafe { bar.val };
+LL | |     }
+   | |_____^
+
+error: aborting due to 14 previous errors
 
diff --git a/src/tools/clippy/tests/ui/missing_panics_doc.rs b/src/tools/clippy/tests/ui/missing_panics_doc.rs
index 0e1533fc1ab..b0fa8e98859 100644
--- a/src/tools/clippy/tests/ui/missing_panics_doc.rs
+++ b/src/tools/clippy/tests/ui/missing_panics_doc.rs
@@ -191,3 +191,11 @@ fn from_declared_macro_should_lint_at_macrosite() {
     // Not here.
     some_macro_that_panics!()
 }
+
+pub fn issue_12760<const N: usize>() {
+    const {
+        if N == 0 {
+            panic!();
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed
index bd7a9a0b984..5478372cbe0 100644
--- a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed
+++ b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.fixed
@@ -141,8 +141,8 @@ fn main() {
         let f = |arg| {
             let loc = "loc".to_owned();
             let _ = std::fs::write("x", &env); // Don't lint. In environment
-            let _ = std::fs::write("x", arg);
-            let _ = std::fs::write("x", loc);
+            let _ = std::fs::write("x", &arg);
+            let _ = std::fs::write("x", &loc);
         };
         let _ = std::fs::write("x", &env); // Don't lint. Borrowed by `f`
         f(arg);
@@ -158,13 +158,13 @@ fn main() {
         fn f(_: impl Debug) {}
 
         let x = X;
-        f(&x); // Don't lint. Has significant drop
+        f(&x); // Don't lint, not copy, passed by a reference to a variable
     }
     {
         fn f(_: impl AsRef<str>) {}
 
         let x = String::new();
-        f(x);
+        f(&x);
     }
     {
         fn f(_: impl AsRef<str>) {}
@@ -299,4 +299,38 @@ fn main() {
             check_str(&owner.0); // Don't lint. `owner` can't be partially moved because it impl Drop
         }
     }
+    {
+        #[derive(Debug)]
+        struct X(Vec<u8>);
+
+        fn f(_: impl Debug) {}
+
+        let x = X(vec![]);
+        f(&x); // Don't lint, makes x unavailable later
+    }
+    {
+        #[derive(Debug)]
+        struct X;
+
+        impl Drop for X {
+            fn drop(&mut self) {}
+        }
+
+        fn f(_: impl Debug) {}
+
+        #[derive(Debug)]
+        struct Y(X);
+
+        let y = Y(X);
+        f(&y); // Don't lint. Not copy, passed by a reference to value
+    }
+    {
+        fn f(_: impl AsRef<str>) {}
+        let x = String::new();
+        f(&x); // Don't lint, not a copy, makes it unavailable later
+        f(String::new()); // Lint, makes no difference
+        let y = "".to_owned();
+        f(&y); // Don't lint
+        f("".to_owned()); // Lint
+    }
 }
diff --git a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs
index 5cfd4ce30cc..2643815d939 100644
--- a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs
+++ b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.rs
@@ -158,7 +158,7 @@ fn main() {
         fn f(_: impl Debug) {}
 
         let x = X;
-        f(&x); // Don't lint. Has significant drop
+        f(&x); // Don't lint, not copy, passed by a reference to a variable
     }
     {
         fn f(_: impl AsRef<str>) {}
@@ -299,4 +299,38 @@ fn main() {
             check_str(&owner.0); // Don't lint. `owner` can't be partially moved because it impl Drop
         }
     }
+    {
+        #[derive(Debug)]
+        struct X(Vec<u8>);
+
+        fn f(_: impl Debug) {}
+
+        let x = X(vec![]);
+        f(&x); // Don't lint, makes x unavailable later
+    }
+    {
+        #[derive(Debug)]
+        struct X;
+
+        impl Drop for X {
+            fn drop(&mut self) {}
+        }
+
+        fn f(_: impl Debug) {}
+
+        #[derive(Debug)]
+        struct Y(X);
+
+        let y = Y(X);
+        f(&y); // Don't lint. Not copy, passed by a reference to value
+    }
+    {
+        fn f(_: impl AsRef<str>) {}
+        let x = String::new();
+        f(&x); // Don't lint, not a copy, makes it unavailable later
+        f(&String::new()); // Lint, makes no difference
+        let y = "".to_owned();
+        f(&y); // Don't lint
+        f(&"".to_owned()); // Lint
+    }
 }
diff --git a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.stderr b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.stderr
index 83c076f8d86..fba0755d14b 100644
--- a/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.stderr
+++ b/src/tools/clippy/tests/ui/needless_borrows_for_generic_args.stderr
@@ -50,28 +50,22 @@ LL |         let _ = Command::new("ls").args(&["-a", "-l"]).status().unwrap();
    |                                         ^^^^^^^^^^^^^ help: change this to: `["-a", "-l"]`
 
 error: the borrowed expression implements the required traits
-  --> tests/ui/needless_borrows_for_generic_args.rs:144:41
-   |
-LL |             let _ = std::fs::write("x", &arg);
-   |                                         ^^^^ help: change this to: `arg`
-
-error: the borrowed expression implements the required traits
-  --> tests/ui/needless_borrows_for_generic_args.rs:145:41
+  --> tests/ui/needless_borrows_for_generic_args.rs:247:13
    |
-LL |             let _ = std::fs::write("x", &loc);
-   |                                         ^^^^ help: change this to: `loc`
+LL |         foo(&a);
+   |             ^^ help: change this to: `a`
 
 error: the borrowed expression implements the required traits
-  --> tests/ui/needless_borrows_for_generic_args.rs:167:11
+  --> tests/ui/needless_borrows_for_generic_args.rs:331:11
    |
-LL |         f(&x);
-   |           ^^ help: change this to: `x`
+LL |         f(&String::new()); // Lint, makes no difference
+   |           ^^^^^^^^^^^^^^ help: change this to: `String::new()`
 
 error: the borrowed expression implements the required traits
-  --> tests/ui/needless_borrows_for_generic_args.rs:247:13
+  --> tests/ui/needless_borrows_for_generic_args.rs:334:11
    |
-LL |         foo(&a);
-   |             ^^ help: change this to: `a`
+LL |         f(&"".to_owned()); // Lint
+   |           ^^^^^^^^^^^^^^ help: change this to: `"".to_owned()`
 
-error: aborting due to 12 previous errors
+error: aborting due to 11 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_late_init.stderr b/src/tools/clippy/tests/ui/needless_late_init.stderr
index 1695784030d..ce64861fa40 100644
--- a/src/tools/clippy/tests/ui/needless_late_init.stderr
+++ b/src/tools/clippy/tests/ui/needless_late_init.stderr
@@ -8,10 +8,11 @@ LL |     a = "zero";
    |
    = note: `-D clippy::needless-late-init` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::needless_late_init)]`
-help: declare `a` here
+help: move the declaration `a` here
+   |
+LL ~     
+LL ~     let a = "zero";
    |
-LL |     let a = "zero";
-   |     ~~~~~
 
 error: unneeded late initialization
   --> tests/ui/needless_late_init.rs:30:5
@@ -22,10 +23,12 @@ LL |     let c;
 LL |     b = 1;
    |     ^^^^^ initialised here
    |
-help: declare `b` here
+help: move the declaration `b` here
+   |
+LL ~     
+LL |     let c;
+LL ~     let b = 1;
    |
-LL |     let b = 1;
-   |     ~~~~~
 
 error: unneeded late initialization
   --> tests/ui/needless_late_init.rs:31:5
@@ -36,10 +39,12 @@ LL |     b = 1;
 LL |     c = 2;
    |     ^^^^^ initialised here
    |
-help: declare `c` here
+help: move the declaration `c` here
+   |
+LL ~     
+LL |     b = 1;
+LL ~     let c = 2;
    |
-LL |     let c = 2;
-   |     ~~~~~
 
 error: unneeded late initialization
   --> tests/ui/needless_late_init.rs:35:5
@@ -49,10 +54,11 @@ LL |     let d: usize;
 LL |     d = 1;
    |     ^^^^^ initialised here
    |
-help: declare `d` here
+help: move the declaration `d` here
+   |
+LL ~     
+LL ~     let d: usize = 1;
    |
-LL |     let d: usize = 1;
-   |     ~~~~~~~~~~~~
 
 error: unneeded late initialization
   --> tests/ui/needless_late_init.rs:38:5
@@ -62,10 +68,11 @@ LL |     let e;
 LL |     e = format!("{}", d);
    |     ^^^^^^^^^^^^^^^^^^^^ initialised here
    |
-help: declare `e` here
+help: move the declaration `e` here
+   |
+LL ~     
+LL ~     let e = format!("{}", d);
    |
-LL |     let e = format!("{}", d);
-   |     ~~~~~
 
 error: unneeded late initialization
   --> tests/ui/needless_late_init.rs:43:5
@@ -73,20 +80,17 @@ error: unneeded late initialization
 LL |     let a;
    |     ^^^^^^
    |
-help: declare `a` here
-   |
-LL |     let a = match n {
-   |     +++++++
-help: remove the assignments from the `match` arms
+help: move the declaration `a` here and remove the assignments from the `match` arms
    |
+LL ~     
+LL |     let n = 1;
+LL ~     let a = match n {
 LL ~         1 => "one",
 LL |         _ => {
 LL ~             "two"
+LL |         },
+LL ~     };
    |
-help: add a semicolon after the `match` expression
-   |
-LL |     };
-   |      +
 
 error: unneeded late initialization
   --> tests/ui/needless_late_init.rs:52:5
@@ -94,20 +98,15 @@ error: unneeded late initialization
 LL |     let b;
    |     ^^^^^^
    |
-help: declare `b` here
-   |
-LL |     let b = if n == 3 {
-   |     +++++++
-help: remove the assignments from the branches
+help: move the declaration `b` here and remove the assignments from the branches
    |
+LL ~     
+LL ~     let b = if n == 3 {
 LL ~         "four"
 LL |     } else {
 LL ~         "five"
+LL ~     };
    |
-help: add a semicolon after the `if` expression
-   |
-LL |     };
-   |      +
 
 error: unneeded late initialization
   --> tests/ui/needless_late_init.rs:59:5
@@ -115,20 +114,16 @@ error: unneeded late initialization
 LL |     let d;
    |     ^^^^^^
    |
-help: declare `d` here
-   |
-LL |     let d = if true {
-   |     +++++++
-help: remove the assignments from the branches
+help: move the declaration `d` here and remove the assignments from the branches
    |
+LL ~     
+LL ~     let d = if true {
+LL |         let temp = 5;
 LL ~         temp
 LL |     } else {
 LL ~         15
+LL ~     };
    |
-help: add a semicolon after the `if` expression
-   |
-LL |     };
-   |      +
 
 error: unneeded late initialization
   --> tests/ui/needless_late_init.rs:67:5
@@ -136,20 +131,15 @@ error: unneeded late initialization
 LL |     let e;
    |     ^^^^^^
    |
-help: declare `e` here
-   |
-LL |     let e = if true {
-   |     +++++++
-help: remove the assignments from the branches
+help: move the declaration `e` here and remove the assignments from the branches
    |
+LL ~     
+LL ~     let e = if true {
 LL ~         format!("{} {}", a, b)
 LL |     } else {
 LL ~         format!("{}", n)
+LL ~     };
    |
-help: add a semicolon after the `if` expression
-   |
-LL |     };
-   |      +
 
 error: unneeded late initialization
   --> tests/ui/needless_late_init.rs:74:5
@@ -157,14 +147,11 @@ error: unneeded late initialization
 LL |     let f;
    |     ^^^^^^
    |
-help: declare `f` here
-   |
-LL |     let f = match 1 {
-   |     +++++++
-help: remove the assignments from the `match` arms
+help: move the declaration `f` here and remove the assignments from the `match` arms
    |
-LL -         1 => f = "three",
-LL +         1 => "three",
+LL ~     
+LL ~     let f = match 1 {
+LL ~         1 => "three",
    |
 
 error: unneeded late initialization
@@ -173,19 +160,15 @@ error: unneeded late initialization
 LL |     let g: usize;
    |     ^^^^^^^^^^^^^
    |
-help: declare `g` here
-   |
-LL |     let g: usize = if true {
-   |     ++++++++++++++
-help: remove the assignments from the branches
-   |
-LL -         g = 5;
-LL +         5
+help: move the declaration `g` here and remove the assignments from the branches
    |
-help: add a semicolon after the `if` expression
+LL ~     
+LL ~     let g: usize = if true {
+LL ~         5
+LL |     } else {
+LL |         panic!();
+LL ~     };
    |
-LL |     };
-   |      +
 
 error: unneeded late initialization
   --> tests/ui/needless_late_init.rs:88:5
@@ -196,10 +179,12 @@ LL |     let y = SignificantDrop;
 LL |     x = 1;
    |     ^^^^^ initialised here
    |
-help: declare `x` here
+help: move the declaration `x` here
+   |
+LL ~     
+LL |     let y = SignificantDrop;
+LL ~     let x = 1;
    |
-LL |     let x = 1;
-   |     ~~~~~
 
 error: unneeded late initialization
   --> tests/ui/needless_late_init.rs:92:5
@@ -210,10 +195,12 @@ LL |     let y = 1;
 LL |     x = SignificantDrop;
    |     ^^^^^^^^^^^^^^^^^^^ initialised here
    |
-help: declare `x` here
+help: move the declaration `x` here
+   |
+LL ~     
+LL |     let y = 1;
+LL ~     let x = SignificantDrop;
    |
-LL |     let x = SignificantDrop;
-   |     ~~~~~
 
 error: unneeded late initialization
   --> tests/ui/needless_late_init.rs:96:5
@@ -224,10 +211,14 @@ LL |     let x;
 LL |     x = SignificantDrop;
    |     ^^^^^^^^^^^^^^^^^^^ initialised here
    |
-help: declare `x` here
+help: move the declaration `x` here
+   |
+LL ~     
+LL |     // types that should be considered insignificant
+ ...
+LL |     let y = Box::new(4);
+LL ~     let x = SignificantDrop;
    |
-LL |     let x = SignificantDrop;
-   |     ~~~~~
 
 error: unneeded late initialization
   --> tests/ui/needless_late_init.rs:115:5
@@ -235,20 +226,17 @@ error: unneeded late initialization
 LL |     let a;
    |     ^^^^^^
    |
-help: declare `a` here
-   |
-LL |     let a = match n {
-   |     +++++++
-help: remove the assignments from the `match` arms
+help: move the declaration `a` here and remove the assignments from the `match` arms
    |
+LL ~     
+LL |     let n = 1;
+LL ~     let a = match n {
 LL ~         1 => f().await,
 LL |         _ => {
 LL ~             "two"
+LL |         },
+LL ~     };
    |
-help: add a semicolon after the `match` expression
-   |
-LL |     };
-   |      +
 
 error: unneeded late initialization
   --> tests/ui/needless_late_init.rs:132:5
@@ -256,20 +244,17 @@ error: unneeded late initialization
 LL |     let a;
    |     ^^^^^^
    |
-help: declare `a` here
-   |
-LL |     let a = match n {
-   |     +++++++
-help: remove the assignments from the `match` arms
+help: move the declaration `a` here and remove the assignments from the `match` arms
    |
+LL ~     
+LL |     let n = 1;
+LL ~     let a = match n {
 LL ~         1 => f(),
 LL |         _ => {
 LL ~             "two"
+LL |         },
+LL ~     };
    |
-help: add a semicolon after the `match` expression
-   |
-LL |     };
-   |      +
 
 error: aborting due to 16 previous errors
 
diff --git a/src/tools/clippy/tests/ui/no_effect.rs b/src/tools/clippy/tests/ui/no_effect.rs
index dabeda72f0c..0ea911c3434 100644
--- a/src/tools/clippy/tests/ui/no_effect.rs
+++ b/src/tools/clippy/tests/ui/no_effect.rs
@@ -1,6 +1,5 @@
 #![feature(fn_traits, unboxed_closures)]
 #![warn(clippy::no_effect_underscore_binding)]
-#![allow(dead_code, path_statements)]
 #![allow(
     clippy::deref_addrof,
     clippy::redundant_field_names,
@@ -33,7 +32,6 @@ impl Neg for Cout {
     }
 }
 
-struct Unit;
 struct Tuple(i32);
 struct Struct {
     field: i32,
@@ -42,10 +40,6 @@ enum Enum {
     Tuple(i32),
     Struct { field: i32 },
 }
-struct DropUnit;
-impl Drop for DropUnit {
-    fn drop(&mut self) {}
-}
 struct DropStruct {
     field: i32,
 }
@@ -117,15 +111,9 @@ impl FnOnce<(&str,)> for GreetStruct3 {
 
 fn main() {
     let s = get_struct();
-    let s2 = get_struct();
 
     0;
     //~^ ERROR: statement with no effect
-    //~| NOTE: `-D clippy::no-effect` implied by `-D warnings`
-    s2;
-    //~^ ERROR: statement with no effect
-    Unit;
-    //~^ ERROR: statement with no effect
     Tuple(0);
     //~^ ERROR: statement with no effect
     Struct { field: 0 };
@@ -192,7 +180,6 @@ fn main() {
     unsafe { unsafe_fn() };
     let _used = get_struct();
     let _x = vec![1];
-    DropUnit;
     DropStruct { field: 0 };
     DropTuple(0);
     DropEnum::Tuple(0);
diff --git a/src/tools/clippy/tests/ui/no_effect.stderr b/src/tools/clippy/tests/ui/no_effect.stderr
index c7c8eecd054..48ec997d938 100644
--- a/src/tools/clippy/tests/ui/no_effect.stderr
+++ b/src/tools/clippy/tests/ui/no_effect.stderr
@@ -1,5 +1,5 @@
 error: statement with no effect
-  --> tests/ui/no_effect.rs:122:5
+  --> tests/ui/no_effect.rs:115:5
    |
 LL |     0;
    |     ^^
@@ -8,151 +8,139 @@ LL |     0;
    = help: to override `-D warnings` add `#[allow(clippy::no_effect)]`
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:125:5
-   |
-LL |     s2;
-   |     ^^^
-
-error: statement with no effect
-  --> tests/ui/no_effect.rs:127:5
-   |
-LL |     Unit;
-   |     ^^^^^
-
-error: statement with no effect
-  --> tests/ui/no_effect.rs:129:5
+  --> tests/ui/no_effect.rs:117:5
    |
 LL |     Tuple(0);
    |     ^^^^^^^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:131:5
+  --> tests/ui/no_effect.rs:119:5
    |
 LL |     Struct { field: 0 };
    |     ^^^^^^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:133:5
+  --> tests/ui/no_effect.rs:121:5
    |
 LL |     Struct { ..s };
    |     ^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:135:5
+  --> tests/ui/no_effect.rs:123:5
    |
 LL |     Union { a: 0 };
    |     ^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:137:5
+  --> tests/ui/no_effect.rs:125:5
    |
 LL |     Enum::Tuple(0);
    |     ^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:139:5
+  --> tests/ui/no_effect.rs:127:5
    |
 LL |     Enum::Struct { field: 0 };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:141:5
+  --> tests/ui/no_effect.rs:129:5
    |
 LL |     5 + 6;
    |     ^^^^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:143:5
+  --> tests/ui/no_effect.rs:131:5
    |
 LL |     *&42;
    |     ^^^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:145:5
+  --> tests/ui/no_effect.rs:133:5
    |
 LL |     &6;
    |     ^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:147:5
+  --> tests/ui/no_effect.rs:135:5
    |
 LL |     (5, 6, 7);
    |     ^^^^^^^^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:149:5
+  --> tests/ui/no_effect.rs:137:5
    |
 LL |     ..;
    |     ^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:151:5
+  --> tests/ui/no_effect.rs:139:5
    |
 LL |     5..;
    |     ^^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:153:5
+  --> tests/ui/no_effect.rs:141:5
    |
 LL |     ..5;
    |     ^^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:155:5
+  --> tests/ui/no_effect.rs:143:5
    |
 LL |     5..6;
    |     ^^^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:157:5
+  --> tests/ui/no_effect.rs:145:5
    |
 LL |     5..=6;
    |     ^^^^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:159:5
+  --> tests/ui/no_effect.rs:147:5
    |
 LL |     [42, 55];
    |     ^^^^^^^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:161:5
+  --> tests/ui/no_effect.rs:149:5
    |
 LL |     [42, 55][1];
    |     ^^^^^^^^^^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:163:5
+  --> tests/ui/no_effect.rs:151:5
    |
 LL |     (42, 55).1;
    |     ^^^^^^^^^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:165:5
+  --> tests/ui/no_effect.rs:153:5
    |
 LL |     [42; 55];
    |     ^^^^^^^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:167:5
+  --> tests/ui/no_effect.rs:155:5
    |
 LL |     [42; 55][13];
    |     ^^^^^^^^^^^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:170:5
+  --> tests/ui/no_effect.rs:158:5
    |
 LL |     || x += 5;
    |     ^^^^^^^^^^
 
 error: statement with no effect
-  --> tests/ui/no_effect.rs:173:5
+  --> tests/ui/no_effect.rs:161:5
    |
 LL |     FooString { s: s };
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: binding to `_` prefixed variable with no side-effect
-  --> tests/ui/no_effect.rs:175:9
+  --> tests/ui/no_effect.rs:163:9
    |
 LL |     let _unused = 1;
    |         ^^^^^^^
@@ -161,22 +149,22 @@ LL |     let _unused = 1;
    = help: to override `-D warnings` add `#[allow(clippy::no_effect_underscore_binding)]`
 
 error: binding to `_` prefixed variable with no side-effect
-  --> tests/ui/no_effect.rs:178:9
+  --> tests/ui/no_effect.rs:166:9
    |
 LL |     let _penguin = || println!("Some helpful closure");
    |         ^^^^^^^^
 
 error: binding to `_` prefixed variable with no side-effect
-  --> tests/ui/no_effect.rs:180:9
+  --> tests/ui/no_effect.rs:168:9
    |
 LL |     let _duck = Struct { field: 0 };
    |         ^^^^^
 
 error: binding to `_` prefixed variable with no side-effect
-  --> tests/ui/no_effect.rs:182:9
+  --> tests/ui/no_effect.rs:170:9
    |
 LL |     let _cat = [2, 4, 6, 8][2];
    |         ^^^^
 
-error: aborting due to 29 previous errors
+error: aborting due to 27 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 0305d895fc5..7fc89bb9538 100644
--- a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs
+++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs
@@ -675,4 +675,60 @@ fn should_not_trigger_on_significant_iterator_drop() {
     }
 }
 
+// https://github.com/rust-lang/rust-clippy/issues/9072
+fn should_not_trigger_lint_if_place_expr_has_significant_drop() {
+    let x = Mutex::new(vec![1, 2, 3]);
+    let x_guard = x.lock().unwrap();
+
+    match x_guard[0] {
+        1 => println!("1!"),
+        x => println!("{x}"),
+    }
+
+    match x_guard.len() {
+        1 => println!("1!"),
+        x => println!("{x}"),
+    }
+}
+
+struct Guard<'a, T>(MutexGuard<'a, T>);
+
+struct Ref<'a, T>(&'a T);
+
+impl<'a, T> Guard<'a, T> {
+    fn guard(&self) -> &MutexGuard<T> {
+        &self.0
+    }
+
+    fn guard_ref(&self) -> Ref<MutexGuard<T>> {
+        Ref(&self.0)
+    }
+
+    fn take(self) -> Box<MutexGuard<'a, T>> {
+        Box::new(self.0)
+    }
+}
+
+fn should_not_trigger_for_significant_drop_ref() {
+    let mutex = Mutex::new(vec![1, 2]);
+    let guard = Guard(mutex.lock().unwrap());
+
+    match guard.guard().len() {
+        0 => println!("empty"),
+        _ => println!("not empty"),
+    }
+
+    match guard.guard_ref().0.len() {
+        0 => println!("empty"),
+        _ => println!("not empty"),
+    }
+
+    match guard.take().len() {
+        //~^ ERROR: temporary with significant `Drop` in `match` scrutinee will live until the
+        //~| NOTE: this might lead to deadlocks or other unexpected behavior
+        0 => println!("empty"),
+        _ => println!("not empty"),
+    };
+}
+
 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 7d5b1acc7f0..811bb065527 100644
--- a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr
+++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr
@@ -28,6 +28,9 @@ LL |     match s.lock_m().get_the_value() {
 LL |             println!("{}", s.lock_m().get_the_value());
    |                            ---------- another value with significant `Drop` created here
 ...
+LL |             println!("{}", s.lock_m().get_the_value());
+   |                            ---------- another value with significant `Drop` created here
+...
 LL |     }
    |      - temporary lives until here
    |
@@ -47,6 +50,9 @@ LL |     match s.lock_m_m().get_the_value() {
 LL |             println!("{}", s.lock_m().get_the_value());
    |                            ---------- another value with significant `Drop` created here
 ...
+LL |             println!("{}", s.lock_m().get_the_value());
+   |                            ---------- another value with significant `Drop` created here
+...
 LL |     }
    |      - temporary lives until here
    |
@@ -360,7 +366,7 @@ LL |     match s.lock().deref().deref() {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^
 ...
 LL |         _ => println!("Value is {}", s.lock().deref()),
-   |                                      ---------------- another value with significant `Drop` created here
+   |                                      -------- another value with significant `Drop` created here
 LL |     };
    |      - temporary lives until here
    |
@@ -378,7 +384,7 @@ LL |     match s.lock().deref().deref() {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^
 ...
 LL |         matcher => println!("Value is {}", s.lock().deref()),
-   |                                            ---------------- another value with significant `Drop` created here
+   |                                            -------- another value with significant `Drop` created here
 LL |         _ => println!("Value was not a match"),
 LL |     };
    |      - temporary lives until here
@@ -499,5 +505,21 @@ LL ~     let value = mutex.lock().unwrap().foo();
 LL ~     match value {
    |
 
-error: aborting due to 26 previous errors
+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
+   |
+LL |     match guard.take().len() {
+   |           ^^^^^^^^^^^^^^^^^^
+...
+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 = guard.take().len();
+LL ~     match value {
+   |
+
+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 ad0e5fab029..2c582c90ba8 100644
--- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.fixed
@@ -21,6 +21,8 @@ fn main() {
     let _ = check_files_ref_mut(&[(FileType::Account, path)]);
     let _ = check_files_self_and_arg(&[(FileType::Account, path)]);
     let _ = check_files_mut_path_buf(&[(FileType::Account, std::path::PathBuf::new())]);
+
+    check_mut_iteratee_and_modify_inner_variable();
 }
 
 // `check_files` and its variants are based on:
@@ -138,3 +140,33 @@ fn check_files_mut_path_buf(files: &[(FileType, std::path::PathBuf)]) -> bool {
 fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
     Ok(std::path::PathBuf::new())
 }
+
+// Issue 12098
+// https://github.com/rust-lang/rust-clippy/issues/12098
+// no message emits
+fn check_mut_iteratee_and_modify_inner_variable() {
+    struct Test {
+        list: Vec<String>,
+        mut_this: bool,
+    }
+
+    impl Test {
+        fn list(&self) -> &[String] {
+            &self.list
+        }
+    }
+
+    let mut test = Test {
+        list: vec![String::from("foo"), String::from("bar")],
+        mut_this: false,
+    };
+
+    for _item in test.list().to_vec() {
+        println!("{}", _item);
+
+        test.mut_this = true;
+        {
+            test.mut_this = true;
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs
index d3d59c4c70f..a28ccd1efef 100644
--- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.rs
@@ -21,6 +21,8 @@ fn main() {
     let _ = check_files_ref_mut(&[(FileType::Account, path)]);
     let _ = check_files_self_and_arg(&[(FileType::Account, path)]);
     let _ = check_files_mut_path_buf(&[(FileType::Account, std::path::PathBuf::new())]);
+
+    check_mut_iteratee_and_modify_inner_variable();
 }
 
 // `check_files` and its variants are based on:
@@ -138,3 +140,33 @@ fn check_files_mut_path_buf(files: &[(FileType, std::path::PathBuf)]) -> bool {
 fn get_file_path(_file_type: &FileType) -> Result<std::path::PathBuf, std::io::Error> {
     Ok(std::path::PathBuf::new())
 }
+
+// Issue 12098
+// https://github.com/rust-lang/rust-clippy/issues/12098
+// no message emits
+fn check_mut_iteratee_and_modify_inner_variable() {
+    struct Test {
+        list: Vec<String>,
+        mut_this: bool,
+    }
+
+    impl Test {
+        fn list(&self) -> &[String] {
+            &self.list
+        }
+    }
+
+    let mut test = Test {
+        list: vec![String::from("foo"), String::from("bar")],
+        mut_this: false,
+    };
+
+    for _item in test.list().to_vec() {
+        println!("{}", _item);
+
+        test.mut_this = true;
+        {
+            test.mut_this = true;
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr
index 9d3591e0dbf..fb98cfddc26 100644
--- a/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_iter_cloned.stderr
@@ -1,5 +1,5 @@
 error: unnecessary use of `copied`
-  --> tests/ui/unnecessary_iter_cloned.rs:29:22
+  --> tests/ui/unnecessary_iter_cloned.rs:31:22
    |
 LL |     for (t, path) in files.iter().copied() {
    |                      ^^^^^^^^^^^^^^^^^^^^^
@@ -17,7 +17,7 @@ LL +         let other = match get_file_path(t) {
    |
 
 error: unnecessary use of `copied`
-  --> tests/ui/unnecessary_iter_cloned.rs:44:22
+  --> tests/ui/unnecessary_iter_cloned.rs:46:22
    |
 LL |     for (t, path) in files.iter().copied() {
    |                      ^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/useless_attribute.fixed b/src/tools/clippy/tests/ui/useless_attribute.fixed
index 81759086f79..231fc0a892a 100644
--- a/src/tools/clippy/tests/ui/useless_attribute.fixed
+++ b/src/tools/clippy/tests/ui/useless_attribute.fixed
@@ -86,8 +86,51 @@ mod module {
 
 #[rustfmt::skip]
 #[allow(unused_import_braces)]
+#[allow(unused_braces)]
 use module::{Struct};
 
 fn main() {
     test_indented_attr();
 }
+
+// Regression test for https://github.com/rust-lang/rust-clippy/issues/4467
+#[allow(dead_code)]
+use std::collections as puppy_doggy;
+
+// Regression test for https://github.com/rust-lang/rust-clippy/issues/11595
+pub mod hidden_glob_reexports {
+    #![allow(unreachable_pub)]
+
+    mod my_prelude {
+        pub struct MyCoolTypeInternal;
+        pub use MyCoolTypeInternal as MyCoolType;
+    }
+
+    mod my_uncool_type {
+        pub(crate) struct MyUncoolType;
+    }
+
+    // This exports `MyCoolType`.
+    pub use my_prelude::*;
+
+    // This hides `my_prelude::MyCoolType`.
+    #[allow(hidden_glob_reexports)]
+    use my_uncool_type::MyUncoolType as MyCoolType;
+}
+
+// Regression test for https://github.com/rust-lang/rust-clippy/issues/10878
+pub mod ambiguous_glob_exports {
+    #![allow(unreachable_pub)]
+
+    mod my_prelude {
+        pub struct MyType;
+    }
+
+    mod my_type {
+        pub struct MyType;
+    }
+
+    #[allow(ambiguous_glob_reexports)]
+    pub use my_prelude::*;
+    pub use my_type::*;
+}
diff --git a/src/tools/clippy/tests/ui/useless_attribute.rs b/src/tools/clippy/tests/ui/useless_attribute.rs
index 59a9dcf093b..8dfcd2110a4 100644
--- a/src/tools/clippy/tests/ui/useless_attribute.rs
+++ b/src/tools/clippy/tests/ui/useless_attribute.rs
@@ -86,8 +86,51 @@ mod module {
 
 #[rustfmt::skip]
 #[allow(unused_import_braces)]
+#[allow(unused_braces)]
 use module::{Struct};
 
 fn main() {
     test_indented_attr();
 }
+
+// Regression test for https://github.com/rust-lang/rust-clippy/issues/4467
+#[allow(dead_code)]
+use std::collections as puppy_doggy;
+
+// Regression test for https://github.com/rust-lang/rust-clippy/issues/11595
+pub mod hidden_glob_reexports {
+    #![allow(unreachable_pub)]
+
+    mod my_prelude {
+        pub struct MyCoolTypeInternal;
+        pub use MyCoolTypeInternal as MyCoolType;
+    }
+
+    mod my_uncool_type {
+        pub(crate) struct MyUncoolType;
+    }
+
+    // This exports `MyCoolType`.
+    pub use my_prelude::*;
+
+    // This hides `my_prelude::MyCoolType`.
+    #[allow(hidden_glob_reexports)]
+    use my_uncool_type::MyUncoolType as MyCoolType;
+}
+
+// Regression test for https://github.com/rust-lang/rust-clippy/issues/10878
+pub mod ambiguous_glob_exports {
+    #![allow(unreachable_pub)]
+
+    mod my_prelude {
+        pub struct MyType;
+    }
+
+    mod my_type {
+        pub struct MyType;
+    }
+
+    #[allow(ambiguous_glob_reexports)]
+    pub use my_prelude::*;
+    pub use my_type::*;
+}
diff --git a/src/tools/clippy/tests/ui/while_float.rs b/src/tools/clippy/tests/ui/while_float.rs
new file mode 100644
index 00000000000..a3b0618948e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/while_float.rs
@@ -0,0 +1,14 @@
+#[deny(clippy::while_float)]
+fn main() {
+    let mut x = 0.0_f32;
+    while x < 42.0_f32 {
+        x += 0.5;
+    }
+    while x < 42.0 {
+        x += 1.0;
+    }
+    let mut x = 0;
+    while x < 42 {
+        x += 1;
+    }
+}
diff --git a/src/tools/clippy/tests/ui/while_float.stderr b/src/tools/clippy/tests/ui/while_float.stderr
new file mode 100644
index 00000000000..b8e934b97c6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/while_float.stderr
@@ -0,0 +1,20 @@
+error: while condition comparing floats
+  --> tests/ui/while_float.rs:4:11
+   |
+LL |     while x < 42.0_f32 {
+   |           ^^^^^^^^^^^^
+   |
+note: the lint level is defined here
+  --> tests/ui/while_float.rs:1:8
+   |
+LL | #[deny(clippy::while_float)]
+   |        ^^^^^^^^^^^^^^^^^^^
+
+error: while condition comparing floats
+  --> tests/ui/while_float.rs:7:11
+   |
+LL |     while x < 42.0 {
+   |           ^^^^^^^^
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js
index c63edd5bf70..7fd779fe9a4 100644
--- a/src/tools/clippy/util/gh-pages/script.js
+++ b/src/tools/clippy/util/gh-pages/script.js
@@ -406,7 +406,7 @@
                 }
 
                 // Search by id
-                if (lint.id.indexOf(searchStr.replace("-", "_")) !== -1) {
+                if (lint.id.indexOf(searchStr.replaceAll("-", "_")) !== -1) {
                     return true;
                 }