about summary refs log tree commit diff
path: root/src/tools
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2022-07-01 01:30:37 +0000
committerbors <bors@rust-lang.org>2022-07-01 01:30:37 +0000
commitacdcdfb61b7b472bfacbb8bb889bdf3204827f2e (patch)
tree40fdadc3be0416e0ad45ecc7fc32d8f98b477f55 /src/tools
parent7425fb293f510a6f138e82a963a3bc599a5b9e1c (diff)
parent5fead7a0f00a3851e2a23a63f0ef685cc3744ffd (diff)
downloadrust-acdcdfb61b7b472bfacbb8bb889bdf3204827f2e.tar.gz
rust-acdcdfb61b7b472bfacbb8bb889bdf3204827f2e.zip
Auto merge of #98706 - flip1995:clippyup, r=Dylan-DPC
Update Clippy

r? `@Manishearth`
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/clippy/.github/workflows/remark.yml2
-rw-r--r--src/tools/clippy/CHANGELOG.md5
-rw-r--r--src/tools/clippy/Cargo.toml3
-rw-r--r--src/tools/clippy/README.md10
-rw-r--r--src/tools/clippy/book/src/README.md2
-rw-r--r--src/tools/clippy/clippy_dev/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_dev/src/main.rs18
-rw-r--r--src/tools/clippy/clippy_dev/src/new_lint.rs2
-rw-r--r--src/tools/clippy/clippy_dev/src/update_lints.rs395
-rw-r--r--src/tools/clippy/clippy_lints/Cargo.toml5
-rw-r--r--src/tools/clippy/clippy_lints/src/assign_ops.rs235
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/booleans.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs68
-rw-r--r--src/tools/clippy/clippy_lints/src/deprecated_lints.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs697
-rw-r--r--src/tools/clippy/clippy_lints/src/double_comparison.rs96
-rw-r--r--src/tools/clippy/clippy_lints/src/duration_subsec.rs75
-rw-r--r--src/tools/clippy/clippy_lints/src/enum_variants.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/eq_op.rs319
-rw-r--r--src/tools/clippy/clippy_lints/src/erasing_op.rs77
-rw-r--r--src/tools/clippy/clippy_lints/src/escape.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs116
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_return.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/integer_division.rs61
-rw-r--r--src/tools/clippy/clippy_lints/src/large_const_arrays.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/let_underscore.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_all.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_complexity.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_correctness.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_internal.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_lints.rs57
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_perf.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_restriction.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_style.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs95
-rw-r--r--src/tools/clippy/clippy_lints/src/lifetimes.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/manual_find.rs158
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/mod.rs43
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs106
-rw-r--r--src/tools/clippy/clippy_lints/src/macro_use.rs42
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs123
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_retain.rs228
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_same_arms.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/mod.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs88
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs162
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/single_match.rs85
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_flatten.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/misc.rs483
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs85
-rw-r--r--src/tools/clippy/clippy_lints/src/neg_multiply.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/non_copy_const.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/non_expressive_names.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/numeric_arithmetic.rs170
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs (renamed from src/tools/clippy/clippy_lints/src/absurd_extreme_comparisons.rs)103
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs101
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/bit_mask.rs (renamed from src/tools/clippy/clippy_lints/src/bit_mask.rs)168
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs147
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/double_comparison.rs54
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs44
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/eq_op.rs45
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/erasing_op.rs53
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/float_cmp.rs139
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs71
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/identity_op.rs (renamed from src/tools/clippy/clippy_lints/src/identity_op.rs)87
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/integer_division.rs27
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs84
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/mod.rs849
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs (renamed from src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs)74
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/modulo_one.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/needless_bitwise_bool.rs36
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs127
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/op_ref.rs218
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs65
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/self_assignment.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/verbose_bit_mask.rs44
-rw-r--r--src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs104
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr.rs50
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr_eq.rs97
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/returns.rs71
-rw-r--r--src/tools/clippy/clippy_lints/src/self_assignment.rs56
-rw-r--r--src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs39
-rw-r--r--src/tools/clippy/clippy_lints/src/strings.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/mod.rs25
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs56
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/utils.rs28
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_async.rs28
-rw-r--r--src/tools/clippy/clippy_lints/src/unwrap.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints.rs107
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs5
-rw-r--r--src/tools/clippy/clippy_utils/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs44
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs69
-rw-r--r--src/tools/clippy/clippy_utils/src/msrvs.rs9
-rw-r--r--src/tools/clippy/clippy_utils/src/paths.rs13
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs266
-rw-r--r--src/tools/clippy/clippy_utils/src/visitors.rs131
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/src/driver.rs10
-rw-r--r--src/tools/clippy/tests/compile-test.rs23
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/Cargo.toml8
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.rs11
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr16
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/Cargo.toml8
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.rs11
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr14
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/Cargo.toml8
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.rs11
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr14
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/Cargo.toml7
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.rs11
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr14
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.toml8
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.rs16
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr14
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/Cargo.toml8
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/src/main.rs11
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_cargo/Cargo.toml8
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_cargo/src/main.rs11
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/Cargo.toml7
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/src/main.rs11
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_file_attr/Cargo.toml8
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_file_attr/src/main.rs13
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/Cargo.toml8
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/src/main.rs11
-rw-r--r--src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/src/main.stderr4
-rw-r--r--src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr4
-rw-r--r--src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed7
-rw-r--r--src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs7
-rw-r--r--src/tools/clippy/tests/ui-internal/default_deprecation_reason.rs30
-rw-r--r--src/tools/clippy/tests/ui-internal/default_deprecation_reason.stderr22
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/macro_rules.rs8
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs14
-rw-r--r--src/tools/clippy/tests/ui/boxed_local.rs (renamed from src/tools/clippy/tests/ui/escape_analysis.rs)5
-rw-r--r--src/tools/clippy/tests/ui/boxed_local.stderr (renamed from src/tools/clippy/tests/ui/escape_analysis.stderr)8
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs16
-rw-r--r--src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr38
-rw-r--r--src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs5
-rw-r--r--src/tools/clippy/tests/ui/default_instead_of_iter_empty.fixed21
-rw-r--r--src/tools/clippy/tests/ui/default_instead_of_iter_empty.rs21
-rw-r--r--src/tools/clippy/tests/ui/default_instead_of_iter_empty.stderr22
-rw-r--r--src/tools/clippy/tests/ui/enum_variants.rs21
-rw-r--r--src/tools/clippy/tests/ui/enum_variants.stderr27
-rw-r--r--src/tools/clippy/tests/ui/explicit_auto_deref.fixed214
-rw-r--r--src/tools/clippy/tests/ui/explicit_auto_deref.rs214
-rw-r--r--src/tools/clippy/tests/ui/explicit_auto_deref.stderr196
-rw-r--r--src/tools/clippy/tests/ui/explicit_deref_methods.fixed3
-rw-r--r--src/tools/clippy/tests/ui/explicit_deref_methods.rs3
-rw-r--r--src/tools/clippy/tests/ui/explicit_deref_methods.stderr24
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_lifetimes.rs12
-rw-r--r--src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr12
-rw-r--r--src/tools/clippy/tests/ui/if_same_then_else.rs61
-rw-r--r--src/tools/clippy/tests/ui/if_same_then_else.stderr20
-rw-r--r--src/tools/clippy/tests/ui/implicit_return.fixed12
-rw-r--r--src/tools/clippy/tests/ui/implicit_return.rs12
-rw-r--r--src/tools/clippy/tests/ui/let_underscore_lock.rs9
-rw-r--r--src/tools/clippy/tests/ui/let_underscore_lock.stderr8
-rw-r--r--src/tools/clippy/tests/ui/logic_bug.rs8
-rw-r--r--src/tools/clippy/tests/ui/logic_bug.stderr20
-rw-r--r--src/tools/clippy/tests/ui/macro_use_imports.fixed1
-rw-r--r--src/tools/clippy/tests/ui/macro_use_imports.rs1
-rw-r--r--src/tools/clippy/tests/ui/macro_use_imports.stderr14
-rw-r--r--src/tools/clippy/tests/ui/macro_use_imports_expect.rs51
-rw-r--r--src/tools/clippy/tests/ui/manual_find.rs22
-rw-r--r--src/tools/clippy/tests/ui/manual_find.stderr29
-rw-r--r--src/tools/clippy/tests/ui/manual_find_fixable.fixed182
-rw-r--r--src/tools/clippy/tests/ui/manual_find_fixable.rs242
-rw-r--r--src/tools/clippy/tests/ui/manual_find_fixable.stderr142
-rw-r--r--src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs9
-rw-r--r--src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr8
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.fixed55
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.rs55
-rw-r--r--src/tools/clippy/tests/ui/manual_rem_euclid.stderr57
-rw-r--r--src/tools/clippy/tests/ui/manual_retain.fixed240
-rw-r--r--src/tools/clippy/tests/ui/manual_retain.rs246
-rw-r--r--src/tools/clippy/tests/ui/manual_retain.stderr124
-rw-r--r--src/tools/clippy/tests/ui/methods.rs1
-rw-r--r--src/tools/clippy/tests/ui/methods.stderr4
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_attr.rs15
-rw-r--r--src/tools/clippy/tests/ui/min_rust_version_attr.stderr8
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.fixed43
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.rs43
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.stderr30
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow_pat.rs2
-rw-r--r--src/tools/clippy/tests/ui/needless_return.fixed36
-rw-r--r--src/tools/clippy/tests/ui/needless_return.rs36
-rw-r--r--src/tools/clippy/tests/ui/needless_return.stderr74
-rw-r--r--src/tools/clippy/tests/ui/neg_multiply.fixed3
-rw-r--r--src/tools/clippy/tests/ui/neg_multiply.rs3
-rw-r--r--src/tools/clippy/tests/ui/neg_multiply.stderr14
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool.rs7
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool.stderr24
-rw-r--r--src/tools/clippy/tests/ui/ptr_arg.rs6
-rw-r--r--src/tools/clippy/tests/ui/ptr_arg.stderr40
-rw-r--r--src/tools/clippy/tests/ui/redundant_clone.fixed5
-rw-r--r--src/tools/clippy/tests/ui/redundant_clone.rs5
-rw-r--r--src/tools/clippy/tests/ui/redundant_clone.stderr60
-rw-r--r--src/tools/clippy/tests/ui/ref_binding_to_reference.rs2
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed2
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_none.rs2
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed2
-rw-r--r--src/tools/clippy/tests/ui/search_is_some_fixable_some.rs2
-rw-r--r--src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs3
-rw-r--r--src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs29
-rw-r--r--src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr331
-rw-r--r--src/tools/clippy/tests/ui/single_match.stderr11
-rw-r--r--src/tools/clippy/tests/ui/single_match_else.rs19
-rw-r--r--src/tools/clippy/tests/ui/single_match_else.stderr82
-rw-r--r--src/tools/clippy/tests/ui/slow_vector_initialization.rs6
-rw-r--r--src/tools/clippy/tests/ui/slow_vector_initialization.stderr28
-rw-r--r--src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed78
-rw-r--r--src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs39
-rw-r--r--src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr102
-rw-r--r--src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs38
-rw-r--r--src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr8
-rw-r--r--src/tools/clippy/tests/ui/unused_async.rs34
-rw-r--r--src/tools/clippy/tests/ui/unused_async.stderr14
-rw-r--r--src/tools/clippy/tests/ui/useless_asref.fixed1
-rw-r--r--src/tools/clippy/tests/ui/useless_asref.rs1
-rw-r--r--src/tools/clippy/tests/ui/useless_asref.stderr22
-rw-r--r--src/tools/clippy/tests/ui/while_let_loop.rs26
-rw-r--r--src/tools/clippy/tests/ui/while_let_on_iterator.fixed3
-rw-r--r--src/tools/clippy/tests/ui/while_let_on_iterator.rs3
-rw-r--r--src/tools/clippy/tests/ui/while_let_on_iterator.stderr46
-rw-r--r--src/tools/clippy/tests/ui/zero_div_zero.rs2
-rw-r--r--src/tools/clippy/tests/ui/zero_div_zero.stderr28
-rw-r--r--src/tools/clippy/util/gh-pages/index.html1
243 files changed, 9047 insertions, 3488 deletions
diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml
index ff471207b65..81ef072bbb0 100644
--- a/src/tools/clippy/.github/workflows/remark.yml
+++ b/src/tools/clippy/.github/workflows/remark.yml
@@ -21,7 +21,7 @@ jobs:
     - name: Setup Node.js
       uses: actions/setup-node@v1.4.4
       with:
-        node-version: '12.x'
+        node-version: '14.x'
 
     - name: Install remark
       run: npm install remark-cli remark-lint remark-lint-maximum-line-length remark-preset-lint-recommended remark-gfm
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 6aaf12ed932..9bc93c1cb42 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -3348,6 +3348,7 @@ Released 2018-09-13
 [`debug_assert_with_mut_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#debug_assert_with_mut_call
 [`decimal_literal_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#decimal_literal_representation
 [`declare_interior_mutable_const`]: https://rust-lang.github.io/rust-clippy/master/index.html#declare_interior_mutable_const
+[`default_instead_of_iter_empty`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_instead_of_iter_empty
 [`default_numeric_fallback`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_numeric_fallback
 [`default_trait_access`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_trait_access
 [`default_union_representation`]: https://rust-lang.github.io/rust-clippy/master/index.html#default_union_representation
@@ -3399,6 +3400,7 @@ Released 2018-09-13
 [`expect_fun_call`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_fun_call
 [`expect_used`]: https://rust-lang.github.io/rust-clippy/master/index.html#expect_used
 [`expl_impl_clone_on_copy`]: https://rust-lang.github.io/rust-clippy/master/index.html#expl_impl_clone_on_copy
+[`explicit_auto_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_auto_deref
 [`explicit_counter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_counter_loop
 [`explicit_deref_methods`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_deref_methods
 [`explicit_into_iter_loop`]: https://rust-lang.github.io/rust-clippy/master/index.html#explicit_into_iter_loop
@@ -3519,6 +3521,7 @@ Released 2018-09-13
 [`manual_async_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_async_fn
 [`manual_bits`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_bits
 [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
+[`manual_find`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find
 [`manual_find_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_find_map
 [`manual_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_flatten
 [`manual_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_map
@@ -3526,6 +3529,8 @@ Released 2018-09-13
 [`manual_non_exhaustive`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_non_exhaustive
 [`manual_ok_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_ok_or
 [`manual_range_contains`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_contains
+[`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid
+[`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain
 [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
 [`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
 [`manual_str_repeat`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_str_repeat
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index e4060ce29a7..644ca6318f6 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy"
-version = "0.1.63"
+version = "0.1.64"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
@@ -31,6 +31,7 @@ termize = "0.1"
 compiletest_rs = { version = "0.8", features = ["tmp"] }
 tester = "0.9"
 regex = "1.5"
+toml = "0.5"
 # This is used by the `collect-metadata` alias.
 filetime = "0.2"
 
diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md
index edbc626e354..2c3defeaa83 100644
--- a/src/tools/clippy/README.md
+++ b/src/tools/clippy/README.md
@@ -5,7 +5,7 @@
 
 A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
 
-[There are over 500 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
+[There are over 550 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
 
 Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
 You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
@@ -214,6 +214,14 @@ specifying the minimum supported Rust version (MSRV) in the clippy configuration
 msrv = "1.30.0"
 ```
 
+Alternatively, the [`rust-version` field](https://doc.rust-lang.org/cargo/reference/manifest.html#the-rust-version-field)
+in the `Cargo.toml` can be used.
+
+```toml
+# Cargo.toml
+rust-version = "1.30"
+```
+
 The MSRV can also be specified as an inner attribute, like below.
 
 ```rust
diff --git a/src/tools/clippy/book/src/README.md b/src/tools/clippy/book/src/README.md
index de1f70d7e96..d941f8b65e8 100644
--- a/src/tools/clippy/book/src/README.md
+++ b/src/tools/clippy/book/src/README.md
@@ -6,7 +6,7 @@
 A collection of lints to catch common mistakes and improve your
 [Rust](https://github.com/rust-lang/rust) code.
 
-[There are over 500 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
+[There are over 550 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
 
 Lints are divided into categories, each with a default [lint
 level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how
diff --git a/src/tools/clippy/clippy_dev/Cargo.toml b/src/tools/clippy/clippy_dev/Cargo.toml
index b0d470a2124..2ac3b4fe2ed 100644
--- a/src/tools/clippy/clippy_dev/Cargo.toml
+++ b/src/tools/clippy/clippy_dev/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2021"
 
 [dependencies]
 aho-corasick = "0.7"
-clap = "3.1"
+clap = "3.2"
 indoc = "1.0"
 itertools = "0.10.1"
 opener = "0.5"
diff --git a/src/tools/clippy/clippy_dev/src/main.rs b/src/tools/clippy/clippy_dev/src/main.rs
index 2c27a0bcaf9..243a901503f 100644
--- a/src/tools/clippy/clippy_dev/src/main.rs
+++ b/src/tools/clippy/clippy_dev/src/main.rs
@@ -5,6 +5,7 @@
 use clap::{Arg, ArgAction, ArgMatches, Command, PossibleValue};
 use clippy_dev::{bless, fmt, lint, new_lint, serve, setup, update_lints};
 use indoc::indoc;
+
 fn main() {
     let matches = get_clap_config();
 
@@ -85,6 +86,11 @@ fn main() {
             let uplift = matches.contains_id("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);
+        },
         _ => {},
     }
 }
@@ -266,6 +272,18 @@ fn get_clap_config() -> ArgMatches {
                     .long("uplift")
                     .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')
+                    .required(false)
+                    .takes_value(true)
+                    .help("The reason for deprecation"),
+            ]),
         ])
         .get_matches()
 }
diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs
index 748d73c0801..7d7e760ef44 100644
--- a/src/tools/clippy/clippy_dev/src/new_lint.rs
+++ b/src/tools/clippy/clippy_dev/src/new_lint.rs
@@ -138,7 +138,7 @@ fn to_camel_case(name: &str) -> String {
         .collect()
 }
 
-fn get_stabilization_version() -> String {
+pub(crate) fn get_stabilization_version() -> String {
     fn parse_manifest(contents: &str) -> Option<String> {
         let version = contents
             .lines()
diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs
index 1bbd9a45b61..2e0659f42d7 100644
--- a/src/tools/clippy/clippy_dev/src/update_lints.rs
+++ b/src/tools/clippy/clippy_dev/src/update_lints.rs
@@ -1,16 +1,17 @@
+use crate::clippy_project_root;
 use aho_corasick::AhoCorasickBuilder;
-use core::fmt::Write as _;
+use indoc::writedoc;
 use itertools::Itertools;
 use rustc_lexer::{tokenize, unescape, LiteralKind, TokenKind};
 use std::collections::{HashMap, HashSet};
 use std::ffi::OsStr;
-use std::fs;
-use std::io::{self, Read as _, Seek as _, Write as _};
+use std::fmt::Write;
+use std::fs::{self, OpenOptions};
+use std::io::{self, Read, Seek, SeekFrom, Write as _};
+use std::ops::Range;
 use std::path::{Path, PathBuf};
 use walkdir::{DirEntry, WalkDir};
 
-use crate::clippy_project_root;
-
 const GENERATED_FILE_COMMENT: &str = "// This file was generated by `cargo dev update_lints`.\n\
      // Use that command to update this file and do not edit by hand.\n\
      // Manual edits will be overwritten.\n\n";
@@ -326,6 +327,200 @@ pub fn rename(old_name: &str, new_name: &str, uplift: bool) {
     println!("note: `cargo uitest` still needs to be run to update the test results");
 }
 
+const DEFAULT_DEPRECATION_REASON: &str = "default deprecation note";
+/// Runs the `deprecate` command
+///
+/// This does the following:
+/// * Adds an entry to `deprecated_lints.rs`.
+/// * Removes the lint declaration (and the entire file if applicable)
+///
+/// # Panics
+///
+/// If a file path could not read from or written to
+pub fn deprecate(name: &str, reason: Option<&String>) {
+    fn finish(
+        (lints, mut deprecated_lints, renamed_lints): (Vec<Lint>, Vec<DeprecatedLint>, Vec<RenamedLint>),
+        name: &str,
+        reason: &str,
+    ) {
+        deprecated_lints.push(DeprecatedLint {
+            name: name.to_string(),
+            reason: reason.to_string(),
+            declaration_range: Range::default(),
+        });
+
+        generate_lint_files(UpdateMode::Change, &lints, &deprecated_lints, &renamed_lints);
+        println!("info: `{}` has successfully been deprecated", name);
+
+        if reason == DEFAULT_DEPRECATION_REASON {
+            println!("note: the deprecation reason must be updated in `clippy_lints/src/deprecated_lints.rs`");
+        }
+        println!("note: you must run `cargo uitest` to update the test results");
+    }
+
+    let reason = reason.map_or(DEFAULT_DEPRECATION_REASON, String::as_str);
+    let name_lower = name.to_lowercase();
+    let name_upper = name.to_uppercase();
+
+    let (mut lints, deprecated_lints, renamed_lints) = gather_all();
+    let Some(lint) = lints.iter().find(|l| l.name == name_lower) else { eprintln!("error: failed to find lint `{}`", name); return; };
+
+    let mod_path = {
+        let mut mod_path = PathBuf::from(format!("clippy_lints/src/{}", lint.module));
+        if mod_path.is_dir() {
+            mod_path = mod_path.join("mod");
+        }
+
+        mod_path.set_extension("rs");
+        mod_path
+    };
+
+    let deprecated_lints_path = &*clippy_project_root().join("clippy_lints/src/deprecated_lints.rs");
+
+    if remove_lint_declaration(&name_lower, &mod_path, &mut lints).unwrap_or(false) {
+        declare_deprecated(&name_upper, deprecated_lints_path, reason).unwrap();
+        finish((lints, deprecated_lints, renamed_lints), name, reason);
+        return;
+    }
+
+    eprintln!("error: lint not found");
+}
+
+fn remove_lint_declaration(name: &str, path: &Path, lints: &mut Vec<Lint>) -> io::Result<bool> {
+    fn remove_lint(name: &str, lints: &mut Vec<Lint>) {
+        lints.iter().position(|l| l.name == name).map(|pos| lints.remove(pos));
+    }
+
+    fn remove_test_assets(name: &str) {
+        let test_file_stem = format!("tests/ui/{}", name);
+        let path = Path::new(&test_file_stem);
+
+        // Some lints have their own directories, delete them
+        if path.is_dir() {
+            fs::remove_dir_all(path).ok();
+            return;
+        }
+
+        // Remove all related test files
+        fs::remove_file(path.with_extension("rs")).ok();
+        fs::remove_file(path.with_extension("stderr")).ok();
+        fs::remove_file(path.with_extension("fixed")).ok();
+    }
+
+    fn remove_impl_lint_pass(lint_name_upper: &str, content: &mut String) {
+        let impl_lint_pass_start = content.find("impl_lint_pass!").unwrap_or_else(|| {
+            content
+                .find("declare_lint_pass!")
+                .unwrap_or_else(|| panic!("failed to find `impl_lint_pass`"))
+        });
+        let mut impl_lint_pass_end = content[impl_lint_pass_start..]
+            .find(']')
+            .expect("failed to find `impl_lint_pass` terminator");
+
+        impl_lint_pass_end += impl_lint_pass_start;
+        if let Some(lint_name_pos) = content[impl_lint_pass_start..impl_lint_pass_end].find(&lint_name_upper) {
+            let mut lint_name_end = impl_lint_pass_start + (lint_name_pos + lint_name_upper.len());
+            for c in content[lint_name_end..impl_lint_pass_end].chars() {
+                // Remove trailing whitespace
+                if c == ',' || c.is_whitespace() {
+                    lint_name_end += 1;
+                } else {
+                    break;
+                }
+            }
+
+            content.replace_range(impl_lint_pass_start + lint_name_pos..lint_name_end, "");
+        }
+    }
+
+    if path.exists() {
+        if let Some(lint) = lints.iter().find(|l| l.name == name) {
+            if lint.module == name {
+                // The lint name is the same as the file, we can just delete the entire file
+                fs::remove_file(path)?;
+            } else {
+                // We can't delete the entire file, just remove the declaration
+
+                if let Some(Some("mod.rs")) = path.file_name().map(OsStr::to_str) {
+                    // Remove clippy_lints/src/some_mod/some_lint.rs
+                    let mut lint_mod_path = path.to_path_buf();
+                    lint_mod_path.set_file_name(name);
+                    lint_mod_path.set_extension("rs");
+
+                    fs::remove_file(lint_mod_path).ok();
+                }
+
+                let mut content =
+                    fs::read_to_string(&path).unwrap_or_else(|_| panic!("failed to read `{}`", path.to_string_lossy()));
+
+                eprintln!(
+                    "warn: you will have to manually remove any code related to `{}` from `{}`",
+                    name,
+                    path.display()
+                );
+
+                assert!(
+                    content[lint.declaration_range.clone()].contains(&name.to_uppercase()),
+                    "error: `{}` does not contain lint `{}`'s declaration",
+                    path.display(),
+                    lint.name
+                );
+
+                // Remove lint declaration (declare_clippy_lint!)
+                content.replace_range(lint.declaration_range.clone(), "");
+
+                // Remove the module declaration (mod xyz;)
+                let mod_decl = format!("\nmod {};", name);
+                content = content.replacen(&mod_decl, "", 1);
+
+                remove_impl_lint_pass(&lint.name.to_uppercase(), &mut content);
+                fs::write(path, content).unwrap_or_else(|_| panic!("failed to write to `{}`", path.to_string_lossy()));
+            }
+
+            remove_test_assets(name);
+            remove_lint(name, lints);
+            return Ok(true);
+        }
+    }
+
+    Ok(false)
+}
+
+fn declare_deprecated(name: &str, path: &Path, reason: &str) -> io::Result<()> {
+    let mut file = OpenOptions::new().write(true).open(path)?;
+
+    file.seek(SeekFrom::End(0))?;
+
+    let version = crate::new_lint::get_stabilization_version();
+    let deprecation_reason = if reason == DEFAULT_DEPRECATION_REASON {
+        "TODO"
+    } else {
+        reason
+    };
+
+    writedoc!(
+        file,
+        "
+
+        declare_deprecated_lint! {{
+            /// ### What it does
+            /// Nothing. This lint has been deprecated.
+            ///
+            /// ### Deprecation reason
+            /// {}
+            #[clippy::version = \"{}\"]
+            pub {},
+            \"{}\"
+        }}
+
+        ",
+        deprecation_reason,
+        version,
+        name,
+        reason,
+    )
+}
+
 /// Replace substrings if they aren't bordered by identifier characters. Returns `None` if there
 /// were no replacements.
 fn replace_ident_like(contents: &str, replacements: &[(&str, &str)]) -> Option<String> {
@@ -393,16 +588,18 @@ struct Lint {
     group: String,
     desc: String,
     module: String,
+    declaration_range: Range<usize>,
 }
 
 impl Lint {
     #[must_use]
-    fn new(name: &str, group: &str, desc: &str, module: &str) -> Self {
+    fn new(name: &str, group: &str, desc: &str, module: &str, declaration_range: Range<usize>) -> Self {
         Self {
             name: name.to_lowercase(),
             group: group.into(),
             desc: remove_line_splices(desc),
             module: module.into(),
+            declaration_range,
         }
     }
 
@@ -433,12 +630,14 @@ impl Lint {
 struct DeprecatedLint {
     name: String,
     reason: String,
+    declaration_range: Range<usize>,
 }
 impl DeprecatedLint {
-    fn new(name: &str, reason: &str) -> Self {
+    fn new(name: &str, reason: &str, declaration_range: Range<usize>) -> Self {
         Self {
             name: name.to_lowercase(),
             reason: remove_line_splices(reason),
+            declaration_range,
         }
     }
 }
@@ -610,7 +809,11 @@ fn clippy_lints_src_files() -> impl Iterator<Item = (PathBuf, DirEntry)> {
 macro_rules! match_tokens {
     ($iter:ident, $($token:ident $({$($fields:tt)*})? $(($capture:ident))?)*) => {
          {
-            $($(let $capture =)? if let Some((TokenKind::$token $({$($fields)*})?, _x)) = $iter.next() {
+            $($(let $capture =)? if let Some(LintDeclSearchResult {
+                    token_kind: TokenKind::$token $({$($fields)*})?,
+                    content: _x,
+                    ..
+            }) = $iter.next() {
                 _x
             } else {
                 continue;
@@ -621,40 +824,72 @@ macro_rules! match_tokens {
     }
 }
 
+struct LintDeclSearchResult<'a> {
+    token_kind: TokenKind,
+    content: &'a str,
+    range: Range<usize>,
+}
+
 /// Parse a source file looking for `declare_clippy_lint` macro invocations.
 fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
     let mut offset = 0usize;
     let mut iter = tokenize(contents).map(|t| {
         let range = offset..offset + t.len;
         offset = range.end;
-        (t.kind, &contents[range])
+
+        LintDeclSearchResult {
+            token_kind: t.kind,
+            content: &contents[range.clone()],
+            range,
+        }
     });
 
-    while iter.any(|(kind, s)| kind == TokenKind::Ident && s == "declare_clippy_lint") {
+    while let Some(LintDeclSearchResult { range, .. }) = iter.find(
+        |LintDeclSearchResult {
+             token_kind, content, ..
+         }| token_kind == &TokenKind::Ident && *content == "declare_clippy_lint",
+    ) {
+        let start = range.start;
+
         let mut iter = iter
             .by_ref()
-            .filter(|&(kind, _)| !matches!(kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
+            .filter(|t| !matches!(t.token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
         // matches `!{`
         match_tokens!(iter, Bang OpenBrace);
         match iter.next() {
             // #[clippy::version = "version"] pub
-            Some((TokenKind::Pound, _)) => {
+            Some(LintDeclSearchResult {
+                token_kind: TokenKind::Pound,
+                ..
+            }) => {
                 match_tokens!(iter, OpenBracket Ident Colon Colon Ident Eq Literal{..} CloseBracket Ident);
             },
             // pub
-            Some((TokenKind::Ident, _)) => (),
+            Some(LintDeclSearchResult {
+                token_kind: TokenKind::Ident,
+                ..
+            }) => (),
             _ => continue,
         }
+
         let (name, group, desc) = match_tokens!(
             iter,
             // LINT_NAME
             Ident(name) Comma
             // group,
             Ident(group) Comma
-            // "description" }
-            Literal{..}(desc) CloseBrace
+            // "description"
+            Literal{..}(desc)
         );
-        lints.push(Lint::new(name, group, desc, module));
+
+        if let Some(LintDeclSearchResult {
+            token_kind: TokenKind::CloseBrace,
+            range,
+            ..
+        }) = iter.next()
+        {
+            lints.push(Lint::new(name, group, desc, module, start..range.end));
+        }
     }
 }
 
@@ -664,12 +899,24 @@ fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) {
     let mut iter = tokenize(contents).map(|t| {
         let range = offset..offset + t.len;
         offset = range.end;
-        (t.kind, &contents[range])
+
+        LintDeclSearchResult {
+            token_kind: t.kind,
+            content: &contents[range.clone()],
+            range,
+        }
     });
-    while iter.any(|(kind, s)| kind == TokenKind::Ident && s == "declare_deprecated_lint") {
-        let mut iter = iter
-            .by_ref()
-            .filter(|&(kind, _)| !matches!(kind, TokenKind::Whitespace | TokenKind::LineComment { .. }));
+
+    while let Some(LintDeclSearchResult { range, .. }) = iter.find(
+        |LintDeclSearchResult {
+             token_kind, content, ..
+         }| token_kind == &TokenKind::Ident && *content == "declare_deprecated_lint",
+    ) {
+        let start = range.start;
+
+        let mut iter = iter.by_ref().filter(|LintDeclSearchResult { ref token_kind, .. }| {
+            !matches!(token_kind, TokenKind::Whitespace | TokenKind::LineComment { .. })
+        });
         let (name, reason) = match_tokens!(
             iter,
             // !{
@@ -680,10 +927,16 @@ fn parse_deprecated_contents(contents: &str, lints: &mut Vec<DeprecatedLint>) {
             Ident Ident(name) Comma
             // "description"
             Literal{kind: LiteralKind::Str{..},..}(reason)
-            // }
-            CloseBrace
         );
-        lints.push(DeprecatedLint::new(name, reason));
+
+        if let Some(LintDeclSearchResult {
+            token_kind: TokenKind::CloseBrace,
+            range,
+            ..
+        }) = iter.next()
+        {
+            lints.push(DeprecatedLint::new(name, reason, start..range.end));
+        }
     }
 }
 
@@ -693,8 +946,14 @@ fn parse_renamed_contents(contents: &str, lints: &mut Vec<RenamedLint>) {
         let mut iter = tokenize(line).map(|t| {
             let range = offset..offset + t.len;
             offset = range.end;
-            (t.kind, &line[range])
+
+            LintDeclSearchResult {
+                token_kind: t.kind,
+                content: &line[range.clone()],
+                range,
+            }
         });
+
         let (old_name, new_name) = match_tokens!(
             iter,
             // ("old_name",
@@ -844,10 +1103,25 @@ mod tests {
         "#;
         let mut result = Vec::new();
         parse_contents(CONTENTS, "module_name", &mut result);
+        for r in &mut result {
+            r.declaration_range = Range::default();
+        }
 
         let expected = vec![
-            Lint::new("ptr_arg", "style", "\"really long text\"", "module_name"),
-            Lint::new("doc_markdown", "pedantic", "\"single line\"", "module_name"),
+            Lint::new(
+                "ptr_arg",
+                "style",
+                "\"really long text\"",
+                "module_name",
+                Range::default(),
+            ),
+            Lint::new(
+                "doc_markdown",
+                "pedantic",
+                "\"single line\"",
+                "module_name",
+                Range::default(),
+            ),
         ];
         assert_eq!(expected, result);
     }
@@ -865,10 +1139,14 @@ mod tests {
 
         let mut result = Vec::new();
         parse_deprecated_contents(DEPRECATED_CONTENTS, &mut result);
+        for r in &mut result {
+            r.declaration_range = Range::default();
+        }
 
         let expected = vec![DeprecatedLint::new(
             "should_assert_eq",
             "\"`assert!()` will be more flexible with RFC 2011\"",
+            Range::default(),
         )];
         assert_eq!(expected, result);
     }
@@ -876,15 +1154,34 @@ mod tests {
     #[test]
     fn test_usable_lints() {
         let lints = vec![
-            Lint::new("should_assert_eq2", "Not Deprecated", "\"abc\"", "module_name"),
-            Lint::new("should_assert_eq2", "internal", "\"abc\"", "module_name"),
-            Lint::new("should_assert_eq2", "internal_style", "\"abc\"", "module_name"),
+            Lint::new(
+                "should_assert_eq2",
+                "Not Deprecated",
+                "\"abc\"",
+                "module_name",
+                Range::default(),
+            ),
+            Lint::new(
+                "should_assert_eq2",
+                "internal",
+                "\"abc\"",
+                "module_name",
+                Range::default(),
+            ),
+            Lint::new(
+                "should_assert_eq2",
+                "internal_style",
+                "\"abc\"",
+                "module_name",
+                Range::default(),
+            ),
         ];
         let expected = vec![Lint::new(
             "should_assert_eq2",
             "Not Deprecated",
             "\"abc\"",
             "module_name",
+            Range::default(),
         )];
         assert_eq!(expected, Lint::usable_lints(&lints));
     }
@@ -892,21 +1189,33 @@ mod tests {
     #[test]
     fn test_by_lint_group() {
         let lints = vec![
-            Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"),
-            Lint::new("should_assert_eq2", "group2", "\"abc\"", "module_name"),
-            Lint::new("incorrect_match", "group1", "\"abc\"", "module_name"),
+            Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
+            Lint::new(
+                "should_assert_eq2",
+                "group2",
+                "\"abc\"",
+                "module_name",
+                Range::default(),
+            ),
+            Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
         ];
         let mut expected: HashMap<String, Vec<Lint>> = HashMap::new();
         expected.insert(
             "group1".to_string(),
             vec![
-                Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"),
-                Lint::new("incorrect_match", "group1", "\"abc\"", "module_name"),
+                Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
+                Lint::new("incorrect_match", "group1", "\"abc\"", "module_name", Range::default()),
             ],
         );
         expected.insert(
             "group2".to_string(),
-            vec![Lint::new("should_assert_eq2", "group2", "\"abc\"", "module_name")],
+            vec![Lint::new(
+                "should_assert_eq2",
+                "group2",
+                "\"abc\"",
+                "module_name",
+                Range::default(),
+            )],
         );
         assert_eq!(expected, Lint::by_lint_group(lints.into_iter()));
     }
@@ -914,8 +1223,12 @@ mod tests {
     #[test]
     fn test_gen_deprecated() {
         let lints = vec![
-            DeprecatedLint::new("should_assert_eq", "\"has been superseded by should_assert_eq2\""),
-            DeprecatedLint::new("another_deprecated", "\"will be removed\""),
+            DeprecatedLint::new(
+                "should_assert_eq",
+                "\"has been superseded by should_assert_eq2\"",
+                Range::default(),
+            ),
+            DeprecatedLint::new("another_deprecated", "\"will be removed\"", Range::default()),
         ];
 
         let expected = GENERATED_FILE_COMMENT.to_string()
@@ -940,9 +1253,9 @@ mod tests {
     #[test]
     fn test_gen_lint_group_list() {
         let lints = vec![
-            Lint::new("abc", "group1", "\"abc\"", "module_name"),
-            Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name"),
-            Lint::new("internal", "internal_style", "\"abc\"", "module_name"),
+            Lint::new("abc", "group1", "\"abc\"", "module_name", Range::default()),
+            Lint::new("should_assert_eq", "group1", "\"abc\"", "module_name", Range::default()),
+            Lint::new("internal", "internal_style", "\"abc\"", "module_name", Range::default()),
         ];
         let expected = GENERATED_FILE_COMMENT.to_string()
             + &[
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index 4d5bf47833f..79a56dc405d 100644
--- a/src/tools/clippy/clippy_lints/Cargo.toml
+++ b/src/tools/clippy/clippy_lints/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_lints"
-version = "0.1.63"
+version = "0.1.64"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
@@ -10,7 +10,6 @@ edition = "2021"
 
 [dependencies]
 cargo_metadata = "0.14"
-clippy_dev = { path = "../clippy_dev", optional = true }
 clippy_utils = { path = "../clippy_utils" }
 if_chain = "1.0"
 itertools = "0.10.1"
@@ -32,7 +31,7 @@ url = { version = "2.2", features = ["serde"] }
 [features]
 deny-warnings = ["clippy_utils/deny-warnings"]
 # build clippy with internal lints enabled, off by default
-internal = ["clippy_utils/internal", "serde_json", "tempfile", "clippy_dev"]
+internal = ["clippy_utils/internal", "serde_json", "tempfile"]
 
 [package.metadata.rust-analyzer]
 # This crate uses #[feature(rustc_private)]
diff --git a/src/tools/clippy/clippy_lints/src/assign_ops.rs b/src/tools/clippy/clippy_lints/src/assign_ops.rs
deleted file mode 100644
index f81da2d4223..00000000000
--- a/src/tools/clippy/clippy_lints/src/assign_ops.rs
+++ /dev/null
@@ -1,235 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
-use clippy_utils::ty::implements_trait;
-use clippy_utils::{binop_traits, sugg};
-use clippy_utils::{eq_expr_value, trait_ref_of_method};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir as hir;
-use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for `a = a op b` or `a = b commutative_op a`
-    /// patterns.
-    ///
-    /// ### Why is this bad?
-    /// These can be written as the shorter `a op= b`.
-    ///
-    /// ### Known problems
-    /// While forbidden by the spec, `OpAssign` traits may have
-    /// implementations that differ from the regular `Op` impl.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let mut a = 5;
-    /// let b = 0;
-    /// // ...
-    ///
-    /// a = a + b;
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// let mut a = 5;
-    /// let b = 0;
-    /// // ...
-    ///
-    /// a += b;
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub ASSIGN_OP_PATTERN,
-    style,
-    "assigning the result of an operation on a variable to that same variable"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for `a op= a op b` or `a op= b op a` patterns.
-    ///
-    /// ### Why is this bad?
-    /// Most likely these are bugs where one meant to write `a
-    /// op= b`.
-    ///
-    /// ### Known problems
-    /// Clippy cannot know for sure if `a op= a op b` should have
-    /// been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both.
-    /// If `a op= a op b` is really the correct behavior it should be
-    /// written as `a = a op a op b` as it's less confusing.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let mut a = 5;
-    /// let b = 2;
-    /// // ...
-    /// a += a + b;
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub MISREFACTORED_ASSIGN_OP,
-    suspicious,
-    "having a variable on both sides of an assign op"
-}
-
-declare_lint_pass!(AssignOps => [ASSIGN_OP_PATTERN, MISREFACTORED_ASSIGN_OP]);
-
-impl<'tcx> LateLintPass<'tcx> for AssignOps {
-    #[allow(clippy::too_many_lines)]
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        match &expr.kind {
-            hir::ExprKind::AssignOp(op, lhs, rhs) => {
-                if let hir::ExprKind::Binary(binop, l, r) = &rhs.kind {
-                    if op.node != binop.node {
-                        return;
-                    }
-                    // lhs op= l op r
-                    if eq_expr_value(cx, lhs, l) {
-                        lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, r);
-                    }
-                    // lhs op= l commutative_op r
-                    if is_commutative(op.node) && eq_expr_value(cx, lhs, r) {
-                        lint_misrefactored_assign_op(cx, expr, *op, rhs, lhs, l);
-                    }
-                }
-            },
-            hir::ExprKind::Assign(assignee, e, _) => {
-                if let hir::ExprKind::Binary(op, l, r) = &e.kind {
-                    let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| {
-                        let ty = cx.typeck_results().expr_ty(assignee);
-                        let rty = cx.typeck_results().expr_ty(rhs);
-                        if_chain! {
-                            if let Some((_, lang_item)) = binop_traits(op.node);
-                            if let Ok(trait_id) = cx.tcx.lang_items().require(lang_item);
-                            let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id);
-                            if trait_ref_of_method(cx, parent_fn)
-                                .map_or(true, |t| t.path.res.def_id() != trait_id);
-                            if implements_trait(cx, ty, trait_id, &[rty.into()]);
-                            then {
-                                span_lint_and_then(
-                                    cx,
-                                    ASSIGN_OP_PATTERN,
-                                    expr.span,
-                                    "manual implementation of an assign operation",
-                                    |diag| {
-                                        if let (Some(snip_a), Some(snip_r)) =
-                                            (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span))
-                                        {
-                                            diag.span_suggestion(
-                                                expr.span,
-                                                "replace it with",
-                                                format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
-                                                Applicability::MachineApplicable,
-                                            );
-                                        }
-                                    },
-                                );
-                            }
-                        }
-                    };
-
-                    let mut visitor = ExprVisitor {
-                        assignee,
-                        counter: 0,
-                        cx,
-                    };
-
-                    walk_expr(&mut visitor, e);
-
-                    if visitor.counter == 1 {
-                        // a = a op b
-                        if eq_expr_value(cx, assignee, l) {
-                            lint(assignee, r);
-                        }
-                        // a = b commutative_op a
-                        // Limited to primitive type as these ops are know to be commutative
-                        if eq_expr_value(cx, assignee, r) && cx.typeck_results().expr_ty(assignee).is_primitive_ty() {
-                            match op.node {
-                                hir::BinOpKind::Add
-                                | hir::BinOpKind::Mul
-                                | hir::BinOpKind::And
-                                | hir::BinOpKind::Or
-                                | hir::BinOpKind::BitXor
-                                | hir::BinOpKind::BitAnd
-                                | hir::BinOpKind::BitOr => {
-                                    lint(assignee, l);
-                                },
-                                _ => {},
-                            }
-                        }
-                    }
-                }
-            },
-            _ => {},
-        }
-    }
-}
-
-fn lint_misrefactored_assign_op(
-    cx: &LateContext<'_>,
-    expr: &hir::Expr<'_>,
-    op: hir::BinOp,
-    rhs: &hir::Expr<'_>,
-    assignee: &hir::Expr<'_>,
-    rhs_other: &hir::Expr<'_>,
-) {
-    span_lint_and_then(
-        cx,
-        MISREFACTORED_ASSIGN_OP,
-        expr.span,
-        "variable appears on both sides of an assignment operation",
-        |diag| {
-            if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) {
-                let a = &sugg::Sugg::hir(cx, assignee, "..");
-                let r = &sugg::Sugg::hir(cx, rhs, "..");
-                let long = format!("{} = {}", snip_a, sugg::make_binop(op.node.into(), a, r));
-                diag.span_suggestion(
-                    expr.span,
-                    &format!(
-                        "did you mean `{} = {} {} {}` or `{}`? Consider replacing it with",
-                        snip_a,
-                        snip_a,
-                        op.node.as_str(),
-                        snip_r,
-                        long
-                    ),
-                    format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
-                    Applicability::MaybeIncorrect,
-                );
-                diag.span_suggestion(
-                    expr.span,
-                    "or",
-                    long,
-                    Applicability::MaybeIncorrect, // snippet
-                );
-            }
-        },
-    );
-}
-
-#[must_use]
-fn is_commutative(op: hir::BinOpKind) -> bool {
-    use rustc_hir::BinOpKind::{
-        Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub,
-    };
-    match op {
-        Add | Mul | And | Or | BitXor | BitAnd | BitOr | Eq | Ne => true,
-        Sub | Div | Rem | Shl | Shr | Lt | Le | Ge | Gt => false,
-    }
-}
-
-struct ExprVisitor<'a, 'tcx> {
-    assignee: &'a hir::Expr<'a>,
-    counter: u8,
-    cx: &'a LateContext<'tcx>,
-}
-
-impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
-    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
-        if eq_expr_value(self.cx, self.assignee, expr) {
-            self.counter += 1;
-        }
-
-        walk_expr(self, expr);
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/attrs.rs b/src/tools/clippy/clippy_lints/src/attrs.rs
index ed12ad9c367..4bcbeacf9fe 100644
--- a/src/tools/clippy/clippy_lints/src/attrs.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs.rs
@@ -78,10 +78,17 @@ declare_clippy_lint! {
     /// Checks for `extern crate` and `use` items annotated with
     /// lint attributes.
     ///
-    /// This lint permits `#[allow(unused_imports)]`, `#[allow(deprecated)]`,
-    /// `#[allow(unreachable_pub)]`, `#[allow(clippy::wildcard_imports)]` and
-    /// `#[allow(clippy::enum_glob_use)]` on `use` items and `#[allow(unused_imports)]` on
-    /// `extern crate` items with a `#[macro_use]` attribute.
+    /// This lint permits lint attributes for lints emitted on the items themself.
+    /// For `use` items these lints are:
+    /// * deprecated
+    /// * unreachable_pub
+    /// * unused_imports
+    /// * clippy::enum_glob_use
+    /// * clippy::macro_use_imports
+    /// * clippy::wildcard_imports
+    ///
+    /// For `extern crate` items these lints are:
+    /// * `unused_imports` on items with `#[macro_use]`
     ///
     /// ### Why is this bad?
     /// Lint attributes have no effect on crate imports. Most
@@ -347,7 +354,10 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
                                             || extract_clippy_lint(lint).map_or(false, |s| {
                                                 matches!(
                                                     s.as_str(),
-                                                    "wildcard_imports" | "enum_glob_use" | "redundant_pub_crate",
+                                                    "wildcard_imports"
+                                                        | "enum_glob_use"
+                                                        | "redundant_pub_crate"
+                                                        | "macro_use_imports",
                                                 )
                                             })
                                         {
diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs
index e4e122ba6eb..526ee2f891a 100644
--- a/src/tools/clippy/clippy_lints/src/booleans.rs
+++ b/src/tools/clippy/clippy_lints/src/booleans.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 use clippy_utils::{eq_expr_value, get_trait_def_id, paths};
@@ -394,9 +394,10 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
                         continue 'simplified;
                     }
                     if stats.terminals[i] != 0 && simplified_stats.terminals[i] == 0 {
-                        span_lint_and_then(
+                        span_lint_hir_and_then(
                             self.cx,
                             LOGIC_BUG,
+                            e.hir_id,
                             e.span,
                             "this boolean expression contains a logic bug",
                             |diag| {
@@ -429,9 +430,10 @@ impl<'a, 'tcx> NonminimalBoolVisitor<'a, 'tcx> {
                 }
             }
             let nonminimal_bool_lint = |suggestions: Vec<_>| {
-                span_lint_and_then(
+                span_lint_hir_and_then(
                     self.cx,
                     NONMINIMAL_BOOL,
+                    e.hir_id,
                     e.span,
                     "this boolean expression can be simplified",
                     |diag| {
diff --git a/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
new file mode 100644
index 00000000000..3c996d3d2ae
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/default_instead_of_iter_empty.rs
@@ -0,0 +1,68 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::last_path_segment;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{match_def_path, paths};
+use rustc_errors::Applicability;
+use rustc_hir::{def, Expr, ExprKind, GenericArg, QPath, TyKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_lint_pass, declare_tool_lint};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// It checks for `std::iter::Empty::default()` and suggests replacing it with
+    /// `std::iter::empty()`.
+    /// ### Why is this bad?
+    /// `std::iter::empty()` is the more idiomatic way.
+    /// ### Example
+    /// ```rust
+    /// let _ = std::iter::Empty::<usize>::default();
+    /// let iter: std::iter::Empty<usize> = std::iter::Empty::default();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let _ = std::iter::empty::<usize>();
+    /// let iter: std::iter::Empty<usize> = std::iter::empty();
+    /// ```
+    #[clippy::version = "1.63.0"]
+    pub DEFAULT_INSTEAD_OF_ITER_EMPTY,
+    style,
+    "check `std::iter::Empty::default()` and replace with `std::iter::empty()`"
+}
+declare_lint_pass!(DefaultIterEmpty => [DEFAULT_INSTEAD_OF_ITER_EMPTY]);
+
+impl<'tcx> LateLintPass<'tcx> for DefaultIterEmpty {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if let ExprKind::Call(iter_expr, []) = &expr.kind
+            && let ExprKind::Path(QPath::TypeRelative(ty, _)) = &iter_expr.kind
+            && let TyKind::Path(ty_path) = &ty.kind
+            && let QPath::Resolved(None, path) = ty_path
+            && let def::Res::Def(_, def_id) = &path.res
+            && match_def_path(cx, *def_id, &paths::ITER_EMPTY)
+        {
+            let mut applicability = Applicability::MachineApplicable;
+            let sugg = make_sugg(cx, ty_path, &mut applicability);
+            span_lint_and_sugg(
+                cx,
+                DEFAULT_INSTEAD_OF_ITER_EMPTY,
+                expr.span,
+                "`std::iter::empty()` is the more idiomatic way",
+                "try",
+                sugg,
+                applicability,
+            );
+        }
+    }
+}
+
+fn make_sugg(cx: &LateContext<'_>, ty_path: &rustc_hir::QPath<'_>, applicability: &mut Applicability) -> String {
+    if let Some(last) = last_path_segment(ty_path).args
+        && let Some(iter_ty) = last.args.iter().find_map(|arg| match arg {
+            GenericArg::Type(ty) => Some(ty),
+            _ => None,
+        })
+    {
+        format!("std::iter::empty::<{}>()", snippet_with_applicability(cx, iter_ty.span, "..", applicability))
+    } else {
+        "std::iter::empty()".to_owned()
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
index 5d5ea0f49c8..9aa5af3190f 100644
--- a/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/deprecated_lints.rs
@@ -1,16 +1,21 @@
-// NOTE: if you add a deprecated lint in this file, please add a corresponding test in
-// tests/ui/deprecated.rs
+// NOTE: Entries should be created with `cargo dev deprecate`
 
 /// This struct fakes the `Lint` declaration that is usually created by `declare_lint!`. This
 /// enables the simple extraction of the metadata without changing the current deprecation
 /// declaration.
-pub struct ClippyDeprecatedLint;
+pub struct ClippyDeprecatedLint {
+    #[allow(dead_code)]
+    pub desc: &'static str,
+}
 
+#[macro_export]
 macro_rules! declare_deprecated_lint {
-    { $(#[$attr:meta])* pub $name: ident, $_reason: expr} => {
+    { $(#[$attr:meta])* pub $name: ident, $reason: literal} => {
         $(#[$attr])*
         #[allow(dead_code)]
-        pub static $name: ClippyDeprecatedLint = ClippyDeprecatedLint {};
+        pub static $name: ClippyDeprecatedLint = ClippyDeprecatedLint {
+            desc: $reason
+        };
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index b47441eff37..59dcc1ebf19 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -1,20 +1,24 @@
 use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
-use clippy_utils::ty::peel_mid_ty_refs;
-use clippy_utils::{get_parent_expr, get_parent_node, is_lint_allowed, path_to_local};
+use clippy_utils::ty::{expr_sig, peel_mid_ty_refs, variant_of_res};
+use clippy_utils::{get_parent_expr, is_lint_allowed, path_to_local, walk_to_expr_usage};
 use rustc_ast::util::parser::{PREC_POSTFIX, PREC_PREFIX};
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_errors::Applicability;
+use rustc_hir::intravisit::{walk_ty, Visitor};
 use rustc_hir::{
-    BindingAnnotation, Body, BodyId, BorrowKind, Destination, Expr, ExprKind, HirId, MatchSource, Mutability, Node,
-    Pat, PatKind, UnOp,
+    self as hir, BindingAnnotation, Body, BodyId, BorrowKind, Expr, ExprKind, GenericArg, HirId, ImplItem,
+    ImplItemKind, Item, ItemKind, Local, MatchSource, Mutability, Node, Pat, PatKind, Path, QPath, TraitItem,
+    TraitItemKind, TyKind, UnOp,
 };
+use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
-use rustc_middle::ty::{self, Ty, TyCtxt, TypeckResults};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeckResults};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::{symbol::sym, Span};
+use rustc_span::{symbol::sym, Span, Symbol};
+use rustc_trait_selection::infer::InferCtxtExt;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -104,10 +108,34 @@ declare_clippy_lint! {
     "`ref` binding to a reference"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for dereferencing expressions which would be covered by auto-deref.
+    ///
+    /// ### Why is this bad?
+    /// This unnecessarily complicates the code.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x = String::new();
+    /// let y: &str = &*x;
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let x = String::new();
+    /// let y: &str = &x;
+    /// ```
+    #[clippy::version = "1.60.0"]
+    pub EXPLICIT_AUTO_DEREF,
+    complexity,
+    "dereferencing when the compiler would automatically dereference"
+}
+
 impl_lint_pass!(Dereferencing => [
     EXPLICIT_DEREF_METHODS,
     NEEDLESS_BORROW,
     REF_BINDING_TO_REFERENCE,
+    EXPLICIT_AUTO_DEREF,
 ]);
 
 #[derive(Default)]
@@ -136,6 +164,12 @@ struct StateData {
     /// Span of the top level expression
     span: Span,
     hir_id: HirId,
+    position: Position,
+}
+
+struct DerefedBorrow {
+    count: usize,
+    msg: &'static str,
 }
 
 enum State {
@@ -147,11 +181,19 @@ enum State {
         /// The required mutability
         target_mut: Mutability,
     },
-    DerefedBorrow {
-        count: usize,
-        required_precedence: i8,
-        msg: &'static str,
+    DerefedBorrow(DerefedBorrow),
+    ExplicitDeref {
+        // Span and id of the top-level deref expression if the parent expression is a borrow.
+        deref_span_id: Option<(Span, HirId)>,
+    },
+    ExplicitDerefField {
+        name: Symbol,
     },
+    Reborrow {
+        deref_span: Span,
+        deref_hir_id: HirId,
+    },
+    Borrow,
 }
 
 // A reference operation considered by this lint pass
@@ -207,13 +249,28 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
 
         match (self.state.take(), kind) {
             (None, kind) => {
-                let parent = get_parent_node(cx.tcx, expr.hir_id);
                 let expr_ty = typeck.expr_ty(expr);
+                let (position, adjustments) = walk_parents(cx, expr);
 
                 match kind {
+                    RefOp::Deref => {
+                        if let Position::FieldAccess(name) = position
+                            && !ty_contains_field(typeck.expr_ty(sub_expr), name)
+                        {
+                            self.state = Some((
+                                State::ExplicitDerefField { name },
+                                StateData { span: expr.span, hir_id: expr.hir_id, position },
+                            ));
+                        } else if position.is_deref_stable() {
+                            self.state = Some((
+                                State::ExplicitDeref { deref_span_id: None },
+                                StateData { span: expr.span, hir_id: expr.hir_id, position },
+                            ));
+                        }
+                    }
                     RefOp::Method(target_mut)
                         if !is_lint_allowed(cx, EXPLICIT_DEREF_METHODS, expr.hir_id)
-                            && is_linted_explicit_deref_position(parent, expr.hir_id, expr.span) =>
+                            && position.lint_explicit_deref() =>
                     {
                         self.state = Some((
                             State::DerefMethod {
@@ -228,12 +285,13 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
                             StateData {
                                 span: expr.span,
                                 hir_id: expr.hir_id,
+                                position
                             },
                         ));
                     },
                     RefOp::AddrOf => {
                         // Find the number of times the borrow is auto-derefed.
-                        let mut iter = find_adjustments(cx.tcx, typeck, expr).iter();
+                        let mut iter = adjustments.iter();
                         let mut deref_count = 0usize;
                         let next_adjust = loop {
                             match iter.next() {
@@ -274,40 +332,43 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
                             "this expression creates a reference which is immediately dereferenced by the compiler";
                         let borrow_msg = "this expression borrows a value the compiler would automatically borrow";
 
-                        let (required_refs, required_precedence, msg) = if is_auto_borrow_position(parent, expr.hir_id)
-                        {
-                            (1, PREC_POSTFIX, if deref_count == 1 { borrow_msg } else { deref_msg })
+                        let (required_refs, msg) = if position.can_auto_borrow() {
+                            (1, if deref_count == 1 { borrow_msg } else { deref_msg })
                         } else if let Some(&Adjust::Borrow(AutoBorrow::Ref(_, mutability))) =
                             next_adjust.map(|a| &a.kind)
                         {
-                            if matches!(mutability, AutoBorrowMutability::Mut { .. })
-                                && !is_auto_reborrow_position(parent)
+                            if matches!(mutability, AutoBorrowMutability::Mut { .. }) && !position.is_reborrow_stable()
                             {
-                                (3, 0, deref_msg)
+                                (3, deref_msg)
                             } else {
-                                (2, 0, deref_msg)
+                                (2, deref_msg)
                             }
                         } else {
-                            (2, 0, deref_msg)
+                            (2, deref_msg)
                         };
 
                         if deref_count >= required_refs {
                             self.state = Some((
-                                State::DerefedBorrow {
+                                State::DerefedBorrow(DerefedBorrow {
                                     // One of the required refs is for the current borrow expression, the remaining ones
                                     // can't be removed without breaking the code. See earlier comment.
                                     count: deref_count - required_refs,
-                                    required_precedence,
                                     msg,
-                                },
+                                }),
+                                StateData { span: expr.span, hir_id: expr.hir_id, position },
+                            ));
+                        } else if position.is_deref_stable() {
+                            self.state = Some((
+                                State::Borrow,
                                 StateData {
                                     span: expr.span,
                                     hir_id: expr.hir_id,
+                                    position
                                 },
                             ));
                         }
                     },
-                    _ => (),
+                    RefOp::Method(..) => (),
                 }
             },
             (
@@ -334,26 +395,90 @@ impl<'tcx> LateLintPass<'tcx> for Dereferencing {
                     data,
                 ));
             },
+            (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf) if state.count != 0 => {
+                self.state = Some((
+                    State::DerefedBorrow(DerefedBorrow {
+                        count: state.count - 1,
+                        ..state
+                    }),
+                    data,
+                ));
+            },
+            (Some((State::DerefedBorrow(state), data)), RefOp::AddrOf) => {
+                let position = data.position;
+                report(cx, expr, State::DerefedBorrow(state), data);
+                if position.is_deref_stable() {
+                    self.state = Some((
+                        State::Borrow,
+                        StateData {
+                            span: expr.span,
+                            hir_id: expr.hir_id,
+                            position,
+                        },
+                    ));
+                }
+            },
+            (Some((State::DerefedBorrow(state), data)), RefOp::Deref) => {
+                let position = data.position;
+                report(cx, expr, State::DerefedBorrow(state), data);
+                if let Position::FieldAccess(name) = position
+                    && !ty_contains_field(typeck.expr_ty(sub_expr), name)
+                {
+                    self.state = Some((
+                        State::ExplicitDerefField { name },
+                        StateData { span: expr.span, hir_id: expr.hir_id, position },
+                    ));
+                } else if position.is_deref_stable() {
+                    self.state = Some((
+                        State::ExplicitDeref { deref_span_id: None },
+                        StateData { span: expr.span, hir_id: expr.hir_id, position },
+                    ));
+                }
+            },
+
+            (Some((State::Borrow, data)), RefOp::Deref) => {
+                if typeck.expr_ty(sub_expr).is_ref() {
+                    self.state = Some((
+                        State::Reborrow {
+                            deref_span: expr.span,
+                            deref_hir_id: expr.hir_id,
+                        },
+                        data,
+                    ));
+                } else {
+                    self.state = Some((
+                        State::ExplicitDeref {
+                            deref_span_id: Some((expr.span, expr.hir_id)),
+                        },
+                        data,
+                    ));
+                }
+            },
             (
                 Some((
-                    State::DerefedBorrow {
-                        count,
-                        required_precedence,
-                        msg,
+                    State::Reborrow {
+                        deref_span,
+                        deref_hir_id,
                     },
                     data,
                 )),
-                RefOp::AddrOf,
-            ) if count != 0 => {
+                RefOp::Deref,
+            ) => {
                 self.state = Some((
-                    State::DerefedBorrow {
-                        count: count - 1,
-                        required_precedence,
-                        msg,
+                    State::ExplicitDeref {
+                        deref_span_id: Some((deref_span, deref_hir_id)),
                     },
                     data,
                 ));
             },
+            (state @ Some((State::ExplicitDeref { .. }, _)), RefOp::Deref) => {
+                self.state = state;
+            },
+            (Some((State::ExplicitDerefField { name }, data)), RefOp::Deref)
+                if !ty_contains_field(typeck.expr_ty(sub_expr), name) =>
+            {
+                self.state = Some((State::ExplicitDerefField { name }, data));
+            },
 
             (Some((state, data)), _) => report(cx, expr, state, data),
         }
@@ -473,131 +598,362 @@ fn deref_method_same_type<'tcx>(result_ty: Ty<'tcx>, arg_ty: Ty<'tcx>) -> bool {
     }
 }
 
-// Checks whether the parent node is a suitable context for switching from a deref method to the
-// deref operator.
-fn is_linted_explicit_deref_position(parent: Option<Node<'_>>, child_id: HirId, child_span: Span) -> bool {
-    let parent = match parent {
-        Some(Node::Expr(e)) if e.span.ctxt() == child_span.ctxt() => e,
-        _ => return true,
-    };
-    match parent.kind {
-        // Leave deref calls in the middle of a method chain.
-        // e.g. x.deref().foo()
-        ExprKind::MethodCall(_, [self_arg, ..], _) if self_arg.hir_id == child_id => false,
-
-        // Leave deref calls resulting in a called function
-        // e.g. (x.deref())()
-        ExprKind::Call(func_expr, _) if func_expr.hir_id == child_id => false,
-
-        // Makes an ugly suggestion
-        // e.g. *x.deref() => *&*x
-        ExprKind::Unary(UnOp::Deref, _)
-        // Postfix expressions would require parens
-        | ExprKind::Match(_, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
-        | ExprKind::Field(..)
-        | ExprKind::Index(..)
-        | ExprKind::Err => false,
-
-        ExprKind::Box(..)
-        | ExprKind::ConstBlock(..)
-        | ExprKind::Array(_)
-        | ExprKind::Call(..)
-        | ExprKind::MethodCall(..)
-        | ExprKind::Tup(..)
-        | ExprKind::Binary(..)
-        | ExprKind::Unary(..)
-        | ExprKind::Lit(..)
-        | ExprKind::Cast(..)
-        | ExprKind::Type(..)
-        | ExprKind::DropTemps(..)
-        | ExprKind::If(..)
-        | ExprKind::Loop(..)
-        | ExprKind::Match(..)
-        | ExprKind::Let(..)
-        | ExprKind::Closure{..}
-        | ExprKind::Block(..)
-        | ExprKind::Assign(..)
-        | ExprKind::AssignOp(..)
-        | ExprKind::Path(..)
-        | ExprKind::AddrOf(..)
-        | ExprKind::Break(..)
-        | ExprKind::Continue(..)
-        | ExprKind::Ret(..)
-        | ExprKind::InlineAsm(..)
-        | ExprKind::Struct(..)
-        | ExprKind::Repeat(..)
-        | ExprKind::Yield(..) => true,
-    }
+/// The position of an expression relative to it's parent.
+#[derive(Clone, Copy)]
+enum Position {
+    MethodReceiver,
+    /// The method is defined on a reference type. e.g. `impl Foo for &T`
+    MethodReceiverRefImpl,
+    Callee,
+    FieldAccess(Symbol),
+    Postfix,
+    Deref,
+    /// Any other location which will trigger auto-deref to a specific time.
+    DerefStable(i8),
+    /// Any other location which will trigger auto-reborrowing.
+    ReborrowStable(i8),
+    Other(i8),
 }
+impl Position {
+    fn is_deref_stable(self) -> bool {
+        matches!(self, Self::DerefStable(_))
+    }
 
-/// Checks if the given expression is in a position which can be auto-reborrowed.
-/// Note: This is only correct assuming auto-deref is already occurring.
-fn is_auto_reborrow_position(parent: Option<Node<'_>>) -> bool {
-    match parent {
-        Some(Node::Expr(parent)) => matches!(parent.kind, ExprKind::MethodCall(..) | ExprKind::Call(..)),
-        Some(Node::Local(_)) => true,
-        _ => false,
+    fn is_reborrow_stable(self) -> bool {
+        matches!(self, Self::DerefStable(_) | Self::ReborrowStable(_))
     }
-}
 
-/// Checks if the given expression is a position which can auto-borrow.
-fn is_auto_borrow_position(parent: Option<Node<'_>>, child_id: HirId) -> bool {
-    if let Some(Node::Expr(parent)) = parent {
-        match parent.kind {
-            // ExprKind::MethodCall(_, [self_arg, ..], _) => self_arg.hir_id == child_id,
-            ExprKind::Field(..) => true,
-            ExprKind::Call(f, _) => f.hir_id == child_id,
-            _ => false,
-        }
-    } else {
-        false
+    fn can_auto_borrow(self) -> bool {
+        matches!(self, Self::MethodReceiver | Self::FieldAccess(_) | Self::Callee)
     }
-}
 
-/// Adjustments are sometimes made in the parent block rather than the expression itself.
-fn find_adjustments<'tcx>(
-    tcx: TyCtxt<'tcx>,
-    typeck: &'tcx TypeckResults<'tcx>,
-    expr: &'tcx Expr<'tcx>,
-) -> &'tcx [Adjustment<'tcx>] {
-    let map = tcx.hir();
-    let mut iter = map.parent_iter(expr.hir_id);
-    let mut prev = expr;
+    fn lint_explicit_deref(self) -> bool {
+        matches!(self, Self::Other(_) | Self::DerefStable(_) | Self::ReborrowStable(_))
+    }
 
-    loop {
-        match typeck.expr_adjustments(prev) {
-            [] => (),
-            a => break a,
-        };
+    fn precedence(self) -> i8 {
+        match self {
+            Self::MethodReceiver
+            | Self::MethodReceiverRefImpl
+            | Self::Callee
+            | Self::FieldAccess(_)
+            | Self::Postfix => PREC_POSTFIX,
+            Self::Deref => PREC_PREFIX,
+            Self::DerefStable(p) | Self::ReborrowStable(p) | Self::Other(p) => p,
+        }
+    }
+}
 
-        match iter.next().map(|(_, x)| x) {
-            Some(Node::Block(_)) => {
-                if let Some((_, Node::Expr(e))) = iter.next() {
-                    prev = e;
+/// Walks up the parent expressions attempting to determine both how stable the auto-deref result
+/// is, and which adjustments will be applied to it. Note this will not consider auto-borrow
+/// locations as those follow different rules.
+#[allow(clippy::too_many_lines)]
+fn walk_parents<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> (Position, &'tcx [Adjustment<'tcx>]) {
+    let mut adjustments = [].as_slice();
+    let mut precedence = 0i8;
+    let ctxt = e.span.ctxt();
+    let position = walk_to_expr_usage(cx, e, &mut |parent, child_id| {
+        // LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead.
+        if adjustments.is_empty() && let Node::Expr(e) = cx.tcx.hir().get(child_id) {
+            adjustments = cx.typeck_results().expr_adjustments(e);
+        }
+        match parent {
+            Node::Local(Local { ty: Some(ty), span, .. }) if span.ctxt() == ctxt => {
+                Some(binding_ty_auto_deref_stability(ty, precedence))
+            },
+            Node::Item(&Item {
+                kind: ItemKind::Static(..) | ItemKind::Const(..),
+                def_id,
+                span,
+                ..
+            })
+            | Node::TraitItem(&TraitItem {
+                kind: TraitItemKind::Const(..),
+                def_id,
+                span,
+                ..
+            })
+            | Node::ImplItem(&ImplItem {
+                kind: ImplItemKind::Const(..),
+                def_id,
+                span,
+                ..
+            }) if span.ctxt() == ctxt => {
+                let ty = cx.tcx.type_of(def_id);
+                Some(if ty.is_ref() {
+                    Position::DerefStable(precedence)
                 } else {
-                    // This shouldn't happen. Blocks are always contained in an expression.
-                    break &[];
-                }
+                    Position::Other(precedence)
+                })
             },
-            Some(Node::Expr(&Expr {
-                kind: ExprKind::Break(Destination { target_id: Ok(id), .. }, _),
+
+            Node::Item(&Item {
+                kind: ItemKind::Fn(..),
+                def_id,
+                span,
+                ..
+            })
+            | Node::TraitItem(&TraitItem {
+                kind: TraitItemKind::Fn(..),
+                def_id,
+                span,
+                ..
+            })
+            | Node::ImplItem(&ImplItem {
+                kind: ImplItemKind::Fn(..),
+                def_id,
+                span,
                 ..
-            })) => {
-                if let Some(Node::Expr(e)) = map.find(id) {
-                    prev = e;
-                    iter = map.parent_iter(id);
+            }) if span.ctxt() == ctxt => {
+                let output = cx.tcx.fn_sig(def_id.to_def_id()).skip_binder().output();
+                Some(if !output.is_ref() {
+                    Position::Other(precedence)
+                } else if output.has_placeholders() || output.has_opaque_types() {
+                    Position::ReborrowStable(precedence)
+                } else {
+                    Position::DerefStable(precedence)
+                })
+            },
+
+            Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind {
+                ExprKind::Ret(_) => {
+                    let output = cx
+                        .tcx
+                        .fn_sig(cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()))
+                        .skip_binder()
+                        .output();
+                    Some(if !output.is_ref() {
+                        Position::Other(precedence)
+                    } else if output.has_placeholders() || output.has_opaque_types() {
+                        Position::ReborrowStable(precedence)
+                    } else {
+                        Position::DerefStable(precedence)
+                    })
+                },
+                ExprKind::Call(func, _) if func.hir_id == child_id => (child_id == e.hir_id).then(|| Position::Callee),
+                ExprKind::Call(func, args) => args
+                    .iter()
+                    .position(|arg| arg.hir_id == child_id)
+                    .zip(expr_sig(cx, func))
+                    .and_then(|(i, sig)| sig.input_with_hir(i))
+                    .map(|(hir_ty, ty)| match hir_ty {
+                        // Type inference for closures can depend on how they're called. Only go by the explicit
+                        // types here.
+                        Some(ty) => binding_ty_auto_deref_stability(ty, precedence),
+                        None => param_auto_deref_stability(ty.skip_binder(), precedence),
+                    }),
+                ExprKind::MethodCall(_, args, _) => {
+                    let id = cx.typeck_results().type_dependent_def_id(parent.hir_id).unwrap();
+                    args.iter().position(|arg| arg.hir_id == child_id).map(|i| {
+                        if i == 0 {
+                            // Check for calls to trait methods where the trait is implemented on a reference.
+                            // Two cases need to be handled:
+                            // * `self` methods on `&T` will never have auto-borrow
+                            // * `&self` methods on `&T` can have auto-borrow, but `&self` methods on `T` will take
+                            //   priority.
+                            if e.hir_id != child_id {
+                                Position::ReborrowStable(precedence)
+                            } else if let Some(trait_id) = cx.tcx.trait_of_item(id)
+                                && let arg_ty = cx.tcx.erase_regions(cx.typeck_results().expr_ty_adjusted(e))
+                                && let ty::Ref(_, sub_ty, _) = *arg_ty.kind()
+                                && let subs = cx.typeck_results().node_substs_opt(child_id).unwrap_or_else(
+                                    || cx.tcx.mk_substs([].iter())
+                                ) && let impl_ty = if cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref() {
+                                    // Trait methods taking `&self`
+                                    sub_ty
+                                } else {
+                                    // Trait methods taking `self`
+                                    arg_ty
+                                } && impl_ty.is_ref()
+                                && cx.tcx.infer_ctxt().enter(|infcx|
+                                    infcx
+                                        .type_implements_trait(trait_id, impl_ty, subs, cx.param_env)
+                                        .must_apply_modulo_regions()
+                                )
+                            {
+                                Position::MethodReceiverRefImpl
+                            } else {
+                                Position::MethodReceiver
+                            }
+                        } else {
+                            param_auto_deref_stability(cx.tcx.fn_sig(id).skip_binder().inputs()[i], precedence)
+                        }
+                    })
+                },
+                ExprKind::Struct(path, fields, _) => {
+                    let variant = variant_of_res(cx, cx.qpath_res(path, parent.hir_id));
+                    fields
+                        .iter()
+                        .find(|f| f.expr.hir_id == child_id)
+                        .zip(variant)
+                        .and_then(|(field, variant)| variant.fields.iter().find(|f| f.name == field.ident.name))
+                        .map(|field| param_auto_deref_stability(cx.tcx.type_of(field.did), precedence))
+                },
+                ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(Position::FieldAccess(name.name)),
+                ExprKind::Unary(UnOp::Deref, child) if child.hir_id == e.hir_id => Some(Position::Deref),
+                ExprKind::Match(child, _, MatchSource::TryDesugar | MatchSource::AwaitDesugar)
+                | ExprKind::Index(child, _)
+                    if child.hir_id == e.hir_id =>
+                {
+                    Some(Position::Postfix)
+                },
+                _ if child_id == e.hir_id => {
+                    precedence = parent.precedence().order();
+                    None
+                },
+                _ => None,
+            },
+            _ => None,
+        }
+    })
+    .unwrap_or(Position::Other(precedence));
+    (position, adjustments)
+}
+
+// Checks the stability of auto-deref when assigned to a binding with the given explicit type.
+//
+// e.g.
+// let x = Box::new(Box::new(0u32));
+// let y1: &Box<_> = x.deref();
+// let y2: &Box<_> = &x;
+//
+// Here `y1` and `y2` would resolve to different types, so the type `&Box<_>` is not stable when
+// switching to auto-dereferencing.
+fn binding_ty_auto_deref_stability(ty: &hir::Ty<'_>, precedence: i8) -> Position {
+    let TyKind::Rptr(_, ty) = &ty.kind else {
+        return Position::Other(precedence);
+    };
+    let mut ty = ty;
+
+    loop {
+        break match ty.ty.kind {
+            TyKind::Rptr(_, ref ref_ty) => {
+                ty = ref_ty;
+                continue;
+            },
+            TyKind::Path(
+                QPath::TypeRelative(_, path)
+                | QPath::Resolved(
+                    _,
+                    Path {
+                        segments: [.., path], ..
+                    },
+                ),
+            ) => {
+                if let Some(args) = path.args
+                    && args.args.iter().any(|arg| match arg {
+                        GenericArg::Infer(_) => true,
+                        GenericArg::Type(ty) => ty_contains_infer(ty),
+                        _ => false,
+                    })
+                {
+                    Position::ReborrowStable(precedence)
                 } else {
-                    // This shouldn't happen. The destination should exist.
-                    break &[];
+                    Position::DerefStable(precedence)
                 }
             },
-            _ => break &[],
+            TyKind::Slice(_)
+            | TyKind::Array(..)
+            | TyKind::BareFn(_)
+            | TyKind::Never
+            | TyKind::Tup(_)
+            | TyKind::Ptr(_)
+            | TyKind::TraitObject(..)
+            | TyKind::Path(_) => Position::DerefStable(precedence),
+            TyKind::OpaqueDef(..)
+            | TyKind::Infer
+            | TyKind::Typeof(..)
+            | TyKind::Err => Position::ReborrowStable(precedence),
+        };
+    }
+}
+
+// Checks whether a type is inferred at some point.
+// e.g. `_`, `Box<_>`, `[_]`
+fn ty_contains_infer(ty: &hir::Ty<'_>) -> bool {
+    struct V(bool);
+    impl Visitor<'_> for V {
+        fn visit_ty(&mut self, ty: &hir::Ty<'_>) {
+            if self.0
+                || matches!(
+                    ty.kind,
+                    TyKind::OpaqueDef(..) | TyKind::Infer | TyKind::Typeof(_) | TyKind::Err
+                )
+            {
+                self.0 = true;
+            } else {
+                walk_ty(self, ty);
+            }
         }
+
+        fn visit_generic_arg(&mut self, arg: &GenericArg<'_>) {
+            if self.0 || matches!(arg, GenericArg::Infer(_)) {
+                self.0 = true;
+            } else if let GenericArg::Type(ty) = arg {
+                self.visit_ty(ty);
+            }
+        }
+    }
+    let mut v = V(false);
+    v.visit_ty(ty);
+    v.0
+}
+
+// Checks whether a type is stable when switching to auto dereferencing,
+fn param_auto_deref_stability(ty: Ty<'_>, precedence: i8) -> Position {
+    let ty::Ref(_, mut ty, _) = *ty.kind() else {
+        return Position::Other(precedence);
+    };
+
+    loop {
+        break match *ty.kind() {
+            ty::Ref(_, ref_ty, _) => {
+                ty = ref_ty;
+                continue;
+            },
+            ty::Infer(_)
+            | ty::Error(_)
+            | ty::Param(_)
+            | ty::Bound(..)
+            | ty::Opaque(..)
+            | ty::Placeholder(_)
+            | ty::Dynamic(..) => Position::ReborrowStable(precedence),
+            ty::Adt(..) if ty.has_placeholders() || ty.has_param_types_or_consts() => {
+                Position::ReborrowStable(precedence)
+            },
+            ty::Adt(..)
+            | ty::Bool
+            | ty::Char
+            | ty::Int(_)
+            | ty::Uint(_)
+            | ty::Float(_)
+            | ty::Foreign(_)
+            | ty::Str
+            | ty::Array(..)
+            | ty::Slice(..)
+            | ty::RawPtr(..)
+            | ty::FnDef(..)
+            | ty::FnPtr(_)
+            | ty::Closure(..)
+            | ty::Generator(..)
+            | ty::GeneratorWitness(..)
+            | ty::Never
+            | ty::Tuple(_)
+            | ty::Projection(_) => Position::DerefStable(precedence),
+        };
+    }
+}
+
+fn ty_contains_field(ty: Ty<'_>, name: Symbol) -> bool {
+    if let ty::Adt(adt, _) = *ty.kind() {
+        adt.is_struct() && adt.all_fields().any(|f| f.name == name)
+    } else {
+        false
     }
 }
 
-#[expect(clippy::needless_pass_by_value)]
-fn report<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, state: State, data: StateData) {
+#[expect(clippy::needless_pass_by_value, clippy::too_many_lines)]
+fn report<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, state: State, data: StateData) {
     match state {
         State::DerefMethod {
             ty_changed_count,
@@ -647,15 +1003,14 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, state: State, data: S
                 app,
             );
         },
-        State::DerefedBorrow {
-            required_precedence,
-            msg,
-            ..
-        } => {
+        State::DerefedBorrow(state) => {
             let mut app = Applicability::MachineApplicable;
-            let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
-            span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, msg, |diag| {
-                let sugg = if required_precedence > expr.precedence().order() && !has_enclosing_paren(&snip) {
+            let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app);
+            span_lint_hir_and_then(cx, NEEDLESS_BORROW, data.hir_id, data.span, state.msg, |diag| {
+                let sugg = if !snip_is_macro
+                    && expr.precedence().order() < data.position.precedence()
+                    && !has_enclosing_paren(&snip)
+                {
                     format!("({})", snip)
                 } else {
                     snip.into()
@@ -663,6 +1018,48 @@ fn report<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>, state: State, data: S
                 diag.span_suggestion(data.span, "change this to", sugg, app);
             });
         },
+        State::ExplicitDeref { deref_span_id } => {
+            let (span, hir_id, precedence) = if let Some((span, hir_id)) = deref_span_id
+                && !cx.typeck_results().expr_ty(expr).is_ref()
+            {
+                (span, hir_id, PREC_PREFIX)
+            } else {
+                (data.span, data.hir_id, data.position.precedence())
+            };
+            span_lint_hir_and_then(
+                cx,
+                EXPLICIT_AUTO_DEREF,
+                hir_id,
+                span,
+                "deref which would be done by auto-deref",
+                |diag| {
+                    let mut app = Applicability::MachineApplicable;
+                    let (snip, snip_is_macro) = snippet_with_context(cx, expr.span, span.ctxt(), "..", &mut app);
+                    let sugg =
+                        if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) {
+                            format!("({})", snip)
+                        } else {
+                            snip.into()
+                        };
+                    diag.span_suggestion(span, "try this", sugg, app);
+                },
+            );
+        },
+        State::ExplicitDerefField { .. } => {
+            span_lint_hir_and_then(
+                cx,
+                EXPLICIT_AUTO_DEREF,
+                data.hir_id,
+                data.span,
+                "deref which would be done by auto-deref",
+                |diag| {
+                    let mut app = Applicability::MachineApplicable;
+                    let snip = snippet_with_context(cx, expr.span, data.span.ctxt(), "..", &mut app).0;
+                    diag.span_suggestion(data.span, "try this", snip.into_owned(), app);
+                },
+            );
+        },
+        State::Borrow | State::Reborrow { .. } => (),
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/double_comparison.rs b/src/tools/clippy/clippy_lints/src/double_comparison.rs
deleted file mode 100644
index ee0440e52ff..00000000000
--- a/src/tools/clippy/clippy_lints/src/double_comparison.rs
+++ /dev/null
@@ -1,96 +0,0 @@
-//! Lint on unnecessary double comparisons. Some examples:
-
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::eq_expr_value;
-use clippy_utils::source::snippet_with_applicability;
-use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Span;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for double comparisons that could be simplified to a single expression.
-    ///
-    ///
-    /// ### Why is this bad?
-    /// Readability.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = 1;
-    /// # let y = 2;
-    /// if x == y || x < y {}
-    /// ```
-    ///
-    /// Use instead:
-    ///
-    /// ```rust
-    /// # let x = 1;
-    /// # let y = 2;
-    /// if x <= y {}
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub DOUBLE_COMPARISONS,
-    complexity,
-    "unnecessary double comparisons that can be simplified"
-}
-
-declare_lint_pass!(DoubleComparisons => [DOUBLE_COMPARISONS]);
-
-impl<'tcx> DoubleComparisons {
-    #[expect(clippy::similar_names)]
-    fn check_binop(cx: &LateContext<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, span: Span) {
-        let (lkind, llhs, lrhs, rkind, rlhs, rrhs) = match (&lhs.kind, &rhs.kind) {
-            (ExprKind::Binary(lb, llhs, lrhs), ExprKind::Binary(rb, rlhs, rrhs)) => {
-                (lb.node, llhs, lrhs, rb.node, rlhs, rrhs)
-            },
-            _ => return,
-        };
-        if !(eq_expr_value(cx, llhs, rlhs) && eq_expr_value(cx, lrhs, rrhs)) {
-            return;
-        }
-        macro_rules! lint_double_comparison {
-            ($op:tt) => {{
-                let mut applicability = Applicability::MachineApplicable;
-                let lhs_str = snippet_with_applicability(cx, llhs.span, "", &mut applicability);
-                let rhs_str = snippet_with_applicability(cx, lrhs.span, "", &mut applicability);
-                let sugg = format!("{} {} {}", lhs_str, stringify!($op), rhs_str);
-                span_lint_and_sugg(
-                    cx,
-                    DOUBLE_COMPARISONS,
-                    span,
-                    "this binary expression can be simplified",
-                    "try",
-                    sugg,
-                    applicability,
-                );
-            }};
-        }
-        #[rustfmt::skip]
-        match (op, lkind, rkind) {
-            (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Lt) | (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Eq) => {
-                lint_double_comparison!(<=);
-            },
-            (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Eq) => {
-                lint_double_comparison!(>=);
-            },
-            (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Lt) => {
-                lint_double_comparison!(!=);
-            },
-            (BinOpKind::And, BinOpKind::Le, BinOpKind::Ge) | (BinOpKind::And, BinOpKind::Ge, BinOpKind::Le) => {
-                lint_double_comparison!(==);
-            },
-            _ => (),
-        };
-    }
-}
-
-impl<'tcx> LateLintPass<'tcx> for DoubleComparisons {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if let ExprKind::Binary(ref kind, lhs, rhs) = expr.kind {
-            Self::check_binop(cx, kind.node, lhs, rhs, expr.span);
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/duration_subsec.rs b/src/tools/clippy/clippy_lints/src/duration_subsec.rs
deleted file mode 100644
index d85ace3a279..00000000000
--- a/src/tools/clippy/clippy_lints/src/duration_subsec.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-use clippy_utils::consts::{constant, Constant};
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_with_applicability;
-use clippy_utils::ty::is_type_diagnostic_item;
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Spanned;
-use rustc_span::sym;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for calculation of subsecond microseconds or milliseconds
-    /// from other `Duration` methods.
-    ///
-    /// ### Why is this bad?
-    /// It's more concise to call `Duration::subsec_micros()` or
-    /// `Duration::subsec_millis()` than to calculate them.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # use std::time::Duration;
-    /// # let duration = Duration::new(5, 0);
-    /// let micros = duration.subsec_nanos() / 1_000;
-    /// let millis = duration.subsec_nanos() / 1_000_000;
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// # use std::time::Duration;
-    /// # let duration = Duration::new(5, 0);
-    /// let micros = duration.subsec_micros();
-    /// let millis = duration.subsec_millis();
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub DURATION_SUBSEC,
-    complexity,
-    "checks for calculation of subsecond microseconds or milliseconds"
-}
-
-declare_lint_pass!(DurationSubsec => [DURATION_SUBSEC]);
-
-impl<'tcx> LateLintPass<'tcx> for DurationSubsec {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if_chain! {
-            if let ExprKind::Binary(Spanned { node: BinOpKind::Div, .. }, left, right) = expr.kind;
-            if let ExprKind::MethodCall(method_path, args, _) = left.kind;
-            if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(&args[0]).peel_refs(), sym::Duration);
-            if let Some((Constant::Int(divisor), _)) = constant(cx, cx.typeck_results(), right);
-            then {
-                let suggested_fn = match (method_path.ident.as_str(), divisor) {
-                    ("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis",
-                    ("subsec_nanos", 1_000) => "subsec_micros",
-                    _ => return,
-                };
-                let mut applicability = Applicability::MachineApplicable;
-                span_lint_and_sugg(
-                    cx,
-                    DURATION_SUBSEC,
-                    expr.span,
-                    &format!("calling `{}()` is more concise than this calculation", suggested_fn),
-                    "try",
-                    format!(
-                        "{}.{}()",
-                        snippet_with_applicability(cx, args[0].span, "_", &mut applicability),
-                        suggested_fn
-                    ),
-                    applicability,
-                );
-            }
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/enum_variants.rs b/src/tools/clippy/clippy_lints/src/enum_variants.rs
index 23b75104570..cd36f9fcd72 100644
--- a/src/tools/clippy/clippy_lints/src/enum_variants.rs
+++ b/src/tools/clippy/clippy_lints/src/enum_variants.rs
@@ -190,7 +190,7 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n
             .map(|e| *e.0)
             .collect();
     }
-    let (what, value) = match (pre.is_empty(), post.is_empty()) {
+    let (what, value) = match (have_no_extra_prefix(&pre), post.is_empty()) {
         (true, true) => return,
         (false, _) => ("pre", pre.join("")),
         (true, false) => {
@@ -213,6 +213,11 @@ fn check_variant(cx: &LateContext<'_>, threshold: u64, def: &EnumDef<'_>, item_n
 }
 
 #[must_use]
+fn have_no_extra_prefix(prefixes: &[&str]) -> bool {
+    prefixes.iter().all(|p| p == &"" || p == &"_")
+}
+
+#[must_use]
 fn to_camel_case(item_name: &str) -> String {
     let mut s = String::new();
     let mut up = true;
diff --git a/src/tools/clippy/clippy_lints/src/eq_op.rs b/src/tools/clippy/clippy_lints/src/eq_op.rs
deleted file mode 100644
index 2f4c90d07cf..00000000000
--- a/src/tools/clippy/clippy_lints/src/eq_op.rs
+++ /dev/null
@@ -1,319 +0,0 @@
-use clippy_utils::diagnostics::{multispan_sugg, span_lint, span_lint_and_then};
-use clippy_utils::get_enclosing_block;
-use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace};
-use clippy_utils::source::snippet;
-use clippy_utils::ty::{implements_trait, is_copy};
-use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, is_in_test_function};
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{def::Res, def_id::DefId, BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, ItemKind, QPath, TyKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::{self, Ty};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for equal operands to comparison, logical and
-    /// bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,
-    /// `||`, `&`, `|`, `^`, `-` and `/`).
-    ///
-    /// ### Why is this bad?
-    /// This is usually just a typo or a copy and paste error.
-    ///
-    /// ### Known problems
-    /// False negatives: We had some false positives regarding
-    /// calls (notably [racer](https://github.com/phildawes/racer) had one instance
-    /// of `x.pop() && x.pop()`), so we removed matching any function or method
-    /// calls. We may introduce a list of known pure functions in the future.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = 1;
-    /// if x + 1 == x + 1 {}
-    ///
-    /// // or
-    ///
-    /// # let a = 3;
-    /// # let b = 4;
-    /// assert_eq!(a, a);
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub EQ_OP,
-    correctness,
-    "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for arguments to `==` which have their address
-    /// taken to satisfy a bound
-    /// and suggests to dereference the other argument instead
-    ///
-    /// ### Why is this bad?
-    /// It is more idiomatic to dereference the other argument.
-    ///
-    /// ### Example
-    /// ```rust,ignore
-    /// &x == y
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust,ignore
-    /// x == *y
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub OP_REF,
-    style,
-    "taking a reference to satisfy the type constraints on `==`"
-}
-
-declare_lint_pass!(EqOp => [EQ_OP, OP_REF]);
-
-impl<'tcx> LateLintPass<'tcx> for EqOp {
-    #[expect(clippy::similar_names, clippy::too_many_lines)]
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        if_chain! {
-            if let Some((macro_call, macro_name)) = first_node_macro_backtrace(cx, e).find_map(|macro_call| {
-                let name = cx.tcx.item_name(macro_call.def_id);
-                matches!(name.as_str(), "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne")
-                    .then(|| (macro_call, name))
-            });
-            if let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn);
-            if eq_expr_value(cx, lhs, rhs);
-            if macro_call.is_local();
-            if !is_in_test_function(cx.tcx, e.hir_id);
-            then {
-                span_lint(
-                    cx,
-                    EQ_OP,
-                    lhs.span.to(rhs.span),
-                    &format!("identical args used in this `{}!` macro call", macro_name),
-                );
-            }
-        }
-        if let ExprKind::Binary(op, left, right) = e.kind {
-            if e.span.from_expansion() {
-                return;
-            }
-            let macro_with_not_op = |expr_kind: &ExprKind<'_>| {
-                if let ExprKind::Unary(_, expr) = *expr_kind {
-                    expr.span.from_expansion()
-                } else {
-                    false
-                }
-            };
-            if macro_with_not_op(&left.kind) || macro_with_not_op(&right.kind) {
-                return;
-            }
-            if is_useless_with_eq_exprs(op.node.into())
-                && eq_expr_value(cx, left, right)
-                && !is_in_test_function(cx.tcx, e.hir_id)
-            {
-                span_lint(
-                    cx,
-                    EQ_OP,
-                    e.span,
-                    &format!("equal expressions as operands to `{}`", op.node.as_str()),
-                );
-                return;
-            }
-            let (trait_id, requires_ref) = match op.node {
-                BinOpKind::Add => (cx.tcx.lang_items().add_trait(), false),
-                BinOpKind::Sub => (cx.tcx.lang_items().sub_trait(), false),
-                BinOpKind::Mul => (cx.tcx.lang_items().mul_trait(), false),
-                BinOpKind::Div => (cx.tcx.lang_items().div_trait(), false),
-                BinOpKind::Rem => (cx.tcx.lang_items().rem_trait(), false),
-                // don't lint short circuiting ops
-                BinOpKind::And | BinOpKind::Or => return,
-                BinOpKind::BitXor => (cx.tcx.lang_items().bitxor_trait(), false),
-                BinOpKind::BitAnd => (cx.tcx.lang_items().bitand_trait(), false),
-                BinOpKind::BitOr => (cx.tcx.lang_items().bitor_trait(), false),
-                BinOpKind::Shl => (cx.tcx.lang_items().shl_trait(), false),
-                BinOpKind::Shr => (cx.tcx.lang_items().shr_trait(), false),
-                BinOpKind::Ne | BinOpKind::Eq => (cx.tcx.lang_items().eq_trait(), true),
-                BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ge | BinOpKind::Gt => {
-                    (cx.tcx.lang_items().partial_ord_trait(), true)
-                },
-            };
-            if let Some(trait_id) = trait_id {
-                match (&left.kind, &right.kind) {
-                    // do not suggest to dereference literals
-                    (&ExprKind::Lit(..), _) | (_, &ExprKind::Lit(..)) => {},
-                    // &foo == &bar
-                    (&ExprKind::AddrOf(BorrowKind::Ref, _, l), &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => {
-                        let lty = cx.typeck_results().expr_ty(l);
-                        let rty = cx.typeck_results().expr_ty(r);
-                        let lcpy = is_copy(cx, lty);
-                        let rcpy = is_copy(cx, rty);
-                        if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
-                            if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
-                                || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
-                            {
-                                return; // Don't lint
-                            }
-                        }
-                        // either operator autorefs or both args are copyable
-                        if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) {
-                            span_lint_and_then(
-                                cx,
-                                OP_REF,
-                                e.span,
-                                "needlessly taken reference of both operands",
-                                |diag| {
-                                    let lsnip = snippet(cx, l.span, "...").to_string();
-                                    let rsnip = snippet(cx, r.span, "...").to_string();
-                                    multispan_sugg(
-                                        diag,
-                                        "use the values directly",
-                                        vec![(left.span, lsnip), (right.span, rsnip)],
-                                    );
-                                },
-                            );
-                        } else if lcpy
-                            && !rcpy
-                            && implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()])
-                        {
-                            span_lint_and_then(
-                                cx,
-                                OP_REF,
-                                e.span,
-                                "needlessly taken reference of left operand",
-                                |diag| {
-                                    let lsnip = snippet(cx, l.span, "...").to_string();
-                                    diag.span_suggestion(
-                                        left.span,
-                                        "use the left value directly",
-                                        lsnip,
-                                        Applicability::MaybeIncorrect, // FIXME #2597
-                                    );
-                                },
-                            );
-                        } else if !lcpy
-                            && rcpy
-                            && implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()])
-                        {
-                            span_lint_and_then(
-                                cx,
-                                OP_REF,
-                                e.span,
-                                "needlessly taken reference of right operand",
-                                |diag| {
-                                    let rsnip = snippet(cx, r.span, "...").to_string();
-                                    diag.span_suggestion(
-                                        right.span,
-                                        "use the right value directly",
-                                        rsnip,
-                                        Applicability::MaybeIncorrect, // FIXME #2597
-                                    );
-                                },
-                            );
-                        }
-                    },
-                    // &foo == bar
-                    (&ExprKind::AddrOf(BorrowKind::Ref, _, l), _) => {
-                        let lty = cx.typeck_results().expr_ty(l);
-                        if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
-                            let rty = cx.typeck_results().expr_ty(right);
-                            if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
-                                || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
-                            {
-                                return; // Don't lint
-                            }
-                        }
-                        let lcpy = is_copy(cx, lty);
-                        if (requires_ref || lcpy)
-                            && implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()])
-                        {
-                            span_lint_and_then(
-                                cx,
-                                OP_REF,
-                                e.span,
-                                "needlessly taken reference of left operand",
-                                |diag| {
-                                    let lsnip = snippet(cx, l.span, "...").to_string();
-                                    diag.span_suggestion(
-                                        left.span,
-                                        "use the left value directly",
-                                        lsnip,
-                                        Applicability::MaybeIncorrect, // FIXME #2597
-                                    );
-                                },
-                            );
-                        }
-                    },
-                    // foo == &bar
-                    (_, &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => {
-                        let rty = cx.typeck_results().expr_ty(r);
-                        if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
-                            let lty = cx.typeck_results().expr_ty(left);
-                            if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
-                                || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
-                            {
-                                return; // Don't lint
-                            }
-                        }
-                        let rcpy = is_copy(cx, rty);
-                        if (requires_ref || rcpy)
-                            && implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()])
-                        {
-                            span_lint_and_then(cx, OP_REF, e.span, "taken reference of right operand", |diag| {
-                                let rsnip = snippet(cx, r.span, "...").to_string();
-                                diag.span_suggestion(
-                                    right.span,
-                                    "use the right value directly",
-                                    rsnip,
-                                    Applicability::MaybeIncorrect, // FIXME #2597
-                                );
-                            });
-                        }
-                    },
-                    _ => {},
-                }
-            }
-        }
-    }
-}
-
-fn in_impl<'tcx>(
-    cx: &LateContext<'tcx>,
-    e: &'tcx Expr<'_>,
-    bin_op: DefId,
-) -> Option<(&'tcx rustc_hir::Ty<'tcx>, &'tcx rustc_hir::Ty<'tcx>)> {
-    if_chain! {
-        if let Some(block) = get_enclosing_block(cx, e.hir_id);
-        if let Some(impl_def_id) = cx.tcx.impl_of_method(block.hir_id.owner.to_def_id());
-        let item = cx.tcx.hir().expect_item(impl_def_id.expect_local());
-        if let ItemKind::Impl(item) = &item.kind;
-        if let Some(of_trait) = &item.of_trait;
-        if let Some(seg) = of_trait.path.segments.last();
-        if let Some(Res::Def(_, trait_id)) = seg.res;
-        if trait_id == bin_op;
-        if let Some(generic_args) = seg.args;
-        if let Some(GenericArg::Type(other_ty)) = generic_args.args.last();
-
-        then {
-            Some((item.self_ty, other_ty))
-        }
-        else {
-            None
-        }
-    }
-}
-
-fn are_equal<'tcx>(cx: &LateContext<'tcx>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool {
-    if_chain! {
-        if let ty::Adt(adt_def, _) = middle_ty.kind();
-        if let Some(local_did) = adt_def.did().as_local();
-        let item = cx.tcx.hir().expect_item(local_did);
-        let middle_ty_id = item.def_id.to_def_id();
-        if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
-        if let Res::Def(_, hir_ty_id) = path.res;
-
-        then {
-            hir_ty_id == middle_ty_id
-        }
-        else {
-            false
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/erasing_op.rs b/src/tools/clippy/clippy_lints/src/erasing_op.rs
deleted file mode 100644
index c1a84973c42..00000000000
--- a/src/tools/clippy/clippy_lints/src/erasing_op.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-use clippy_utils::consts::{constant_simple, Constant};
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::ty::same_type_and_consts;
-
-use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::TypeckResults;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for erasing operations, e.g., `x * 0`.
-    ///
-    /// ### Why is this bad?
-    /// The whole expression can be replaced by zero.
-    /// This is most likely not the intended outcome and should probably be
-    /// corrected
-    ///
-    /// ### Example
-    /// ```rust
-    /// let x = 1;
-    /// 0 / x;
-    /// 0 * x;
-    /// x & 0;
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub ERASING_OP,
-    correctness,
-    "using erasing operations, e.g., `x * 0` or `y & 0`"
-}
-
-declare_lint_pass!(ErasingOp => [ERASING_OP]);
-
-impl<'tcx> LateLintPass<'tcx> for ErasingOp {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        if e.span.from_expansion() {
-            return;
-        }
-        if let ExprKind::Binary(ref cmp, left, right) = e.kind {
-            let tck = cx.typeck_results();
-            match cmp.node {
-                BinOpKind::Mul | BinOpKind::BitAnd => {
-                    check(cx, tck, left, right, e);
-                    check(cx, tck, right, left, e);
-                },
-                BinOpKind::Div => check(cx, tck, left, right, e),
-                _ => (),
-            }
-        }
-    }
-}
-
-fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) -> bool {
-    let input_ty = tck.expr_ty(input).peel_refs();
-    let output_ty = tck.expr_ty(output).peel_refs();
-    !same_type_and_consts(input_ty, output_ty)
-}
-
-fn check<'tcx>(
-    cx: &LateContext<'tcx>,
-    tck: &TypeckResults<'tcx>,
-    op: &Expr<'tcx>,
-    other: &Expr<'tcx>,
-    parent: &Expr<'tcx>,
-) {
-    if constant_simple(cx, tck, op) == Some(Constant::Int(0)) {
-        if different_types(tck, other, parent) {
-            return;
-        }
-        span_lint(
-            cx,
-            ERASING_OP,
-            parent.span,
-            "this operation will always return zero. This is likely not the intended outcome",
-        );
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/escape.rs b/src/tools/clippy/clippy_lints/src/escape.rs
index 9d21dd71e0e..7a65b849a66 100644
--- a/src/tools/clippy/clippy_lints/src/escape.rs
+++ b/src/tools/clippy/clippy_lints/src/escape.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint;
+use clippy_utils::diagnostics::span_lint_hir;
 use clippy_utils::ty::contains_ty;
 use rustc_hir::intravisit;
 use rustc_hir::{self, AssocItemKind, Body, FnDecl, HirId, HirIdSet, Impl, ItemKind, Node};
@@ -118,9 +118,10 @@ impl<'tcx> LateLintPass<'tcx> for BoxedLocal {
         });
 
         for node in v.set {
-            span_lint(
+            span_lint_hir(
                 cx,
                 BOXED_LOCAL,
+                node,
                 cx.tcx.hir().span(node),
                 "local variable doesn't need to be boxed here",
             );
diff --git a/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs b/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs
deleted file mode 100644
index 98aee7592ae..00000000000
--- a/src/tools/clippy/clippy_lints/src/float_equality_without_abs.rs
+++ /dev/null
@@ -1,116 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::{match_def_path, paths, sugg};
-use if_chain::if_chain;
-use rustc_ast::util::parser::AssocOp;
-use rustc_errors::Applicability;
-use rustc_hir::def::{DefKind, Res};
-use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::source_map::Spanned;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for statements of the form `(a - b) < f32::EPSILON` or
-    /// `(a - b) < f64::EPSILON`. Notes the missing `.abs()`.
-    ///
-    /// ### Why is this bad?
-    /// The code without `.abs()` is more likely to have a bug.
-    ///
-    /// ### Known problems
-    /// If the user can ensure that b is larger than a, the `.abs()` is
-    /// technically unnecessary. However, it will make the code more robust and doesn't have any
-    /// large performance implications. If the abs call was deliberately left out for performance
-    /// reasons, it is probably better to state this explicitly in the code, which then can be done
-    /// with an allow.
-    ///
-    /// ### Example
-    /// ```rust
-    /// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
-    ///     (a - b) < f32::EPSILON
-    /// }
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
-    ///     (a - b).abs() < f32::EPSILON
-    /// }
-    /// ```
-    #[clippy::version = "1.48.0"]
-    pub FLOAT_EQUALITY_WITHOUT_ABS,
-    suspicious,
-    "float equality check without `.abs()`"
-}
-
-declare_lint_pass!(FloatEqualityWithoutAbs => [FLOAT_EQUALITY_WITHOUT_ABS]);
-
-impl<'tcx> LateLintPass<'tcx> for FloatEqualityWithoutAbs {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        let lhs;
-        let rhs;
-
-        // check if expr is a binary expression with a lt or gt operator
-        if let ExprKind::Binary(op, left, right) = expr.kind {
-            match op.node {
-                BinOpKind::Lt => {
-                    lhs = left;
-                    rhs = right;
-                },
-                BinOpKind::Gt => {
-                    lhs = right;
-                    rhs = left;
-                },
-                _ => return,
-            };
-        } else {
-            return;
-        }
-
-        if_chain! {
-
-            // left hand side is a subtraction
-            if let ExprKind::Binary(
-                Spanned {
-                    node: BinOpKind::Sub,
-                    ..
-                },
-                val_l,
-                val_r,
-            ) = lhs.kind;
-
-            // right hand side matches either f32::EPSILON or f64::EPSILON
-            if let ExprKind::Path(ref epsilon_path) = rhs.kind;
-            if let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id);
-            if match_def_path(cx, def_id, &paths::F32_EPSILON) || match_def_path(cx, def_id, &paths::F64_EPSILON);
-
-            // values of the subtractions on the left hand side are of the type float
-            let t_val_l = cx.typeck_results().expr_ty(val_l);
-            let t_val_r = cx.typeck_results().expr_ty(val_r);
-            if let ty::Float(_) = t_val_l.kind();
-            if let ty::Float(_) = t_val_r.kind();
-
-            then {
-                let sug_l = sugg::Sugg::hir(cx, val_l, "..");
-                let sug_r = sugg::Sugg::hir(cx, val_r, "..");
-                // format the suggestion
-                let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par());
-                // spans the lint
-                span_lint_and_then(
-                    cx,
-                    FLOAT_EQUALITY_WITHOUT_ABS,
-                    expr.span,
-                    "float equality check without `.abs()`",
-                    | diag | {
-                        diag.span_suggestion(
-                            lhs.span,
-                            "add `.abs()`",
-                            suggestion,
-                            Applicability::MaybeIncorrect,
-                        );
-                    }
-                );
-            }
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/implicit_return.rs b/src/tools/clippy/clippy_lints/src/implicit_return.rs
index 647947d5d30..a6610ade37e 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_return.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_return.rs
@@ -1,5 +1,5 @@
 use clippy_utils::{
-    diagnostics::span_lint_and_sugg,
+    diagnostics::span_lint_hir_and_then,
     get_async_fn_body, is_async_fn,
     source::{snippet_with_applicability, snippet_with_context, walk_span_to_context},
     visitors::expr_visitor_no_bodies,
@@ -43,31 +43,38 @@ declare_clippy_lint! {
 
 declare_lint_pass!(ImplicitReturn => [IMPLICIT_RETURN]);
 
-fn lint_return(cx: &LateContext<'_>, span: Span) {
+fn lint_return(cx: &LateContext<'_>, emission_place: HirId, span: Span) {
     let mut app = Applicability::MachineApplicable;
     let snip = snippet_with_applicability(cx, span, "..", &mut app);
-    span_lint_and_sugg(
+    span_lint_hir_and_then(
         cx,
         IMPLICIT_RETURN,
+        emission_place,
         span,
         "missing `return` statement",
-        "add `return` as shown",
-        format!("return {}", snip),
-        app,
+        |diag| {
+            diag.span_suggestion(span, "add `return` as shown", format!("return {}", snip), app);
+        },
     );
 }
 
-fn lint_break(cx: &LateContext<'_>, break_span: Span, expr_span: Span) {
+fn lint_break(cx: &LateContext<'_>, emission_place: HirId, break_span: Span, expr_span: Span) {
     let mut app = Applicability::MachineApplicable;
     let snip = snippet_with_context(cx, expr_span, break_span.ctxt(), "..", &mut app).0;
-    span_lint_and_sugg(
+    span_lint_hir_and_then(
         cx,
         IMPLICIT_RETURN,
+        emission_place,
         break_span,
         "missing `return` statement",
-        "change `break` to `return` as shown",
-        format!("return {}", snip),
-        app,
+        |diag| {
+            diag.span_suggestion(
+                break_span,
+                "change `break` to `return` as shown",
+                format!("return {}", snip),
+                app,
+            );
+        },
     );
 }
 
@@ -152,7 +159,7 @@ fn lint_implicit_returns(
                             // At this point sub_expr can be `None` in async functions which either diverge, or return
                             // the unit type.
                             if let Some(sub_expr) = sub_expr {
-                                lint_break(cx, e.span, sub_expr.span);
+                                lint_break(cx, e.hir_id, e.span, sub_expr.span);
                             }
                         } else {
                             // the break expression is from a macro call, add a return to the loop
@@ -166,10 +173,10 @@ fn lint_implicit_returns(
             if add_return {
                 #[expect(clippy::option_if_let_else)]
                 if let Some(span) = call_site_span {
-                    lint_return(cx, span);
+                    lint_return(cx, expr.hir_id, span);
                     LintLocation::Parent
                 } else {
-                    lint_return(cx, expr.span);
+                    lint_return(cx, expr.hir_id, expr.span);
                     LintLocation::Inner
                 }
             } else {
@@ -198,10 +205,10 @@ fn lint_implicit_returns(
         {
             #[expect(clippy::option_if_let_else)]
             if let Some(span) = call_site_span {
-                lint_return(cx, span);
+                lint_return(cx, expr.hir_id, span);
                 LintLocation::Parent
             } else {
-                lint_return(cx, expr.span);
+                lint_return(cx, expr.hir_id, expr.span);
                 LintLocation::Inner
             }
         },
diff --git a/src/tools/clippy/clippy_lints/src/integer_division.rs b/src/tools/clippy/clippy_lints/src/integer_division.rs
deleted file mode 100644
index 3effba56826..00000000000
--- a/src/tools/clippy/clippy_lints/src/integer_division.rs
+++ /dev/null
@@ -1,61 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use if_chain::if_chain;
-use rustc_hir as hir;
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for division of integers
-    ///
-    /// ### Why is this bad?
-    /// When outside of some very specific algorithms,
-    /// integer division is very often a mistake because it discards the
-    /// remainder.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let x = 3 / 2;
-    /// println!("{}", x);
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// let x = 3f32 / 2f32;
-    /// println!("{}", x);
-    /// ```
-    #[clippy::version = "1.37.0"]
-    pub INTEGER_DIVISION,
-    restriction,
-    "integer division may cause loss of precision"
-}
-
-declare_lint_pass!(IntegerDivision => [INTEGER_DIVISION]);
-
-impl<'tcx> LateLintPass<'tcx> for IntegerDivision {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if is_integer_division(cx, expr) {
-            span_lint_and_help(
-                cx,
-                INTEGER_DIVISION,
-                expr.span,
-                "integer division",
-                None,
-                "division of integers may cause loss of precision. consider using floats",
-            );
-        }
-    }
-}
-
-fn is_integer_division<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) -> bool {
-    if_chain! {
-        if let hir::ExprKind::Binary(binop, left, right) = &expr.kind;
-        if binop.node == hir::BinOpKind::Div;
-        then {
-            let (left_ty, right_ty) = (cx.typeck_results().expr_ty(left), cx.typeck_results().expr_ty(right));
-            return left_ty.is_integral() && right_ty.is_integral();
-        }
-    }
-
-    false
-}
diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
index 289755bfec6..984c5cd4e37 100644
--- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
+++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
@@ -24,7 +24,7 @@ declare_clippy_lint! {
     /// ```
     ///
     /// Use instead:
-    /// ```rust.ignore
+    /// ```rust,ignore
     /// pub static a = [0u32; 1_000_000];
     /// ```
     #[clippy::version = "1.44.0"]
diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs
index 26c540e2223..176787497eb 100644
--- a/src/tools/clippy/clippy_lints/src/let_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs
@@ -99,12 +99,13 @@ declare_clippy_lint! {
 
 declare_lint_pass!(LetUnderscore => [LET_UNDERSCORE_MUST_USE, LET_UNDERSCORE_LOCK, LET_UNDERSCORE_DROP]);
 
-const SYNC_GUARD_PATHS: [&[&str]; 5] = [
+const SYNC_GUARD_PATHS: [&[&str]; 6] = [
     &paths::MUTEX_GUARD,
     &paths::RWLOCK_READ_GUARD,
     &paths::RWLOCK_WRITE_GUARD,
-    &paths::PARKING_LOT_RAWMUTEX,
-    &paths::PARKING_LOT_RAWRWLOCK,
+    &paths::PARKING_LOT_MUTEX_GUARD,
+    &paths::PARKING_LOT_RWLOCK_READ_GUARD,
+    &paths::PARKING_LOT_RWLOCK_WRITE_GUARD,
 ];
 
 impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_all.rs b/src/tools/clippy/clippy_lints/src/lib.register_all.rs
index 8a2cfbff953..563ad891603 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_all.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_all.rs
@@ -3,12 +3,9 @@
 // Manual edits will be overwritten.
 
 store.register_group(true, "clippy::all", Some("clippy_all"), vec![
-    LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
     LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
     LintId::of(approx_const::APPROX_CONSTANT),
     LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
-    LintId::of(assign_ops::ASSIGN_OP_PATTERN),
-    LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
     LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
     LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
     LintId::of(attrs::DEPRECATED_CFG_ATTR),
@@ -18,8 +15,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
     LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
     LintId::of(await_holding_invalid::AWAIT_HOLDING_REFCELL_REF),
-    LintId::of(bit_mask::BAD_BIT_MASK),
-    LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
     LintId::of(blacklisted_name::BLACKLISTED_NAME),
     LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
     LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
@@ -43,6 +38,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(copies::IF_SAME_THEN_ELSE),
     LintId::of(crate_in_macro_def::CRATE_IN_MACRO_DEF),
     LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
+    LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY),
+    LintId::of(dereference::EXPLICIT_AUTO_DEREF),
     LintId::of(dereference::NEEDLESS_BORROW),
     LintId::of(derivable_impls::DERIVABLE_IMPLS),
     LintId::of(derive::DERIVE_HASH_XOR_EQ),
@@ -52,7 +49,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(disallowed_types::DISALLOWED_TYPES),
     LintId::of(doc::MISSING_SAFETY_DOC),
     LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
-    LintId::of(double_comparison::DOUBLE_COMPARISONS),
     LintId::of(double_parens::DOUBLE_PARENS),
     LintId::of(drop_forget_ref::DROP_COPY),
     LintId::of(drop_forget_ref::DROP_NON_DROP),
@@ -62,18 +58,13 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(drop_forget_ref::FORGET_REF),
     LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
     LintId::of(duplicate_mod::DUPLICATE_MOD),
-    LintId::of(duration_subsec::DURATION_SUBSEC),
     LintId::of(entry::MAP_ENTRY),
     LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
     LintId::of(enum_variants::ENUM_VARIANT_NAMES),
     LintId::of(enum_variants::MODULE_INCEPTION),
-    LintId::of(eq_op::EQ_OP),
-    LintId::of(eq_op::OP_REF),
-    LintId::of(erasing_op::ERASING_OP),
     LintId::of(escape::BOXED_LOCAL),
     LintId::of(eta_reduction::REDUNDANT_CLOSURE),
     LintId::of(explicit_write::EXPLICIT_WRITE),
-    LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
     LintId::of(float_literal::EXCESSIVE_PRECISION),
     LintId::of(format::USELESS_FORMAT),
     LintId::of(format_args::FORMAT_IN_FORMAT_ARGS),
@@ -93,7 +84,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(functions::RESULT_UNIT_ERR),
     LintId::of(functions::TOO_MANY_ARGUMENTS),
     LintId::of(get_first::GET_FIRST),
-    LintId::of(identity_op::IDENTITY_OP),
     LintId::of(if_let_mutex::IF_LET_MUTEX),
     LintId::of(indexing_slicing::OUT_OF_BOUNDS_INDEXING),
     LintId::of(infinite_iter::INFINITE_ITER),
@@ -118,6 +108,7 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(loops::FOR_KV_MAP),
     LintId::of(loops::FOR_LOOPS_OVER_FALLIBLES),
     LintId::of(loops::ITER_NEXT_LOOP),
+    LintId::of(loops::MANUAL_FIND),
     LintId::of(loops::MANUAL_FLATTEN),
     LintId::of(loops::MANUAL_MEMCPY),
     LintId::of(loops::MISSING_SPIN_LOOP),
@@ -134,6 +125,8 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(manual_async_fn::MANUAL_ASYNC_FN),
     LintId::of(manual_bits::MANUAL_BITS),
     LintId::of(manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE),
+    LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
+    LintId::of(manual_retain::MANUAL_RETAIN),
     LintId::of(manual_strip::MANUAL_STRIP),
     LintId::of(map_clone::MAP_CLONE),
     LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
@@ -220,9 +213,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(methods::WRONG_SELF_CONVENTION),
     LintId::of(methods::ZST_OFFSET),
     LintId::of(minmax::MIN_MAX),
-    LintId::of(misc::CMP_NAN),
-    LintId::of(misc::CMP_OWNED),
-    LintId::of(misc::MODULO_ONE),
     LintId::of(misc::SHORT_CIRCUIT_STATEMENT),
     LintId::of(misc::TOPLEVEL_REF_ARG),
     LintId::of(misc::ZERO_PTR),
@@ -256,6 +246,23 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
     LintId::of(octal_escapes::OCTAL_ESCAPES),
     LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
+    LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
+    LintId::of(operators::ASSIGN_OP_PATTERN),
+    LintId::of(operators::BAD_BIT_MASK),
+    LintId::of(operators::CMP_NAN),
+    LintId::of(operators::CMP_OWNED),
+    LintId::of(operators::DOUBLE_COMPARISONS),
+    LintId::of(operators::DURATION_SUBSEC),
+    LintId::of(operators::EQ_OP),
+    LintId::of(operators::ERASING_OP),
+    LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS),
+    LintId::of(operators::IDENTITY_OP),
+    LintId::of(operators::INEFFECTIVE_BIT_MASK),
+    LintId::of(operators::MISREFACTORED_ASSIGN_OP),
+    LintId::of(operators::MODULO_ONE),
+    LintId::of(operators::OP_REF),
+    LintId::of(operators::PTR_EQ),
+    LintId::of(operators::SELF_ASSIGNMENT),
     LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
     LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
     LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
@@ -264,7 +271,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(ptr::INVALID_NULL_PTR_USAGE),
     LintId::of(ptr::MUT_FROM_REF),
     LintId::of(ptr::PTR_ARG),
-    LintId::of(ptr_eq::PTR_EQ),
     LintId::of(ptr_offset_with_cast::PTR_OFFSET_WITH_CAST),
     LintId::of(question_mark::QUESTION_MARK),
     LintId::of(ranges::MANUAL_RANGE_CONTAINS),
@@ -282,7 +288,6 @@ store.register_group(true, "clippy::all", Some("clippy_all"), vec![
     LintId::of(repeat_once::REPEAT_ONCE),
     LintId::of(returns::LET_AND_RETURN),
     LintId::of(returns::NEEDLESS_RETURN),
-    LintId::of(self_assignment::SELF_ASSIGNMENT),
     LintId::of(self_named_constructors::SELF_NAMED_CONSTRUCTORS),
     LintId::of(serde_api::SERDE_API_MISUSE),
     LintId::of(single_component_path_imports::SINGLE_COMPONENT_PATH_IMPORTS),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs b/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs
index 4f1c3673f85..3784d3c68dc 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_complexity.rs
@@ -9,21 +9,21 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
     LintId::of(bytes_count_to_len::BYTES_COUNT_TO_LEN),
     LintId::of(casts::CHAR_LIT_AS_U8),
     LintId::of(casts::UNNECESSARY_CAST),
+    LintId::of(dereference::EXPLICIT_AUTO_DEREF),
     LintId::of(derivable_impls::DERIVABLE_IMPLS),
-    LintId::of(double_comparison::DOUBLE_COMPARISONS),
     LintId::of(double_parens::DOUBLE_PARENS),
-    LintId::of(duration_subsec::DURATION_SUBSEC),
     LintId::of(explicit_write::EXPLICIT_WRITE),
     LintId::of(format::USELESS_FORMAT),
     LintId::of(functions::TOO_MANY_ARGUMENTS),
-    LintId::of(identity_op::IDENTITY_OP),
     LintId::of(int_plus_one::INT_PLUS_ONE),
     LintId::of(lifetimes::EXTRA_UNUSED_LIFETIMES),
     LintId::of(lifetimes::NEEDLESS_LIFETIMES),
     LintId::of(loops::EXPLICIT_COUNTER_LOOP),
+    LintId::of(loops::MANUAL_FIND),
     LintId::of(loops::MANUAL_FLATTEN),
     LintId::of(loops::SINGLE_ELEMENT_LOOP),
     LintId::of(loops::WHILE_LET_LOOP),
+    LintId::of(manual_rem_euclid::MANUAL_REM_EUCLID),
     LintId::of(manual_strip::MANUAL_STRIP),
     LintId::of(map_unit_fn::OPTION_MAP_UNIT_FN),
     LintId::of(map_unit_fn::RESULT_MAP_UNIT_FN),
@@ -69,6 +69,9 @@ store.register_group(true, "clippy::complexity", Some("clippy_complexity"), vec!
     LintId::of(neg_cmp_op_on_partial_ord::NEG_CMP_OP_ON_PARTIAL_ORD),
     LintId::of(no_effect::NO_EFFECT),
     LintId::of(no_effect::UNNECESSARY_OPERATION),
+    LintId::of(operators::DOUBLE_COMPARISONS),
+    LintId::of(operators::DURATION_SUBSEC),
+    LintId::of(operators::IDENTITY_OP),
     LintId::of(overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL),
     LintId::of(partialeq_ne_impl::PARTIALEQ_NE_IMPL),
     LintId::of(precedence::PRECEDENCE),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_correctness.rs b/src/tools/clippy/clippy_lints/src/lib.register_correctness.rs
index 92a3a0aabf1..7d5e65cb27a 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_correctness.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_correctness.rs
@@ -3,14 +3,11 @@
 // Manual edits will be overwritten.
 
 store.register_group(true, "clippy::correctness", Some("clippy_correctness"), vec![
-    LintId::of(absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS),
     LintId::of(approx_const::APPROX_CONSTANT),
     LintId::of(async_yields_async::ASYNC_YIELDS_ASYNC),
     LintId::of(attrs::DEPRECATED_SEMVER),
     LintId::of(attrs::MISMATCHED_TARGET_OS),
     LintId::of(attrs::USELESS_ATTRIBUTE),
-    LintId::of(bit_mask::BAD_BIT_MASK),
-    LintId::of(bit_mask::INEFFECTIVE_BIT_MASK),
     LintId::of(booleans::LOGIC_BUG),
     LintId::of(casts::CAST_REF_TO_MUT),
     LintId::of(casts::CAST_SLICE_DIFFERENT_SIZES),
@@ -24,8 +21,6 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
     LintId::of(drop_forget_ref::FORGET_REF),
     LintId::of(drop_forget_ref::UNDROPPED_MANUALLY_DROPS),
     LintId::of(enum_clike::ENUM_CLIKE_UNPORTABLE_VARIANT),
-    LintId::of(eq_op::EQ_OP),
-    LintId::of(erasing_op::ERASING_OP),
     LintId::of(format_impl::RECURSIVE_FORMAT_IMPL),
     LintId::of(formatting::POSSIBLE_MISSING_COMMA),
     LintId::of(functions::NOT_UNSAFE_PTR_ARG_DEREF),
@@ -47,17 +42,22 @@ store.register_group(true, "clippy::correctness", Some("clippy_correctness"), ve
     LintId::of(methods::UNINIT_ASSUMED_INIT),
     LintId::of(methods::ZST_OFFSET),
     LintId::of(minmax::MIN_MAX),
-    LintId::of(misc::CMP_NAN),
-    LintId::of(misc::MODULO_ONE),
     LintId::of(non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS),
     LintId::of(open_options::NONSENSICAL_OPEN_OPTIONS),
+    LintId::of(operators::ABSURD_EXTREME_COMPARISONS),
+    LintId::of(operators::BAD_BIT_MASK),
+    LintId::of(operators::CMP_NAN),
+    LintId::of(operators::EQ_OP),
+    LintId::of(operators::ERASING_OP),
+    LintId::of(operators::INEFFECTIVE_BIT_MASK),
+    LintId::of(operators::MODULO_ONE),
+    LintId::of(operators::SELF_ASSIGNMENT),
     LintId::of(option_env_unwrap::OPTION_ENV_UNWRAP),
     LintId::of(ptr::INVALID_NULL_PTR_USAGE),
     LintId::of(ptr::MUT_FROM_REF),
     LintId::of(ranges::REVERSED_EMPTY_RANGES),
     LintId::of(read_zero_byte_vec::READ_ZERO_BYTE_VEC),
     LintId::of(regex::INVALID_REGEX),
-    LintId::of(self_assignment::SELF_ASSIGNMENT),
     LintId::of(serde_api::SERDE_API_MISUSE),
     LintId::of(size_of_in_element_count::SIZE_OF_IN_ELEMENT_COUNT),
     LintId::of(swap::ALMOST_SWAPPED),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_internal.rs b/src/tools/clippy/clippy_lints/src/lib.register_internal.rs
index 4778f4fdfa7..be63646a12f 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_internal.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_internal.rs
@@ -6,6 +6,7 @@ store.register_group(true, "clippy::internal", Some("clippy_internal"), vec![
     LintId::of(utils::internal_lints::CLIPPY_LINTS_INTERNAL),
     LintId::of(utils::internal_lints::COLLAPSIBLE_SPAN_LINT_CALLS),
     LintId::of(utils::internal_lints::COMPILER_LINT_FUNCTIONS),
+    LintId::of(utils::internal_lints::DEFAULT_DEPRECATION_REASON),
     LintId::of(utils::internal_lints::DEFAULT_LINT),
     LintId::of(utils::internal_lints::IF_CHAIN_STYLE),
     LintId::of(utils::internal_lints::INTERNING_DEFINED_SYMBOL),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs
index 8ad984c68b8..d3c75f8b519 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_lints.rs
@@ -10,6 +10,8 @@ store.register_lints(&[
     #[cfg(feature = "internal")]
     utils::internal_lints::COMPILER_LINT_FUNCTIONS,
     #[cfg(feature = "internal")]
+    utils::internal_lints::DEFAULT_DEPRECATION_REASON,
+    #[cfg(feature = "internal")]
     utils::internal_lints::DEFAULT_LINT,
     #[cfg(feature = "internal")]
     utils::internal_lints::IF_CHAIN_STYLE,
@@ -33,7 +35,6 @@ store.register_lints(&[
     utils::internal_lints::PRODUCE_ICE,
     #[cfg(feature = "internal")]
     utils::internal_lints::UNNECESSARY_SYMBOL_STR,
-    absurd_extreme_comparisons::ABSURD_EXTREME_COMPARISONS,
     almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE,
     approx_const::APPROX_CONSTANT,
     as_conversions::AS_CONVERSIONS,
@@ -41,8 +42,6 @@ store.register_lints(&[
     asm_syntax::INLINE_ASM_X86_ATT_SYNTAX,
     asm_syntax::INLINE_ASM_X86_INTEL_SYNTAX,
     assertions_on_constants::ASSERTIONS_ON_CONSTANTS,
-    assign_ops::ASSIGN_OP_PATTERN,
-    assign_ops::MISREFACTORED_ASSIGN_OP,
     async_yields_async::ASYNC_YIELDS_ASYNC,
     attrs::ALLOW_ATTRIBUTES_WITHOUT_REASON,
     attrs::BLANKET_CLIPPY_RESTRICTION_LINTS,
@@ -55,9 +54,6 @@ store.register_lints(&[
     await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE,
     await_holding_invalid::AWAIT_HOLDING_LOCK,
     await_holding_invalid::AWAIT_HOLDING_REFCELL_REF,
-    bit_mask::BAD_BIT_MASK,
-    bit_mask::INEFFECTIVE_BIT_MASK,
-    bit_mask::VERBOSE_BIT_MASK,
     blacklisted_name::BLACKLISTED_NAME,
     blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS,
     bool_assert_comparison::BOOL_ASSERT_COMPARISON,
@@ -105,8 +101,10 @@ store.register_lints(&[
     dbg_macro::DBG_MACRO,
     default::DEFAULT_TRAIT_ACCESS,
     default::FIELD_REASSIGN_WITH_DEFAULT,
+    default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY,
     default_numeric_fallback::DEFAULT_NUMERIC_FALLBACK,
     default_union_representation::DEFAULT_UNION_REPRESENTATION,
+    dereference::EXPLICIT_AUTO_DEREF,
     dereference::EXPLICIT_DEREF_METHODS,
     dereference::NEEDLESS_BORROW,
     dereference::REF_BINDING_TO_REFERENCE,
@@ -125,7 +123,6 @@ store.register_lints(&[
     doc::MISSING_SAFETY_DOC,
     doc::NEEDLESS_DOCTEST_MAIN,
     doc_link_with_quotes::DOC_LINK_WITH_QUOTES,
-    double_comparison::DOUBLE_COMPARISONS,
     double_parens::DOUBLE_PARENS,
     drop_forget_ref::DROP_COPY,
     drop_forget_ref::DROP_NON_DROP,
@@ -135,7 +132,6 @@ store.register_lints(&[
     drop_forget_ref::FORGET_REF,
     drop_forget_ref::UNDROPPED_MANUALLY_DROPS,
     duplicate_mod::DUPLICATE_MOD,
-    duration_subsec::DURATION_SUBSEC,
     else_if_without_else::ELSE_IF_WITHOUT_ELSE,
     empty_drop::EMPTY_DROP,
     empty_enum::EMPTY_ENUM,
@@ -145,10 +141,7 @@ store.register_lints(&[
     enum_variants::ENUM_VARIANT_NAMES,
     enum_variants::MODULE_INCEPTION,
     enum_variants::MODULE_NAME_REPETITIONS,
-    eq_op::EQ_OP,
-    eq_op::OP_REF,
     equatable_if_let::EQUATABLE_IF_LET,
-    erasing_op::ERASING_OP,
     escape::BOXED_LOCAL,
     eta_reduction::REDUNDANT_CLOSURE,
     eta_reduction::REDUNDANT_CLOSURE_FOR_METHOD_CALLS,
@@ -159,7 +152,6 @@ store.register_lints(&[
     exit::EXIT,
     explicit_write::EXPLICIT_WRITE,
     fallible_impl_from::FALLIBLE_IMPL_FROM,
-    float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS,
     float_literal::EXCESSIVE_PRECISION,
     float_literal::LOSSY_FLOAT_LITERAL,
     floating_point_arithmetic::IMPRECISE_FLOPS,
@@ -185,7 +177,6 @@ store.register_lints(&[
     functions::TOO_MANY_LINES,
     future_not_send::FUTURE_NOT_SEND,
     get_first::GET_FIRST,
-    identity_op::IDENTITY_OP,
     if_let_mutex::IF_LET_MUTEX,
     if_not_else::IF_NOT_ELSE,
     if_then_some_else_none::IF_THEN_SOME_ELSE_NONE,
@@ -204,7 +195,6 @@ store.register_lints(&[
     init_numbered_fields::INIT_NUMBERED_FIELDS,
     inline_fn_without_body::INLINE_FN_WITHOUT_BODY,
     int_plus_one::INT_PLUS_ONE,
-    integer_division::INTEGER_DIVISION,
     invalid_upcast_comparisons::INVALID_UPCAST_COMPARISONS,
     items_after_statements::ITEMS_AFTER_STATEMENTS,
     iter_not_returning_iterator::ITER_NOT_RETURNING_ITERATOR,
@@ -234,6 +224,7 @@ store.register_lints(&[
     loops::FOR_KV_MAP,
     loops::FOR_LOOPS_OVER_FALLIBLES,
     loops::ITER_NEXT_LOOP,
+    loops::MANUAL_FIND,
     loops::MANUAL_FLATTEN,
     loops::MANUAL_MEMCPY,
     loops::MISSING_SPIN_LOOP,
@@ -253,6 +244,8 @@ store.register_lints(&[
     manual_bits::MANUAL_BITS,
     manual_non_exhaustive::MANUAL_NON_EXHAUSTIVE,
     manual_ok_or::MANUAL_OK_OR,
+    manual_rem_euclid::MANUAL_REM_EUCLID,
+    manual_retain::MANUAL_RETAIN,
     manual_strip::MANUAL_STRIP,
     map_clone::MAP_CLONE,
     map_err_ignore::MAP_ERR_IGNORE,
@@ -364,11 +357,6 @@ store.register_lints(&[
     methods::WRONG_SELF_CONVENTION,
     methods::ZST_OFFSET,
     minmax::MIN_MAX,
-    misc::CMP_NAN,
-    misc::CMP_OWNED,
-    misc::FLOAT_CMP,
-    misc::FLOAT_CMP_CONST,
-    misc::MODULO_ONE,
     misc::SHORT_CIRCUIT_STATEMENT,
     misc::TOPLEVEL_REF_ARG,
     misc::USED_UNDERSCORE_BINDING,
@@ -392,7 +380,6 @@ store.register_lints(&[
     mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION,
     module_style::MOD_MODULE_FILES,
     module_style::SELF_NAMED_MODULE_FILES,
-    modulo_arithmetic::MODULO_ARITHMETIC,
     mut_key::MUTABLE_KEY_TYPE,
     mut_mut::MUT_MUT,
     mut_mutex_lock::MUT_MUTEX_LOCK,
@@ -401,7 +388,6 @@ store.register_lints(&[
     mutex_atomic::MUTEX_ATOMIC,
     mutex_atomic::MUTEX_INTEGER,
     needless_arbitrary_self_type::NEEDLESS_ARBITRARY_SELF_TYPE,
-    needless_bitwise_bool::NEEDLESS_BITWISE_BOOL,
     needless_bool::BOOL_COMPARISON,
     needless_bool::NEEDLESS_BOOL,
     needless_borrowed_ref::NEEDLESS_BORROWED_REFERENCE,
@@ -426,11 +412,34 @@ store.register_lints(&[
     non_octal_unix_permissions::NON_OCTAL_UNIX_PERMISSIONS,
     non_send_fields_in_send_ty::NON_SEND_FIELDS_IN_SEND_TY,
     nonstandard_macro_braces::NONSTANDARD_MACRO_BRACES,
-    numeric_arithmetic::FLOAT_ARITHMETIC,
-    numeric_arithmetic::INTEGER_ARITHMETIC,
     octal_escapes::OCTAL_ESCAPES,
     only_used_in_recursion::ONLY_USED_IN_RECURSION,
     open_options::NONSENSICAL_OPEN_OPTIONS,
+    operators::ABSURD_EXTREME_COMPARISONS,
+    operators::ASSIGN_OP_PATTERN,
+    operators::BAD_BIT_MASK,
+    operators::CMP_NAN,
+    operators::CMP_OWNED,
+    operators::DOUBLE_COMPARISONS,
+    operators::DURATION_SUBSEC,
+    operators::EQ_OP,
+    operators::ERASING_OP,
+    operators::FLOAT_ARITHMETIC,
+    operators::FLOAT_CMP,
+    operators::FLOAT_CMP_CONST,
+    operators::FLOAT_EQUALITY_WITHOUT_ABS,
+    operators::IDENTITY_OP,
+    operators::INEFFECTIVE_BIT_MASK,
+    operators::INTEGER_ARITHMETIC,
+    operators::INTEGER_DIVISION,
+    operators::MISREFACTORED_ASSIGN_OP,
+    operators::MODULO_ARITHMETIC,
+    operators::MODULO_ONE,
+    operators::NEEDLESS_BITWISE_BOOL,
+    operators::OP_REF,
+    operators::PTR_EQ,
+    operators::SELF_ASSIGNMENT,
+    operators::VERBOSE_BIT_MASK,
     option_env_unwrap::OPTION_ENV_UNWRAP,
     option_if_let_else::OPTION_IF_LET_ELSE,
     overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL,
@@ -449,7 +458,6 @@ store.register_lints(&[
     ptr::INVALID_NULL_PTR_USAGE,
     ptr::MUT_FROM_REF,
     ptr::PTR_ARG,
-    ptr_eq::PTR_EQ,
     ptr_offset_with_cast::PTR_OFFSET_WITH_CAST,
     pub_use::PUB_USE,
     question_mark::QUESTION_MARK,
@@ -477,7 +485,6 @@ store.register_lints(&[
     returns::LET_AND_RETURN,
     returns::NEEDLESS_RETURN,
     same_name_method::SAME_NAME_METHOD,
-    self_assignment::SELF_ASSIGNMENT,
     self_named_constructors::SELF_NAMED_CONSTRUCTORS,
     semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED,
     serde_api::SERDE_API_MISUSE,
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs
index 48de92ae945..a1b54665814 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_pedantic.rs
@@ -4,7 +4,6 @@
 
 store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
     LintId::of(attrs::INLINE_ALWAYS),
-    LintId::of(bit_mask::VERBOSE_BIT_MASK),
     LintId::of(borrow_as_ptr::BORROW_AS_PTR),
     LintId::of(bytecount::NAIVE_BYTECOUNT),
     LintId::of(case_sensitive_file_extension_comparisons::CASE_SENSITIVE_FILE_EXTENSION_COMPARISONS),
@@ -65,17 +64,18 @@ store.register_group(true, "clippy::pedantic", Some("clippy_pedantic"), vec![
     LintId::of(methods::INEFFICIENT_TO_STRING),
     LintId::of(methods::MAP_UNWRAP_OR),
     LintId::of(methods::UNNECESSARY_JOIN),
-    LintId::of(misc::FLOAT_CMP),
     LintId::of(misc::USED_UNDERSCORE_BINDING),
     LintId::of(mismatching_type_param_order::MISMATCHING_TYPE_PARAM_ORDER),
     LintId::of(mut_mut::MUT_MUT),
-    LintId::of(needless_bitwise_bool::NEEDLESS_BITWISE_BOOL),
     LintId::of(needless_continue::NEEDLESS_CONTINUE),
     LintId::of(needless_for_each::NEEDLESS_FOR_EACH),
     LintId::of(needless_pass_by_value::NEEDLESS_PASS_BY_VALUE),
     LintId::of(no_effect::NO_EFFECT_UNDERSCORE_BINDING),
     LintId::of(non_expressive_names::MANY_SINGLE_CHAR_NAMES),
     LintId::of(non_expressive_names::SIMILAR_NAMES),
+    LintId::of(operators::FLOAT_CMP),
+    LintId::of(operators::NEEDLESS_BITWISE_BOOL),
+    LintId::of(operators::VERBOSE_BIT_MASK),
     LintId::of(pass_by_ref_or_value::LARGE_TYPES_PASSED_BY_VALUE),
     LintId::of(pass_by_ref_or_value::TRIVIALLY_COPY_PASS_BY_REF),
     LintId::of(ranges::RANGE_MINUS_ONE),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_perf.rs b/src/tools/clippy/clippy_lints/src/lib.register_perf.rs
index 82431863e6c..6bf519c24e8 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_perf.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_perf.rs
@@ -13,6 +13,7 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
     LintId::of(loops::MANUAL_MEMCPY),
     LintId::of(loops::MISSING_SPIN_LOOP),
     LintId::of(loops::NEEDLESS_COLLECT),
+    LintId::of(manual_retain::MANUAL_RETAIN),
     LintId::of(methods::EXPECT_FUN_CALL),
     LintId::of(methods::EXTEND_WITH_DRAIN),
     LintId::of(methods::ITER_NTH),
@@ -21,7 +22,7 @@ store.register_group(true, "clippy::perf", Some("clippy_perf"), vec![
     LintId::of(methods::OR_FUN_CALL),
     LintId::of(methods::SINGLE_CHAR_PATTERN),
     LintId::of(methods::UNNECESSARY_TO_OWNED),
-    LintId::of(misc::CMP_OWNED),
+    LintId::of(operators::CMP_OWNED),
     LintId::of(redundant_clone::REDUNDANT_CLONE),
     LintId::of(slow_vector_initialization::SLOW_VECTOR_INITIALIZATION),
     LintId::of(types::BOX_COLLECTION),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs b/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs
index 3695012f552..970e9db4772 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_restriction.rs
@@ -25,7 +25,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
     LintId::of(implicit_return::IMPLICIT_RETURN),
     LintId::of(indexing_slicing::INDEXING_SLICING),
     LintId::of(inherent_impl::MULTIPLE_INHERENT_IMPL),
-    LintId::of(integer_division::INTEGER_DIVISION),
     LintId::of(large_include_file::LARGE_INCLUDE_FILE),
     LintId::of(let_underscore::LET_UNDERSCORE_MUST_USE),
     LintId::of(literal_representation::DECIMAL_LITERAL_REPRESENTATION),
@@ -39,7 +38,6 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
     LintId::of(methods::FILETYPE_IS_FILE),
     LintId::of(methods::GET_UNWRAP),
     LintId::of(methods::UNWRAP_USED),
-    LintId::of(misc::FLOAT_CMP_CONST),
     LintId::of(misc_early::SEPARATED_LITERAL_SUFFIX),
     LintId::of(misc_early::UNNEEDED_FIELD_PATTERN),
     LintId::of(misc_early::UNSEPARATED_LITERAL_SUFFIX),
@@ -49,9 +47,11 @@ store.register_group(true, "clippy::restriction", Some("clippy_restriction"), ve
     LintId::of(mixed_read_write_in_expression::MIXED_READ_WRITE_IN_EXPRESSION),
     LintId::of(module_style::MOD_MODULE_FILES),
     LintId::of(module_style::SELF_NAMED_MODULE_FILES),
-    LintId::of(modulo_arithmetic::MODULO_ARITHMETIC),
-    LintId::of(numeric_arithmetic::FLOAT_ARITHMETIC),
-    LintId::of(numeric_arithmetic::INTEGER_ARITHMETIC),
+    LintId::of(operators::FLOAT_ARITHMETIC),
+    LintId::of(operators::FLOAT_CMP_CONST),
+    LintId::of(operators::INTEGER_ARITHMETIC),
+    LintId::of(operators::INTEGER_DIVISION),
+    LintId::of(operators::MODULO_ARITHMETIC),
     LintId::of(panic_in_result_fn::PANIC_IN_RESULT_FN),
     LintId::of(panic_unimplemented::PANIC),
     LintId::of(panic_unimplemented::TODO),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_style.rs b/src/tools/clippy/clippy_lints/src/lib.register_style.rs
index b6992ae0ad2..15a1bc569af 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_style.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_style.rs
@@ -4,7 +4,6 @@
 
 store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(assertions_on_constants::ASSERTIONS_ON_CONSTANTS),
-    LintId::of(assign_ops::ASSIGN_OP_PATTERN),
     LintId::of(blacklisted_name::BLACKLISTED_NAME),
     LintId::of(blocks_in_if_conditions::BLOCKS_IN_IF_CONDITIONS),
     LintId::of(bool_assert_comparison::BOOL_ASSERT_COMPARISON),
@@ -14,6 +13,7 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(collapsible_if::COLLAPSIBLE_IF),
     LintId::of(comparison_chain::COMPARISON_CHAIN),
     LintId::of(default::FIELD_REASSIGN_WITH_DEFAULT),
+    LintId::of(default_instead_of_iter_empty::DEFAULT_INSTEAD_OF_ITER_EMPTY),
     LintId::of(dereference::NEEDLESS_BORROW),
     LintId::of(derive::DERIVE_PARTIAL_EQ_WITHOUT_EQ),
     LintId::of(disallowed_methods::DISALLOWED_METHODS),
@@ -22,7 +22,6 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(doc::NEEDLESS_DOCTEST_MAIN),
     LintId::of(enum_variants::ENUM_VARIANT_NAMES),
     LintId::of(enum_variants::MODULE_INCEPTION),
-    LintId::of(eq_op::OP_REF),
     LintId::of(eta_reduction::REDUNDANT_CLOSURE),
     LintId::of(float_literal::EXCESSIVE_PRECISION),
     LintId::of(from_over_into::FROM_OVER_INTO),
@@ -97,9 +96,11 @@ store.register_group(true, "clippy::style", Some("clippy_style"), vec![
     LintId::of(non_copy_const::BORROW_INTERIOR_MUTABLE_CONST),
     LintId::of(non_copy_const::DECLARE_INTERIOR_MUTABLE_CONST),
     LintId::of(non_expressive_names::JUST_UNDERSCORES_AND_DIGITS),
+    LintId::of(operators::ASSIGN_OP_PATTERN),
+    LintId::of(operators::OP_REF),
+    LintId::of(operators::PTR_EQ),
     LintId::of(ptr::CMP_NULL),
     LintId::of(ptr::PTR_ARG),
-    LintId::of(ptr_eq::PTR_EQ),
     LintId::of(question_mark::QUESTION_MARK),
     LintId::of(ranges::MANUAL_RANGE_CONTAINS),
     LintId::of(redundant_field_names::REDUNDANT_FIELD_NAMES),
diff --git a/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs b/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs
index 7b13713c36e..f7558f87098 100644
--- a/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.register_suspicious.rs
@@ -4,7 +4,6 @@
 
 store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec![
     LintId::of(almost_complete_letter_range::ALMOST_COMPLETE_LETTER_RANGE),
-    LintId::of(assign_ops::MISREFACTORED_ASSIGN_OP),
     LintId::of(attrs::BLANKET_CLIPPY_RESTRICTION_LINTS),
     LintId::of(await_holding_invalid::AWAIT_HOLDING_INVALID_TYPE),
     LintId::of(await_holding_invalid::AWAIT_HOLDING_LOCK),
@@ -16,7 +15,6 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
     LintId::of(drop_forget_ref::DROP_NON_DROP),
     LintId::of(drop_forget_ref::FORGET_NON_DROP),
     LintId::of(duplicate_mod::DUPLICATE_MOD),
-    LintId::of(float_equality_without_abs::FLOAT_EQUALITY_WITHOUT_ABS),
     LintId::of(format_impl::PRINT_IN_FORMAT_IMPL),
     LintId::of(formatting::SUSPICIOUS_ASSIGNMENT_FORMATTING),
     LintId::of(formatting::SUSPICIOUS_ELSE_FORMATTING),
@@ -29,6 +27,8 @@ store.register_group(true, "clippy::suspicious", Some("clippy_suspicious"), vec!
     LintId::of(methods::SUSPICIOUS_MAP),
     LintId::of(mut_key::MUTABLE_KEY_TYPE),
     LintId::of(octal_escapes::OCTAL_ESCAPES),
+    LintId::of(operators::FLOAT_EQUALITY_WITHOUT_ABS),
+    LintId::of(operators::MISREFACTORED_ASSIGN_OP),
     LintId::of(rc_clone_in_vec_init::RC_CLONE_IN_VEC_INIT),
     LintId::of(suspicious_trait_impl::SUSPICIOUS_ARITHMETIC_IMPL),
     LintId::of(suspicious_trait_impl::SUSPICIOUS_OP_ASSIGN_IMPL),
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 84898eae05a..172fdf8c852 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -7,6 +7,7 @@
 #![feature(let_chains)]
 #![feature(let_else)]
 #![feature(lint_reasons)]
+#![feature(never_type)]
 #![feature(once_cell)]
 #![feature(rustc_private)]
 #![feature(stmt_expr_attributes)]
@@ -52,6 +53,7 @@ extern crate clippy_utils;
 use clippy_utils::parse_msrv;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_lint::LintId;
+use rustc_semver::RustcVersion;
 use rustc_session::Session;
 
 /// Macro used to declare a Clippy lint.
@@ -159,25 +161,22 @@ macro_rules! declare_clippy_lint {
 }
 
 #[cfg(feature = "internal")]
-mod deprecated_lints;
+pub mod deprecated_lints;
 #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))]
 mod utils;
 
 mod renamed_lints;
 
 // begin lints modules, do not remove this comment, it’s used in `update_lints`
-mod absurd_extreme_comparisons;
 mod almost_complete_letter_range;
 mod approx_const;
 mod as_conversions;
 mod as_underscore;
 mod asm_syntax;
 mod assertions_on_constants;
-mod assign_ops;
 mod async_yields_async;
 mod attrs;
 mod await_holding_invalid;
-mod bit_mask;
 mod blacklisted_name;
 mod blocks_in_if_conditions;
 mod bool_assert_comparison;
@@ -199,6 +198,7 @@ mod crate_in_macro_def;
 mod create_dir;
 mod dbg_macro;
 mod default;
+mod default_instead_of_iter_empty;
 mod default_numeric_fallback;
 mod default_union_representation;
 mod dereference;
@@ -209,11 +209,9 @@ mod disallowed_script_idents;
 mod disallowed_types;
 mod doc;
 mod doc_link_with_quotes;
-mod double_comparison;
 mod double_parens;
 mod drop_forget_ref;
 mod duplicate_mod;
-mod duration_subsec;
 mod else_if_without_else;
 mod empty_drop;
 mod empty_enum;
@@ -221,9 +219,7 @@ mod empty_structs_with_brackets;
 mod entry;
 mod enum_clike;
 mod enum_variants;
-mod eq_op;
 mod equatable_if_let;
-mod erasing_op;
 mod escape;
 mod eta_reduction;
 mod excessive_bools;
@@ -231,7 +227,6 @@ mod exhaustive_items;
 mod exit;
 mod explicit_write;
 mod fallible_impl_from;
-mod float_equality_without_abs;
 mod float_literal;
 mod floating_point_arithmetic;
 mod format;
@@ -244,7 +239,6 @@ mod from_str_radix_10;
 mod functions;
 mod future_not_send;
 mod get_first;
-mod identity_op;
 mod if_let_mutex;
 mod if_not_else;
 mod if_then_some_else_none;
@@ -260,7 +254,6 @@ mod inherent_to_string;
 mod init_numbered_fields;
 mod inline_fn_without_body;
 mod int_plus_one;
-mod integer_division;
 mod invalid_upcast_comparisons;
 mod items_after_statements;
 mod iter_not_returning_iterator;
@@ -281,6 +274,8 @@ mod manual_async_fn;
 mod manual_bits;
 mod manual_non_exhaustive;
 mod manual_ok_or;
+mod manual_rem_euclid;
+mod manual_retain;
 mod manual_strip;
 mod map_clone;
 mod map_err_ignore;
@@ -300,7 +295,6 @@ mod missing_enforced_import_rename;
 mod missing_inline;
 mod mixed_read_write_in_expression;
 mod module_style;
-mod modulo_arithmetic;
 mod mut_key;
 mod mut_mut;
 mod mut_mutex_lock;
@@ -308,7 +302,6 @@ mod mut_reference;
 mod mutable_debug_assertion;
 mod mutex_atomic;
 mod needless_arbitrary_self_type;
-mod needless_bitwise_bool;
 mod needless_bool;
 mod needless_borrowed_ref;
 mod needless_continue;
@@ -327,10 +320,10 @@ mod non_expressive_names;
 mod non_octal_unix_permissions;
 mod non_send_fields_in_send_ty;
 mod nonstandard_macro_braces;
-mod numeric_arithmetic;
 mod octal_escapes;
 mod only_used_in_recursion;
 mod open_options;
+mod operators;
 mod option_env_unwrap;
 mod option_if_let_else;
 mod overflow_check_conditional;
@@ -342,7 +335,6 @@ mod path_buf_push_overwrite;
 mod pattern_type_mismatch;
 mod precedence;
 mod ptr;
-mod ptr_eq;
 mod ptr_offset_with_cast;
 mod pub_use;
 mod question_mark;
@@ -363,7 +355,6 @@ mod repeat_once;
 mod return_self_not_must_use;
 mod returns;
 mod same_name_method;
-mod self_assignment;
 mod self_named_constructors;
 mod semicolon_if_nothing_returned;
 mod serde_api;
@@ -448,6 +439,39 @@ pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, sess: &Se
     store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes { msrv }));
 }
 
+fn read_msrv(conf: &Conf, sess: &Session) -> Option<RustcVersion> {
+    let cargo_msrv = std::env::var("CARGO_PKG_RUST_VERSION")
+        .ok()
+        .and_then(|v| parse_msrv(&v, None, None));
+    let clippy_msrv = conf.msrv.as_ref().and_then(|s| {
+        parse_msrv(s, None, None).or_else(|| {
+            sess.err(&format!(
+                "error reading Clippy's configuration file. `{}` is not a valid Rust version",
+                s
+            ));
+            None
+        })
+    });
+
+    if let Some(cargo_msrv) = cargo_msrv {
+        if let Some(clippy_msrv) = clippy_msrv {
+            // if both files have an msrv, let's compare them and emit a warning if they differ
+            if clippy_msrv != cargo_msrv {
+                sess.warn(&format!(
+                    "the MSRV in `clippy.toml` and `Cargo.toml` differ; using `{}` from `clippy.toml`",
+                    clippy_msrv
+                ));
+            }
+
+            Some(clippy_msrv)
+        } else {
+            Some(cargo_msrv)
+        }
+    } else {
+        clippy_msrv
+    }
+}
+
 #[doc(hidden)]
 pub fn read_conf(sess: &Session) -> Conf {
     let file_name = match utils::conf::lookup_conf_file() {
@@ -463,12 +487,11 @@ pub fn read_conf(sess: &Session) -> Conf {
     let TryConf { conf, errors } = utils::conf::read(&file_name);
     // all conf errors are non-fatal, we just use the default conf in case of error
     for error in errors {
-        sess.struct_err(&format!(
+        sess.err(&format!(
             "error reading Clippy's configuration file `{}`: {}",
             file_name.display(),
             format_error(error)
-        ))
-        .emit();
+        ));
     }
 
     conf
@@ -543,21 +566,14 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
         ))
     });
     store.register_late_pass(|| Box::new(booleans::NonminimalBool));
-    store.register_late_pass(|| Box::new(needless_bitwise_bool::NeedlessBitwiseBool));
-    store.register_late_pass(|| Box::new(eq_op::EqOp));
     store.register_late_pass(|| Box::new(enum_clike::UnportableVariant));
     store.register_late_pass(|| Box::new(float_literal::FloatLiteral));
-    let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
-    store.register_late_pass(move || Box::new(bit_mask::BitMask::new(verbose_bit_mask_threshold)));
     store.register_late_pass(|| Box::new(ptr::Ptr));
-    store.register_late_pass(|| Box::new(ptr_eq::PtrEq));
     store.register_late_pass(|| Box::new(needless_bool::NeedlessBool));
     store.register_late_pass(|| Box::new(needless_bool::BoolComparison));
     store.register_late_pass(|| Box::new(needless_for_each::NeedlessForEach));
     store.register_late_pass(|| Box::new(misc::MiscLints));
     store.register_late_pass(|| Box::new(eta_reduction::EtaReduction));
-    store.register_late_pass(|| Box::new(identity_op::IdentityOp));
-    store.register_late_pass(|| Box::new(erasing_op::ErasingOp));
     store.register_late_pass(|| Box::new(mut_mut::MutMut));
     store.register_late_pass(|| Box::new(mut_reference::UnnecessaryMutPassed));
     store.register_late_pass(|| Box::new(len_zero::LenZero));
@@ -575,16 +591,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(non_octal_unix_permissions::NonOctalUnixPermissions));
     store.register_early_pass(|| Box::new(unnecessary_self_imports::UnnecessarySelfImports));
 
-    let msrv = conf.msrv.as_ref().and_then(|s| {
-        parse_msrv(s, None, None).or_else(|| {
-            sess.err(&format!(
-                "error reading Clippy's configuration file. `{}` is not a valid Rust version",
-                s
-            ));
-            None
-        })
-    });
-
+    let msrv = read_msrv(conf, sess);
     let avoid_breaking_exported_api = conf.avoid_breaking_exported_api;
     let allow_expect_in_tests = conf.allow_expect_in_tests;
     let allow_unwrap_in_tests = conf.allow_unwrap_in_tests;
@@ -639,7 +646,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(borrow_deref_ref::BorrowDerefRef));
     store.register_late_pass(|| Box::new(no_effect::NoEffect));
     store.register_late_pass(|| Box::new(temporary_assignment::TemporaryAssignment));
-    store.register_late_pass(|| Box::new(transmute::Transmute));
+    store.register_late_pass(move || Box::new(transmute::Transmute::new(msrv)));
     let cognitive_complexity_threshold = conf.cognitive_complexity_threshold;
     store.register_late_pass(move || {
         Box::new(cognitive_complexity::CognitiveComplexity::new(
@@ -655,7 +662,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(derivable_impls::DerivableImpls));
     store.register_late_pass(|| Box::new(drop_forget_ref::DropForgetRef));
     store.register_late_pass(|| Box::new(empty_enum::EmptyEnum));
-    store.register_late_pass(|| Box::new(absurd_extreme_comparisons::AbsurdExtremeComparisons));
     store.register_late_pass(|| Box::new(invalid_upcast_comparisons::InvalidUpcastComparisons));
     store.register_late_pass(|| Box::new(regex::Regex));
     store.register_late_pass(|| Box::new(copies::CopyAndPaste));
@@ -678,8 +684,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(move || Box::new(doc::DocMarkdown::new(doc_valid_idents.clone())));
     store.register_late_pass(|| Box::new(neg_multiply::NegMultiply));
     store.register_late_pass(|| Box::new(mem_forget::MemForget));
-    store.register_late_pass(|| Box::new(numeric_arithmetic::NumericArithmetic::default()));
-    store.register_late_pass(|| Box::new(assign_ops::AssignOps));
     store.register_late_pass(|| Box::new(let_if_seq::LetIfSeq));
     store.register_late_pass(|| Box::new(mixed_read_write_in_expression::EvalOrderDependence));
     store.register_late_pass(|| Box::new(missing_doc::MissingDoc::new()));
@@ -706,7 +710,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(useless_conversion::UselessConversion::default()));
     store.register_late_pass(|| Box::new(implicit_hasher::ImplicitHasher));
     store.register_late_pass(|| Box::new(fallible_impl_from::FallibleImplFrom));
-    store.register_late_pass(|| Box::new(double_comparison::DoubleComparisons));
     store.register_late_pass(|| Box::new(question_mark::QuestionMark));
     store.register_early_pass(|| Box::new(suspicious_operation_groupings::SuspiciousOperationGroupings));
     store.register_late_pass(|| Box::new(suspicious_trait_impl::SuspiciousImpl));
@@ -714,7 +717,6 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(inherent_impl::MultipleInherentImpl));
     store.register_late_pass(|| Box::new(neg_cmp_op_on_partial_ord::NoNegCompOpForPartialOrd));
     store.register_late_pass(|| Box::new(unwrap::Unwrap));
-    store.register_late_pass(|| Box::new(duration_subsec::DurationSubsec));
     store.register_late_pass(|| Box::new(indexing_slicing::IndexingSlicing));
     store.register_late_pass(|| Box::new(non_copy_const::NonCopyConst));
     store.register_late_pass(|| Box::new(ptr_offset_with_cast::PtrOffsetWithCast));
@@ -725,13 +727,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(assertions_on_constants::AssertionsOnConstants));
     store.register_late_pass(|| Box::new(transmuting_null::TransmutingNull));
     store.register_late_pass(|| Box::new(path_buf_push_overwrite::PathBufPushOverwrite));
-    store.register_late_pass(|| Box::new(integer_division::IntegerDivision));
     store.register_late_pass(|| Box::new(inherent_to_string::InherentToString));
     let max_trait_bounds = conf.max_trait_bounds;
     store.register_late_pass(move || Box::new(trait_bounds::TraitBounds::new(max_trait_bounds)));
     store.register_late_pass(|| Box::new(comparison_chain::ComparisonChain));
     store.register_late_pass(|| Box::new(mut_key::MutableKeyType));
-    store.register_late_pass(|| Box::new(modulo_arithmetic::ModuloArithmetic));
     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()));
@@ -828,9 +828,7 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(stable_sort_primitive::StableSortPrimitive));
     store.register_late_pass(|| Box::new(repeat_once::RepeatOnce));
     store.register_late_pass(|| Box::new(unwrap_in_result::UnwrapInResult));
-    store.register_late_pass(|| Box::new(self_assignment::SelfAssignment));
     store.register_late_pass(|| Box::new(manual_ok_or::ManualOkOr));
-    store.register_late_pass(|| Box::new(float_equality_without_abs::FloatEqualityWithoutAbs));
     store.register_late_pass(|| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
     store.register_late_pass(|| Box::new(async_yields_async::AsyncYieldsAsync));
     let disallowed_methods = conf.disallowed_methods.clone();
@@ -910,6 +908,11 @@ pub fn register_plugins(store: &mut rustc_lint::LintStore, sess: &Session, conf:
     store.register_late_pass(|| Box::new(mismatching_type_param_order::TypeParamMismatch));
     store.register_late_pass(|| Box::new(as_underscore::AsUnderscore));
     store.register_late_pass(|| Box::new(read_zero_byte_vec::ReadZeroByteVec));
+    store.register_late_pass(|| Box::new(default_instead_of_iter_empty::DefaultIterEmpty));
+    store.register_late_pass(move || Box::new(manual_rem_euclid::ManualRemEuclid::new(msrv)));
+    store.register_late_pass(move || Box::new(manual_retain::ManualRetain::new(msrv)));
+    let verbose_bit_mask_threshold = conf.verbose_bit_mask_threshold;
+    store.register_late_pass(move || Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index 93f5663312f..5c0bd57ac50 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -92,7 +92,9 @@ impl<'tcx> LateLintPass<'tcx> for Lifetimes {
         if let ItemKind::Fn(ref sig, generics, id) = item.kind {
             check_fn_inner(cx, sig.decl, Some(id), None, generics, item.span, true);
         } else if let ItemKind::Impl(impl_) = item.kind {
-            report_extra_impl_lifetimes(cx, impl_);
+            if !item.span.from_expansion() {
+                report_extra_impl_lifetimes(cx, impl_);
+            }
         }
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
new file mode 100644
index 00000000000..33736d6d4e6
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
@@ -0,0 +1,158 @@
+use super::utils::make_iterator_snippet;
+use super::MANUAL_FIND;
+use clippy_utils::{
+    diagnostics::span_lint_and_then, higher, is_lang_ctor, path_res, peel_blocks_with_stmt,
+    source::snippet_with_applicability, ty::implements_trait,
+};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{
+    def::Res, lang_items::LangItem, BindingAnnotation, Block, Expr, ExprKind, HirId, Node, Pat, PatKind, Stmt, StmtKind,
+};
+use rustc_lint::LateContext;
+use rustc_span::source_map::Span;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    pat: &'tcx Pat<'_>,
+    arg: &'tcx Expr<'_>,
+    body: &'tcx Expr<'_>,
+    span: Span,
+    expr: &'tcx Expr<'_>,
+) {
+    let inner_expr = peel_blocks_with_stmt(body);
+    // Check for the specific case that the result is returned and optimize suggestion for that (more
+    // cases can be added later)
+    if_chain! {
+        if let Some(higher::If { cond, then, r#else: None, }) = higher::If::hir(inner_expr);
+        if let Some(binding_id) = get_binding(pat);
+        if let ExprKind::Block(block, _) = then.kind;
+        if let [stmt] = block.stmts;
+        if let StmtKind::Semi(semi) = stmt.kind;
+        if let ExprKind::Ret(Some(ret_value)) = semi.kind;
+        if let ExprKind::Call(Expr { kind: ExprKind::Path(ctor), .. }, [inner_ret]) = ret_value.kind;
+        if is_lang_ctor(cx, ctor, LangItem::OptionSome);
+        if path_res(cx, inner_ret) == Res::Local(binding_id);
+        if let Some((last_stmt, last_ret)) = last_stmt_and_ret(cx, expr);
+        then {
+            let mut applicability = Applicability::MachineApplicable;
+            let mut snippet = make_iterator_snippet(cx, arg, &mut applicability);
+            // Checks if `pat` is a single reference to a binding (`&x`)
+            let is_ref_to_binding =
+                matches!(pat.kind, PatKind::Ref(inner, _) if matches!(inner.kind, PatKind::Binding(..)));
+            // If `pat` is not a binding or a reference to a binding (`x` or `&x`)
+            // we need to map it to the binding returned by the function (i.e. `.map(|(x, _)| x)`)
+            if !(matches!(pat.kind, PatKind::Binding(..)) || is_ref_to_binding) {
+                snippet.push_str(
+                    &format!(
+                        ".map(|{}| {})",
+                        snippet_with_applicability(cx, pat.span, "..", &mut applicability),
+                        snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability),
+                    )[..],
+                );
+            }
+            let ty = cx.typeck_results().expr_ty(inner_ret);
+            if cx.tcx.lang_items().copy_trait().map_or(false, |id| implements_trait(cx, ty, id, &[])) {
+                snippet.push_str(
+                    &format!(
+                        ".find(|{}{}| {})",
+                        "&".repeat(1 + usize::from(is_ref_to_binding)),
+                        snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability),
+                        snippet_with_applicability(cx, cond.span, "..", &mut applicability),
+                    )[..],
+                );
+                if is_ref_to_binding {
+                    snippet.push_str(".copied()");
+                }
+            } else {
+                applicability = Applicability::MaybeIncorrect;
+                snippet.push_str(
+                    &format!(
+                        ".find(|{}| {})",
+                        snippet_with_applicability(cx, inner_ret.span, "..", &mut applicability),
+                        snippet_with_applicability(cx, cond.span, "..", &mut applicability),
+                    )[..],
+                );
+            }
+            // Extends to `last_stmt` to include semicolon in case of `return None;`
+            let lint_span = span.to(last_stmt.span).to(last_ret.span);
+            span_lint_and_then(
+                cx,
+                MANUAL_FIND,
+                lint_span,
+                "manual implementation of `Iterator::find`",
+                |diag| {
+                    if applicability == Applicability::MaybeIncorrect {
+                        diag.note("you may need to dereference some variables");
+                    }
+                    diag.span_suggestion(
+                        lint_span,
+                        "replace with an iterator",
+                        snippet,
+                        applicability,
+                    );
+                },
+            );
+        }
+    }
+}
+
+fn get_binding(pat: &Pat<'_>) -> Option<HirId> {
+    let mut hir_id = None;
+    let mut count = 0;
+    pat.each_binding(|annotation, id, _, _| {
+        count += 1;
+        if count > 1 {
+            hir_id = None;
+            return;
+        }
+        if let BindingAnnotation::Unannotated = annotation {
+            hir_id = Some(id);
+        }
+    });
+    hir_id
+}
+
+// Returns the last statement and last return if function fits format for lint
+fn last_stmt_and_ret<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+) -> Option<(&'tcx Stmt<'tcx>, &'tcx Expr<'tcx>)> {
+    // Returns last non-return statement and the last return
+    fn extract<'tcx>(block: &Block<'tcx>) -> Option<(&'tcx Stmt<'tcx>, &'tcx Expr<'tcx>)> {
+        if let [.., last_stmt] = block.stmts {
+            if let Some(ret) = block.expr {
+                return Some((last_stmt, ret));
+            }
+            if_chain! {
+                if let [.., snd_last, _] = block.stmts;
+                if let StmtKind::Semi(last_expr) = last_stmt.kind;
+                if let ExprKind::Ret(Some(ret)) = last_expr.kind;
+                then {
+                    return Some((snd_last, ret));
+                }
+            }
+        }
+        None
+    }
+    let mut parent_iter = cx.tcx.hir().parent_iter(expr.hir_id);
+    if_chain! {
+        // This should be the loop
+        if let Some((node_hir, Node::Stmt(..))) = parent_iter.next();
+        // This should be the funciton body
+        if let Some((_, Node::Block(block))) = parent_iter.next();
+        if let Some((last_stmt, last_ret)) = extract(block);
+        if last_stmt.hir_id == node_hir;
+        if let ExprKind::Path(path) = &last_ret.kind;
+        if is_lang_ctor(cx, path, LangItem::OptionNone);
+        if let Some((_, Node::Expr(_block))) = parent_iter.next();
+        // This includes the function header
+        if let Some((_, func)) = parent_iter.next();
+        if func.fn_kind().is_some();
+        then {
+            Some((block.stmts.last().unwrap(), last_ret))
+        } else {
+            None
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs
index 391de922e1e..ed270bd490d 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs
@@ -5,6 +5,7 @@ mod explicit_iter_loop;
 mod for_kv_map;
 mod for_loops_over_fallibles;
 mod iter_next_loop;
+mod manual_find;
 mod manual_flatten;
 mod manual_memcpy;
 mod missing_spin_loop;
@@ -346,7 +347,14 @@ declare_clippy_lint! {
     ///
     /// ### Example
     /// ```ignore
-    /// while let Some(val) = iter() {
+    /// while let Some(val) = iter.next() {
+    ///     ..
+    /// }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```ignore
+    /// for val in &mut iter {
     ///     ..
     /// }
     /// ```
@@ -602,6 +610,37 @@ declare_clippy_lint! {
     "An empty busy waiting loop"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Check for manual implementations of Iterator::find
+    ///
+    /// ### Why is this bad?
+    /// It doesn't affect performance, but using `find` is shorter and easier to read.
+    ///
+    /// ### Example
+    ///
+    /// ```rust
+    /// fn example(arr: Vec<i32>) -> Option<i32> {
+    ///     for el in arr {
+    ///         if el == 1 {
+    ///             return Some(el);
+    ///         }
+    ///     }
+    ///     None
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// fn example(arr: Vec<i32>) -> Option<i32> {
+    ///     arr.into_iter().find(|&el| el == 1)
+    /// }
+    /// ```
+    #[clippy::version = "1.61.0"]
+    pub MANUAL_FIND,
+    complexity,
+    "manual implementation of `Iterator::find`"
+}
+
 declare_lint_pass!(Loops => [
     MANUAL_MEMCPY,
     MANUAL_FLATTEN,
@@ -622,6 +661,7 @@ declare_lint_pass!(Loops => [
     SAME_ITEM_PUSH,
     SINGLE_ELEMENT_LOOP,
     MISSING_SPIN_LOOP,
+    MANUAL_FIND,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Loops {
@@ -696,6 +736,7 @@ fn check_for_loop<'tcx>(
     single_element_loop::check(cx, pat, arg, body, expr);
     same_item_push::check(cx, pat, arg, body, expr);
     manual_flatten::check(cx, pat, arg, body, span);
+    manual_find::check(cx, pat, arg, body, span, expr);
 }
 
 fn check_for_loop_arg(cx: &LateContext<'_>, pat: &Pat<'_>, arg: &Expr<'_>) {
diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs
index 8f57df0be6b..45af6be2653 100644
--- a/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/while_let_loop.rs
@@ -2,71 +2,60 @@ use super::WHILE_LET_LOOP;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::higher;
 use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::needs_ordered_drop;
+use clippy_utils::visitors::any_temporaries_need_ordered_drop;
 use rustc_errors::Applicability;
-use rustc_hir::{Block, Expr, ExprKind, MatchSource, Pat, StmtKind};
-use rustc_lint::{LateContext, LintContext};
-use rustc_middle::lint::in_external_macro;
+use rustc_hir::{Block, Expr, ExprKind, Local, MatchSource, Pat, StmtKind};
+use rustc_lint::LateContext;
 
 pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, loop_block: &'tcx Block<'_>) {
-    // extract the expression from the first statement (if any) in a block
-    let inner_stmt_expr = extract_expr_from_first_stmt(loop_block);
-    // or extract the first expression (if any) from the block
-    if let Some(inner) = inner_stmt_expr.or_else(|| extract_first_expr(loop_block)) {
-        if let Some(higher::IfLet {
-            let_pat,
-            let_expr,
-            if_else: Some(if_else),
-            ..
-        }) = higher::IfLet::hir(cx, inner)
-        {
-            if is_simple_break_expr(if_else) {
-                could_be_while_let(cx, expr, let_pat, let_expr);
+    let (init, has_trailing_exprs) = match (loop_block.stmts, loop_block.expr) {
+        ([stmt, stmts @ ..], expr) => {
+            if let StmtKind::Local(&Local { init: Some(e), .. }) | StmtKind::Semi(e) | StmtKind::Expr(e) = stmt.kind {
+                (e, !stmts.is_empty() || expr.is_some())
+            } else {
+                return;
             }
-        }
-
-        if let ExprKind::Match(matchexpr, arms, MatchSource::Normal) = inner.kind {
-            if arms.len() == 2
-                && arms[0].guard.is_none()
-                && arms[1].guard.is_none()
-                && is_simple_break_expr(arms[1].body)
-            {
-                could_be_while_let(cx, expr, arms[0].pat, matchexpr);
-            }
-        }
-    }
-}
+        },
+        ([], Some(e)) => (e, false),
+        _ => return,
+    };
 
-/// If a block begins with a statement (possibly a `let` binding) and has an
-/// expression, return it.
-fn extract_expr_from_first_stmt<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
-    if let Some(first_stmt) = block.stmts.get(0) {
-        if let StmtKind::Local(local) = first_stmt.kind {
-            return local.init;
-        }
+    if let Some(if_let) = higher::IfLet::hir(cx, init)
+        && let Some(else_expr) = if_let.if_else
+        && is_simple_break_expr(else_expr)
+    {
+        could_be_while_let(cx, expr, if_let.let_pat, if_let.let_expr, has_trailing_exprs);
+    } else if let ExprKind::Match(scrutinee, [arm1, arm2], MatchSource::Normal) = init.kind
+        && arm1.guard.is_none()
+        && arm2.guard.is_none()
+        && is_simple_break_expr(arm2.body)
+    {
+        could_be_while_let(cx, expr, arm1.pat, scrutinee, has_trailing_exprs);
     }
-    None
 }
 
-/// If a block begins with an expression (with or without semicolon), return it.
-fn extract_first_expr<'tcx>(block: &Block<'tcx>) -> Option<&'tcx Expr<'tcx>> {
-    match block.expr {
-        Some(expr) if block.stmts.is_empty() => Some(expr),
-        None if !block.stmts.is_empty() => match block.stmts[0].kind {
-            StmtKind::Expr(expr) | StmtKind::Semi(expr) => Some(expr),
-            StmtKind::Local(..) | StmtKind::Item(..) => None,
-        },
-        _ => None,
-    }
+/// Returns `true` if expr contains a single break expression without a label or eub-expression.
+fn is_simple_break_expr(e: &Expr<'_>) -> bool {
+    matches!(peel_blocks(e).kind, ExprKind::Break(dest, None) if dest.label.is_none())
 }
 
-/// Returns `true` if expr contains a single break expr without destination label
-/// and
-/// passed expression. The expression may be within a block.
-fn is_simple_break_expr(expr: &Expr<'_>) -> bool {
-    match expr.kind {
-        ExprKind::Break(dest, ref passed_expr) if dest.label.is_none() && passed_expr.is_none() => true,
-        ExprKind::Block(b, _) => extract_first_expr(b).map_or(false, is_simple_break_expr),
-        _ => false,
+/// Removes any blocks containing only a single expression.
+fn peel_blocks<'tcx>(e: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
+    if let ExprKind::Block(b, _) = e.kind {
+        match (b.stmts, b.expr) {
+            ([s], None) => {
+                if let StmtKind::Expr(e) | StmtKind::Semi(e) = s.kind {
+                    peel_blocks(e)
+                } else {
+                    e
+                }
+            },
+            ([], Some(e)) => peel_blocks(e),
+            _ => e,
+        }
+    } else {
+        e
     }
 }
 
@@ -75,8 +64,13 @@ fn could_be_while_let<'tcx>(
     expr: &'tcx Expr<'_>,
     let_pat: &'tcx Pat<'_>,
     let_expr: &'tcx Expr<'_>,
+    has_trailing_exprs: bool,
 ) {
-    if in_external_macro(cx.sess(), expr.span) {
+    if has_trailing_exprs
+        && (needs_ordered_drop(cx, cx.typeck_results().expr_ty(let_expr))
+            || any_temporaries_need_ordered_drop(cx, let_expr))
+    {
+        // Switching to a `while let` loop will extend the lifetime of some values.
         return;
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/macro_use.rs b/src/tools/clippy/clippy_lints/src/macro_use.rs
index 753469c603d..d573a1b4fbb 100644
--- a/src/tools/clippy/clippy_lints/src/macro_use.rs
+++ b/src/tools/clippy/clippy_lints/src/macro_use.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::source::snippet;
 use hir::def::{DefKind, Res};
 use if_chain::if_chain;
@@ -50,8 +50,9 @@ impl MacroRefData {
 #[derive(Default)]
 #[expect(clippy::module_name_repetitions)]
 pub struct MacroUseImports {
-    /// the actual import path used and the span of the attribute above it.
-    imports: Vec<(String, Span)>,
+    /// the actual import path used and the span of the attribute above it. The value is
+    /// the location, where the lint should be emitted.
+    imports: Vec<(String, Span, hir::HirId)>,
     /// the span of the macro reference, kept to ensure only one reference is used per macro call.
     collected: FxHashSet<Span>,
     mac_refs: Vec<MacroRefData>,
@@ -90,7 +91,8 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
         if_chain! {
             if cx.sess().opts.edition >= Edition::Edition2018;
             if let hir::ItemKind::Use(path, _kind) = &item.kind;
-            let attrs = cx.tcx.hir().attrs(item.hir_id());
+            let hir_id = item.hir_id();
+            let attrs = cx.tcx.hir().attrs(hir_id);
             if let Some(mac_attr) = attrs.iter().find(|attr| attr.has_name(sym::macro_use));
             if let Res::Def(DefKind::Mod, id) = path.res;
             if !id.is_local();
@@ -99,7 +101,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
                     if let Res::Def(DefKind::Macro(_mac_type), mac_id) = kid.res {
                         let span = mac_attr.span;
                         let def_path = cx.tcx.def_path_str(mac_id);
-                        self.imports.push((def_path, span));
+                        self.imports.push((def_path, span, hir_id));
                     }
                 }
             } else {
@@ -137,7 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
     fn check_crate_post(&mut self, cx: &LateContext<'_>) {
         let mut used = FxHashMap::default();
         let mut check_dup = vec![];
-        for (import, span) in &self.imports {
+        for (import, span, hir_id) in &self.imports {
             let found_idx = self.mac_refs.iter().position(|mac| import.ends_with(&mac.name));
 
             if let Some(idx) = found_idx {
@@ -150,7 +152,7 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
                     [] | [_] => return,
                     [root, item] => {
                         if !check_dup.contains(&(*item).to_string()) {
-                            used.entry(((*root).to_string(), span))
+                            used.entry(((*root).to_string(), span, hir_id))
                                 .or_insert_with(Vec::new)
                                 .push((*item).to_string());
                             check_dup.push((*item).to_string());
@@ -168,13 +170,13 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
                                     }
                                 })
                                 .collect::<Vec<_>>();
-                            used.entry(((*root).to_string(), span))
+                            used.entry(((*root).to_string(), span, hir_id))
                                 .or_insert_with(Vec::new)
                                 .push(filtered.join("::"));
                             check_dup.extend(filtered);
                         } else {
                             let rest = rest.to_vec();
-                            used.entry(((*root).to_string(), span))
+                            used.entry(((*root).to_string(), span, hir_id))
                                 .or_insert_with(Vec::new)
                                 .push(rest.join("::"));
                             check_dup.extend(rest.iter().map(ToString::to_string));
@@ -185,27 +187,33 @@ impl<'tcx> LateLintPass<'tcx> for MacroUseImports {
         }
 
         let mut suggestions = vec![];
-        for ((root, span), path) in used {
+        for ((root, span, hir_id), path) in used {
             if path.len() == 1 {
-                suggestions.push((span, format!("{}::{}", root, path[0])));
+                suggestions.push((span, format!("{}::{}", root, path[0]), hir_id));
             } else {
-                suggestions.push((span, format!("{}::{{{}}}", root, path.join(", "))));
+                suggestions.push((span, format!("{}::{{{}}}", root, path.join(", ")), hir_id));
             }
         }
 
         // If mac_refs is not empty we have encountered an import we could not handle
         // such as `std::prelude::v1::foo` or some other macro that expands to an import.
         if self.mac_refs.is_empty() {
-            for (span, import) in suggestions {
+            for (span, import, hir_id) in suggestions {
                 let help = format!("use {};", import);
-                span_lint_and_sugg(
+                span_lint_hir_and_then(
                     cx,
                     MACRO_USE_IMPORTS,
+                    *hir_id,
                     *span,
                     "`macro_use` attributes are no longer needed in the Rust 2018 edition",
-                    "remove the attribute and import the macro directly, try",
-                    help,
-                    Applicability::MaybeIncorrect,
+                    |diag| {
+                        diag.span_suggestion(
+                            *span,
+                            "remove the attribute and import the macro directly, try",
+                            help,
+                            Applicability::MaybeIncorrect,
+                        );
+                    },
                 );
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
index 14f5faafd7c..4278e98dc91 100644
--- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
@@ -1,6 +1,6 @@
-use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::source::snippet_opt;
-use clippy_utils::{is_doc_hidden, is_lint_allowed, meets_msrv, msrvs};
+use clippy_utils::{is_doc_hidden, meets_msrv, msrvs};
 use rustc_ast::ast::{self, VisibilityKind};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -190,12 +190,13 @@ impl<'tcx> LateLintPass<'tcx> for ManualNonExhaustiveEnum {
                 !self
                     .constructed_enum_variants
                     .contains(&(enum_id.to_def_id(), variant_id.to_def_id()))
-                    && !is_lint_allowed(cx, MANUAL_NON_EXHAUSTIVE, cx.tcx.hir().local_def_id_to_hir_id(enum_id))
             })
         {
-            span_lint_and_then(
+            let hir_id = cx.tcx.hir().local_def_id_to_hir_id(enum_id);
+            span_lint_hir_and_then(
                 cx,
                 MANUAL_NON_EXHAUSTIVE,
+                hir_id,
                 enum_span,
                 "this seems like a manual implementation of the non-exhaustive pattern",
                 |diag| {
diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
new file mode 100644
index 00000000000..b5698965fc3
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
@@ -0,0 +1,123 @@
+use clippy_utils::consts::{constant_full_int, FullInt};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{in_constant, meets_msrv, msrvs, path_to_local};
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind, Node, TyKind};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for an expression like `((x % 4) + 4) % 4` which is a common manual reimplementation
+    /// of `x.rem_euclid(4)`.
+    ///
+    /// ### Why is this bad?
+    /// It's simpler and more readable.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x: i32 = 24;
+    /// let rem = ((x % 4) + 4) % 4;
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let x: i32 = 24;
+    /// let rem = x.rem_euclid(4);
+    /// ```
+    #[clippy::version = "1.63.0"]
+    pub MANUAL_REM_EUCLID,
+    complexity,
+    "manually reimplementing `rem_euclid`"
+}
+
+pub struct ManualRemEuclid {
+    msrv: Option<RustcVersion>,
+}
+
+impl ManualRemEuclid {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+}
+
+impl_lint_pass!(ManualRemEuclid => [MANUAL_REM_EUCLID]);
+
+impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if !meets_msrv(self.msrv, msrvs::REM_EUCLID) {
+            return;
+        }
+
+        if in_constant(cx, expr.hir_id) && !meets_msrv(self.msrv, msrvs::REM_EUCLID_CONST) {
+            return;
+        }
+
+        if in_external_macro(cx.sess(), expr.span) {
+            return;
+        }
+
+        if let ExprKind::Binary(op1, expr1, right) = expr.kind
+            && op1.node == BinOpKind::Rem
+            && let Some(const1) = check_for_unsigned_int_constant(cx, right)
+            && let ExprKind::Binary(op2, left, right) = expr1.kind
+            && op2.node == BinOpKind::Add
+            && let Some((const2, expr2)) = check_for_either_unsigned_int_constant(cx, left, right)
+            && let ExprKind::Binary(op3, expr3, right) = expr2.kind
+            && op3.node == BinOpKind::Rem
+            && let Some(const3) = check_for_unsigned_int_constant(cx, right)
+            // Also ensures the const is nonzero since zero can't be a divisor
+            && const1 == const2 && const2 == const3
+            && let Some(hir_id) = path_to_local(expr3)
+            && let Some(Node::Binding(_)) = cx.tcx.hir().find(hir_id) {
+                // Apply only to params or locals with annotated types
+                match cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
+                    Some(Node::Param(..)) => (),
+                    Some(Node::Local(local)) => {
+                        let Some(ty) = local.ty else { return };
+                        if matches!(ty.kind, TyKind::Infer) {
+                            return;
+                        }
+                    }
+                    _ => return,
+                };
+
+                let mut app = Applicability::MachineApplicable;
+                let rem_of = snippet_with_applicability(cx, expr3.span, "_", &mut app);
+                span_lint_and_sugg(
+                    cx,
+                    MANUAL_REM_EUCLID,
+                    expr.span,
+                    "manual `rem_euclid` implementation",
+                    "consider using",
+                    format!("{rem_of}.rem_euclid({const1})"),
+                    app,
+                );
+        }
+    }
+
+    extract_msrv_attr!(LateContext);
+}
+
+// Checks if either the left or right expressions can be an unsigned int constant and returns that
+// constant along with the other expression unchanged if so
+fn check_for_either_unsigned_int_constant<'a>(
+    cx: &'a LateContext<'_>,
+    left: &'a Expr<'_>,
+    right: &'a Expr<'_>,
+) -> Option<(u128, &'a Expr<'a>)> {
+    check_for_unsigned_int_constant(cx, left)
+        .map(|int_const| (int_const, right))
+        .or_else(|| check_for_unsigned_int_constant(cx, right).map(|int_const| (int_const, left)))
+}
+
+fn check_for_unsigned_int_constant<'a>(cx: &'a LateContext<'_>, expr: &'a Expr<'_>) -> Option<u128> {
+    let Some(int_const) = constant_full_int(cx, cx.typeck_results(), expr) else { return None };
+    match int_const {
+        FullInt::S(s) => s.try_into().ok(),
+        FullInt::U(u) => Some(u),
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs
new file mode 100644
index 00000000000..c35e1e021ef
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs
@@ -0,0 +1,228 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::{get_parent_expr, match_def_path, paths, SpanlessEq};
+use clippy_utils::{meets_msrv, msrvs};
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_hir::ExprKind::Assign;
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::symbol::sym;
+
+const ACCEPTABLE_METHODS: [&[&str]; 4] = [
+    &paths::HASHSET_ITER,
+    &paths::BTREESET_ITER,
+    &paths::SLICE_INTO,
+    &paths::VEC_DEQUE_ITER,
+];
+const ACCEPTABLE_TYPES: [(rustc_span::Symbol, Option<RustcVersion>); 6] = [
+    (sym::BTreeSet, Some(msrvs::BTREE_SET_RETAIN)),
+    (sym::BTreeMap, Some(msrvs::BTREE_MAP_RETAIN)),
+    (sym::HashSet, Some(msrvs::HASH_SET_RETAIN)),
+    (sym::HashMap, Some(msrvs::HASH_MAP_RETAIN)),
+    (sym::Vec, None),
+    (sym::VecDeque, None),
+];
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for code to be replaced by `.retain()`.
+    /// ### Why is this bad?
+    /// `.retain()` is simpler and avoids needless allocation.
+    /// ### Example
+    /// ```rust
+    /// let mut vec = vec![0, 1, 2];
+    /// vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect();
+    /// vec = vec.into_iter().filter(|x| x % 2 == 0).collect();
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let mut vec = vec![0, 1, 2];
+    /// vec.retain(|x| x % 2 == 0);
+    /// ```
+    #[clippy::version = "1.63.0"]
+    pub MANUAL_RETAIN,
+    perf,
+    "`retain()` is simpler and the same functionalitys"
+}
+
+pub struct ManualRetain {
+    msrv: Option<RustcVersion>,
+}
+
+impl ManualRetain {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+}
+
+impl_lint_pass!(ManualRetain => [MANUAL_RETAIN]);
+
+impl<'tcx> LateLintPass<'tcx> for ManualRetain {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
+        if let Some(parent_expr) = get_parent_expr(cx, expr)
+            && let Assign(left_expr, collect_expr, _) = &parent_expr.kind
+            && let hir::ExprKind::MethodCall(seg, _, _) = &collect_expr.kind
+            && seg.args.is_none()
+            && let hir::ExprKind::MethodCall(_, [target_expr], _) = &collect_expr.kind
+            && let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id)
+            && match_def_path(cx, collect_def_id, &paths::CORE_ITER_COLLECT) {
+            check_into_iter(cx, parent_expr, left_expr, target_expr, self.msrv);
+            check_iter(cx, parent_expr, left_expr, target_expr, self.msrv);
+            check_to_owned(cx, parent_expr, left_expr, target_expr, self.msrv);
+        }
+    }
+
+    extract_msrv_attr!(LateContext);
+}
+
+fn check_into_iter(
+    cx: &LateContext<'_>,
+    parent_expr: &hir::Expr<'_>,
+    left_expr: &hir::Expr<'_>,
+    target_expr: &hir::Expr<'_>,
+    msrv: Option<RustcVersion>,
+) {
+    if let hir::ExprKind::MethodCall(_, [into_iter_expr, _], _) = &target_expr.kind
+        && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
+        && match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER)
+        && let hir::ExprKind::MethodCall(_, [struct_expr], _) = &into_iter_expr.kind
+        && let Some(into_iter_def_id) = cx.typeck_results().type_dependent_def_id(into_iter_expr.hir_id)
+        && match_def_path(cx, into_iter_def_id, &paths::CORE_ITER_INTO_ITER)
+        && match_acceptable_type(cx, left_expr, msrv)
+        && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) {
+        suggest(cx, parent_expr, left_expr, target_expr);
+    }
+}
+
+fn check_iter(
+    cx: &LateContext<'_>,
+    parent_expr: &hir::Expr<'_>,
+    left_expr: &hir::Expr<'_>,
+    target_expr: &hir::Expr<'_>,
+    msrv: Option<RustcVersion>,
+) {
+    if let hir::ExprKind::MethodCall(_, [filter_expr], _) = &target_expr.kind
+        && let Some(copied_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
+        && (match_def_path(cx, copied_def_id, &paths::CORE_ITER_COPIED)
+            || match_def_path(cx, copied_def_id, &paths::CORE_ITER_CLONED))
+        && let hir::ExprKind::MethodCall(_, [iter_expr, _], _) = &filter_expr.kind
+        && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(filter_expr.hir_id)
+        && match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER)
+        && let hir::ExprKind::MethodCall(_, [struct_expr], _) = &iter_expr.kind
+        && let Some(iter_expr_def_id) = cx.typeck_results().type_dependent_def_id(iter_expr.hir_id)
+        && match_acceptable_def_path(cx, iter_expr_def_id)
+        && match_acceptable_type(cx, left_expr, msrv)
+        && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr) {
+        suggest(cx, parent_expr, left_expr, filter_expr);
+    }
+}
+
+fn check_to_owned(
+    cx: &LateContext<'_>,
+    parent_expr: &hir::Expr<'_>,
+    left_expr: &hir::Expr<'_>,
+    target_expr: &hir::Expr<'_>,
+    msrv: Option<RustcVersion>,
+) {
+    if meets_msrv(msrv,  msrvs::STRING_RETAIN)
+        && let hir::ExprKind::MethodCall(_, [filter_expr], _) = &target_expr.kind
+        && let Some(to_owned_def_id) = cx.typeck_results().type_dependent_def_id(target_expr.hir_id)
+        && match_def_path(cx, to_owned_def_id, &paths::TO_OWNED_METHOD)
+        && let hir::ExprKind::MethodCall(_, [chars_expr, _], _) = &filter_expr.kind
+        && let Some(filter_def_id) = cx.typeck_results().type_dependent_def_id(filter_expr.hir_id)
+        && match_def_path(cx, filter_def_id, &paths::CORE_ITER_FILTER)
+        && let hir::ExprKind::MethodCall(_, [str_expr], _) = &chars_expr.kind
+        && let Some(chars_expr_def_id) = cx.typeck_results().type_dependent_def_id(chars_expr.hir_id)
+        && match_def_path(cx, chars_expr_def_id, &paths::STR_CHARS)
+        && let ty = cx.typeck_results().expr_ty(str_expr).peel_refs()
+        && is_type_diagnostic_item(cx, ty, sym::String)
+        && SpanlessEq::new(cx).eq_expr(left_expr, str_expr) {
+        suggest(cx, parent_expr, left_expr, filter_expr);
+    }
+}
+
+fn suggest(cx: &LateContext<'_>, parent_expr: &hir::Expr<'_>, left_expr: &hir::Expr<'_>, filter_expr: &hir::Expr<'_>) {
+    if let hir::ExprKind::MethodCall(_, [_, closure], _) = filter_expr.kind
+        && let hir::ExprKind::Closure{ body, ..} = closure.kind
+        && let filter_body = cx.tcx.hir().body(body)
+        && let [filter_params] = filter_body.params
+        && let Some(sugg) = match filter_params.pat.kind {
+            hir::PatKind::Binding(_, _, filter_param_ident, None) => {
+                Some(format!("{}.retain(|{}| {})", snippet(cx, left_expr.span, ".."), filter_param_ident, snippet(cx, filter_body.value.span, "..")))
+            },
+            hir::PatKind::Tuple([key_pat, value_pat], _) => {
+                make_sugg(cx, key_pat, value_pat, left_expr, filter_body)
+            },
+            hir::PatKind::Ref(pat, _) => {
+                match pat.kind {
+                    hir::PatKind::Binding(_, _, filter_param_ident, None) => {
+                        Some(format!("{}.retain(|{}| {})", snippet(cx, left_expr.span, ".."), filter_param_ident, snippet(cx, filter_body.value.span, "..")))
+                    },
+                    _ => None
+                }
+            },
+            _ => None
+        } {
+        span_lint_and_sugg(
+            cx,
+            MANUAL_RETAIN,
+            parent_expr.span,
+            "this expression can be written more simply using `.retain()`",
+            "consider calling `.retain()` instead",
+            sugg,
+            Applicability::MachineApplicable
+        );
+    }
+}
+
+fn make_sugg(
+    cx: &LateContext<'_>,
+    key_pat: &rustc_hir::Pat<'_>,
+    value_pat: &rustc_hir::Pat<'_>,
+    left_expr: &hir::Expr<'_>,
+    filter_body: &hir::Body<'_>,
+) -> Option<String> {
+    match (&key_pat.kind, &value_pat.kind) {
+        (hir::PatKind::Binding(_, _, key_param_ident, None), hir::PatKind::Binding(_, _, value_param_ident, None)) => {
+            Some(format!(
+                "{}.retain(|{}, &mut {}| {})",
+                snippet(cx, left_expr.span, ".."),
+                key_param_ident,
+                value_param_ident,
+                snippet(cx, filter_body.value.span, "..")
+            ))
+        },
+        (hir::PatKind::Binding(_, _, key_param_ident, None), hir::PatKind::Wild) => Some(format!(
+            "{}.retain(|{}, _| {})",
+            snippet(cx, left_expr.span, ".."),
+            key_param_ident,
+            snippet(cx, filter_body.value.span, "..")
+        )),
+        (hir::PatKind::Wild, hir::PatKind::Binding(_, _, value_param_ident, None)) => Some(format!(
+            "{}.retain(|_, &mut {}| {})",
+            snippet(cx, left_expr.span, ".."),
+            value_param_ident,
+            snippet(cx, filter_body.value.span, "..")
+        )),
+        _ => None,
+    }
+}
+
+fn match_acceptable_def_path(cx: &LateContext<'_>, collect_def_id: DefId) -> bool {
+    ACCEPTABLE_METHODS
+        .iter()
+        .any(|&method| match_def_path(cx, collect_def_id, method))
+}
+
+fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: Option<RustcVersion>) -> bool {
+    let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs();
+    ACCEPTABLE_TYPES.iter().any(|(ty, acceptable_msrv)| {
+        is_type_diagnostic_item(cx, expr_ty, *ty)
+            && acceptable_msrv.map_or(true, |acceptable_msrv| meets_msrv(msrv, acceptable_msrv))
+    })
+}
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 16fefea5520..15513de7d86 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
@@ -285,7 +285,7 @@ impl<'a> NormalizedPat<'a> {
                 // TODO: Handle negative integers. They're currently treated as a wild match.
                 ExprKind::Lit(lit) => match lit.node {
                     LitKind::Str(sym, _) => Self::LitStr(sym),
-                    LitKind::ByteStr(ref bytes) => Self::LitBytes(&**bytes),
+                    LitKind::ByteStr(ref bytes) => Self::LitBytes(bytes),
                     LitKind::Byte(val) => Self::LitInt(val.into()),
                     LitKind::Char(val) => Self::LitInt(val.into()),
                     LitKind::Int(val, _) => Self::LitInt(val),
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs
index 9df2db45dcf..5ae4a65acaf 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_single_binding.rs
@@ -55,7 +55,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
                         cx,
                         (ex, expr),
                         (bind_names, matched_vars),
-                        &*snippet_body,
+                        &snippet_body,
                         &mut applicability,
                         Some(span),
                     );
@@ -88,7 +88,7 @@ pub(crate) fn check<'a>(cx: &LateContext<'a>, ex: &Expr<'a>, arms: &[Arm<'_>], e
                         cx,
                         (ex, expr),
                         (bind_names, matched_vars),
-                        &*snippet_body,
+                        &snippet_body,
                         &mut applicability,
                         None,
                     );
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs
index 8302ce426e5..fa3b8d1fcea 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs
@@ -118,7 +118,7 @@ fn lint(cx: &LateContext<'_>, case_method: &CaseMethod, bad_case_span: Span, bad
         MATCH_STR_CASE_MISMATCH,
         bad_case_span,
         "this `match` arm has a differing case than its expression",
-        &*format!("consider changing the case of this arm to respect `{}`", method_str),
+        &format!("consider changing the case of this arm to respect `{}`", method_str),
         format!("\"{}\"", suggestion),
         Applicability::MachineApplicable,
     );
diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs
index 3e765173fb9..b2a873ef582 100644
--- a/src/tools/clippy/clippy_lints/src/matches/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs
@@ -791,7 +791,7 @@ declare_clippy_lint! {
     /// the match block and thus will not unlock.
     ///
     /// ### Example
-    /// ```rust.ignore
+    /// ```rust,ignore
     /// # use std::sync::Mutex;
     ///
     /// # struct State {}
@@ -963,7 +963,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
                 return;
             }
             if matches!(source, MatchSource::Normal | MatchSource::ForLoopDesugar) {
-                significant_drop_in_scrutinee::check(cx, expr, ex, source);
+                significant_drop_in_scrutinee::check(cx, expr, ex, arms, source);
             }
 
             collapsible_match::check_match(cx, arms);
diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
index 0ea3f3b673b..8499e050af2 100644
--- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
@@ -3,16 +3,13 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::snippet;
 use clippy_utils::sugg::Sugg;
 use clippy_utils::ty::needs_ordered_drop;
-use clippy_utils::{higher, match_def_path};
-use clippy_utils::{is_lang_ctor, is_trait_method, paths};
+use clippy_utils::visitors::any_temporaries_need_ordered_drop;
+use clippy_utils::{higher, is_lang_ctor, is_trait_method, match_def_path, paths};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::{OptionNone, PollPending};
-use rustc_hir::{
-    intravisit::{walk_expr, Visitor},
-    Arm, Block, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp,
-};
+use rustc_hir::{Arm, Expr, ExprKind, Node, Pat, PatKind, QPath, UnOp};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, subst::GenericArgKind, DefIdTree, Ty};
 use rustc_span::sym;
@@ -47,79 +44,6 @@ fn try_get_generic_ty(ty: Ty<'_>, index: usize) -> Option<Ty<'_>> {
     }
 }
 
-// Checks if there are any temporaries created in the given expression for which drop order
-// matters.
-fn temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> bool {
-    struct V<'a, 'tcx> {
-        cx: &'a LateContext<'tcx>,
-        res: bool,
-    }
-    impl<'a, 'tcx> Visitor<'tcx> for V<'a, 'tcx> {
-        fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
-            match expr.kind {
-                // Taking the reference of a value leaves a temporary
-                // e.g. In `&String::new()` the string is a temporary value.
-                // Remaining fields are temporary values
-                // e.g. In `(String::new(), 0).1` the string is a temporary value.
-                ExprKind::AddrOf(_, _, expr) | ExprKind::Field(expr, _) => {
-                    if !matches!(expr.kind, ExprKind::Path(_)) {
-                        if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(expr)) {
-                            self.res = true;
-                        } else {
-                            self.visit_expr(expr);
-                        }
-                    }
-                },
-                // the base type is always taken by reference.
-                // e.g. In `(vec![0])[0]` the vector is a temporary value.
-                ExprKind::Index(base, index) => {
-                    if !matches!(base.kind, ExprKind::Path(_)) {
-                        if needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(base)) {
-                            self.res = true;
-                        } else {
-                            self.visit_expr(base);
-                        }
-                    }
-                    self.visit_expr(index);
-                },
-                // Method calls can take self by reference.
-                // e.g. In `String::new().len()` the string is a temporary value.
-                ExprKind::MethodCall(_, [self_arg, args @ ..], _) => {
-                    if !matches!(self_arg.kind, ExprKind::Path(_)) {
-                        let self_by_ref = self
-                            .cx
-                            .typeck_results()
-                            .type_dependent_def_id(expr.hir_id)
-                            .map_or(false, |id| self.cx.tcx.fn_sig(id).skip_binder().inputs()[0].is_ref());
-                        if self_by_ref && needs_ordered_drop(self.cx, self.cx.typeck_results().expr_ty(self_arg)) {
-                            self.res = true;
-                        } else {
-                            self.visit_expr(self_arg);
-                        }
-                    }
-                    args.iter().for_each(|arg| self.visit_expr(arg));
-                },
-                // Either explicitly drops values, or changes control flow.
-                ExprKind::DropTemps(_)
-                | ExprKind::Ret(_)
-                | ExprKind::Break(..)
-                | ExprKind::Yield(..)
-                | ExprKind::Block(Block { expr: None, .. }, _)
-                | ExprKind::Loop(..) => (),
-
-                // Only consider the final expression.
-                ExprKind::Block(Block { expr: Some(expr), .. }, _) => self.visit_expr(expr),
-
-                _ => walk_expr(self, expr),
-            }
-        }
-    }
-
-    let mut v = V { cx, res: false };
-    v.visit_expr(expr);
-    v.res
-}
-
 fn find_sugg_for_if_let<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx Expr<'_>,
@@ -191,7 +115,7 @@ fn find_sugg_for_if_let<'tcx>(
     // scrutinee would be, so they have to be considered as well.
     // e.g. in `if let Some(x) = foo.lock().unwrap().baz.as_ref() { .. }` the lock will be held
     // for the duration if body.
-    let needs_drop = needs_ordered_drop(cx, check_ty) || temporaries_need_ordered_drop(cx, let_expr);
+    let needs_drop = needs_ordered_drop(cx, check_ty) || any_temporaries_need_ordered_drop(cx, let_expr);
 
     // check that `while_let_on_iterator` lint does not trigger
     if_chain! {
@@ -362,9 +286,9 @@ fn find_good_method_for_match<'a>(
         .qpath_res(path_right, arms[1].pat.hir_id)
         .opt_def_id()?;
     let body_node_pair = if match_def_path(cx, left_id, expected_left) && match_def_path(cx, right_id, expected_right) {
-        (&(*arms[0].body).kind, &(*arms[1].body).kind)
+        (&arms[0].body.kind, &arms[1].body.kind)
     } else if match_def_path(cx, right_id, expected_left) && match_def_path(cx, right_id, expected_right) {
-        (&(*arms[1].body).kind, &(*arms[0].body).kind)
+        (&arms[1].body.kind, &arms[0].body.kind)
     } else {
         return None;
     };
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 dcaf6f865de..0704a5af525 100644
--- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
@@ -1,10 +1,10 @@
 use crate::FxHashSet;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::get_attr;
 use clippy_utils::source::{indent_of, snippet};
+use clippy_utils::{get_attr, is_lint_allowed};
 use rustc_errors::{Applicability, Diagnostic};
 use rustc_hir::intravisit::{walk_expr, Visitor};
-use rustc_hir::{Expr, ExprKind, MatchSource};
+use rustc_hir::{Arm, Expr, ExprKind, MatchSource};
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::ty::subst::GenericArgKind;
 use rustc_middle::ty::{Ty, TypeAndMut};
@@ -16,12 +16,23 @@ pub(super) fn check<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx Expr<'tcx>,
     scrutinee: &'tcx Expr<'_>,
+    arms: &'tcx [Arm<'_>],
     source: MatchSource,
 ) {
+    if is_lint_allowed(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, expr.hir_id) {
+        return;
+    }
+
     if let Some((suggestions, message)) = has_significant_drop_in_scrutinee(cx, scrutinee, source) {
         for found in suggestions {
             span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| {
                 set_diagnostic(diag, cx, expr, found);
+                let s = Span::new(expr.span.hi(), expr.span.hi(), expr.span.ctxt(), None);
+                diag.span_label(s, "temporary lives until here");
+                for span in has_significant_drop_in_arms(cx, arms) {
+                    diag.span_label(span, "another value with significant `Drop` created here");
+                }
+                diag.note("this might lead to deadlocks or other unexpected behavior");
             });
         }
     }
@@ -80,22 +91,77 @@ fn has_significant_drop_in_scrutinee<'tcx, 'a>(
     let mut helper = SigDropHelper::new(cx);
     helper.find_sig_drop(scrutinee).map(|drops| {
         let message = if source == MatchSource::Normal {
-            "temporary with significant drop in match scrutinee"
+            "temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression"
         } else {
-            "temporary with significant drop in for loop"
+            "temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression"
         };
         (drops, message)
     })
 }
 
+struct SigDropChecker<'a, 'tcx> {
+    seen_types: FxHashSet<Ty<'tcx>>,
+    cx: &'a LateContext<'tcx>,
+}
+
+impl<'a, 'tcx> SigDropChecker<'a, 'tcx> {
+    fn new(cx: &'a LateContext<'tcx>) -> SigDropChecker<'a, 'tcx> {
+        SigDropChecker {
+            seen_types: FxHashSet::default(),
+            cx,
+        }
+    }
+
+    fn get_type(&self, ex: &'tcx Expr<'_>) -> Ty<'tcx> {
+        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, cx: &LateContext<'tcx>, 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 {
+                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;
+                    }
+                }
+
+                for generic_arg in b.iter() {
+                    if let GenericArgKind::Type(ty) = generic_arg.unpack() {
+                        if self.has_sig_drop_attr(cx, ty) {
+                            return true;
+                        }
+                    }
+                }
+                false
+            },
+            rustc_middle::ty::Array(ty, _)
+            | rustc_middle::ty::RawPtr(TypeAndMut { ty, .. })
+            | rustc_middle::ty::Ref(_, ty, _)
+            | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty),
+            _ => false,
+        }
+    }
+}
+
 struct SigDropHelper<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
     is_chain_end: bool,
-    seen_types: FxHashSet<Ty<'tcx>>,
     has_significant_drop: bool,
     current_sig_drop: Option<FoundSigDrop>,
     sig_drop_spans: Option<Vec<FoundSigDrop>>,
     special_handling_for_binary_op: bool,
+    sig_drop_checker: SigDropChecker<'a, 'tcx>,
 }
 
 #[expect(clippy::enum_variant_names)]
@@ -118,11 +184,11 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
         SigDropHelper {
             cx,
             is_chain_end: true,
-            seen_types: FxHashSet::default(),
             has_significant_drop: false,
             current_sig_drop: None,
             sig_drop_spans: None,
             special_handling_for_binary_op: false,
+            sig_drop_checker: SigDropChecker::new(cx),
         }
     }
 
@@ -163,7 +229,7 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
         if self.current_sig_drop.is_some() {
             return;
         }
-        let ty = self.get_type(expr);
+        let ty = self.sig_drop_checker.get_type(expr);
         if ty.is_ref() {
             // We checked that the type was ref, so builtin_deref will return Some TypeAndMut,
             // but let's avoid any chance of an ICE
@@ -187,14 +253,6 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
         }
     }
 
-    fn get_type(&self, ex: &'tcx Expr<'_>) -> Ty<'tcx> {
-        self.cx.typeck_results().expr_ty(ex)
-    }
-
-    fn has_seen_type(&mut self, ty: Ty<'tcx>) -> bool {
-        !self.seen_types.insert(ty)
-    }
-
     fn visit_exprs_for_binary_ops(
         &mut self,
         left: &'tcx Expr<'_>,
@@ -214,44 +272,15 @@ impl<'a, 'tcx> SigDropHelper<'a, 'tcx> {
 
         self.special_handling_for_binary_op = false;
     }
-
-    fn has_sig_drop_attr(&mut self, cx: &LateContext<'tcx>, 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 {
-                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;
-                    }
-                }
-
-                for generic_arg in b.iter() {
-                    if let GenericArgKind::Type(ty) = generic_arg.unpack() {
-                        if self.has_sig_drop_attr(cx, ty) {
-                            return true;
-                        }
-                    }
-                }
-                false
-            },
-            rustc_middle::ty::Array(ty, _)
-            | rustc_middle::ty::RawPtr(TypeAndMut { ty, .. })
-            | rustc_middle::ty::Ref(_, ty, _)
-            | rustc_middle::ty::Slice(ty) => self.has_sig_drop_attr(cx, *ty),
-            _ => false,
-        }
-    }
 }
 
 impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
     fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
-        if !self.is_chain_end && self.has_sig_drop_attr(self.cx, self.get_type(ex)) {
+        if !self.is_chain_end
+            && self
+                .sig_drop_checker
+                .has_sig_drop_attr(self.cx, self.sig_drop_checker.get_type(ex))
+        {
             self.has_significant_drop = true;
             return;
         }
@@ -330,3 +359,38 @@ impl<'a, 'tcx> Visitor<'tcx> for SigDropHelper<'a, 'tcx> {
         }
     }
 }
+
+struct ArmSigDropHelper<'a, 'tcx> {
+    sig_drop_checker: SigDropChecker<'a, 'tcx>,
+    found_sig_drop_spans: FxHashSet<Span>,
+}
+
+impl<'a, 'tcx> ArmSigDropHelper<'a, 'tcx> {
+    fn new(cx: &'a LateContext<'tcx>) -> ArmSigDropHelper<'a, 'tcx> {
+        ArmSigDropHelper {
+            sig_drop_checker: SigDropChecker::new(cx),
+            found_sig_drop_spans: FxHashSet::<Span>::default(),
+        }
+    }
+}
+
+fn has_significant_drop_in_arms<'tcx, 'a>(cx: &'a LateContext<'tcx>, arms: &'tcx [Arm<'_>]) -> FxHashSet<Span> {
+    let mut helper = ArmSigDropHelper::new(cx);
+    for arm in arms {
+        helper.visit_expr(arm.body);
+    }
+    helper.found_sig_drop_spans
+}
+
+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))
+        {
+            self.found_sig_drop_spans.insert(ex.span);
+            return;
+        }
+        walk_expr(self, ex);
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
index 0c4cb45d147..92091a0c339 100644
--- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
@@ -140,70 +140,45 @@ fn check_opt_like<'a>(
     ty: Ty<'a>,
     els: Option<&Expr<'_>>,
 ) {
-    // list of candidate `Enum`s we know will never get any more members
-    let candidates = &[
-        (&paths::COW, "Borrowed"),
-        (&paths::COW, "Cow::Borrowed"),
-        (&paths::COW, "Cow::Owned"),
-        (&paths::COW, "Owned"),
-        (&paths::OPTION, "None"),
-        (&paths::RESULT, "Err"),
-        (&paths::RESULT, "Ok"),
-    ];
-
-    // We want to suggest to exclude an arm that contains only wildcards or forms the exhaustive
-    // match with the second branch, without enum variants in matches.
-    if !contains_only_wilds(arms[1].pat) && !form_exhaustive_matches(arms[0].pat, arms[1].pat) {
-        return;
+    // We don't want to lint if the second arm contains an enum which could
+    // have more variants in the future.
+    if form_exhaustive_matches(cx, ty, arms[0].pat, arms[1].pat) {
+        report_single_pattern(cx, ex, arms, expr, els);
     }
+}
 
+/// Returns `true` if all of the types in the pattern are enums which we know
+/// won't be expanded in the future
+fn pat_in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'a>, pat: &Pat<'_>) -> bool {
     let mut paths_and_types = Vec::new();
-    if !collect_pat_paths(&mut paths_and_types, cx, arms[1].pat, ty) {
-        return;
-    }
+    collect_pat_paths(&mut paths_and_types, cx, pat, ty);
+    paths_and_types.iter().all(|ty| in_candidate_enum(cx, *ty))
+}
 
-    let in_candidate_enum = |path_info: &(String, Ty<'_>)| -> bool {
-        let (path, ty) = path_info;
-        for &(ty_path, pat_path) in candidates {
-            if path == pat_path && match_type(cx, *ty, ty_path) {
-                return true;
-            }
+/// Returns `true` if the given type is an enum we know won't be expanded in the future
+fn in_candidate_enum<'a>(cx: &LateContext<'a>, ty: Ty<'_>) -> bool {
+    // list of candidate `Enum`s we know will never get any more members
+    let candidates = [&paths::COW, &paths::OPTION, &paths::RESULT];
+
+    for candidate_ty in candidates {
+        if match_type(cx, ty, candidate_ty) {
+            return true;
         }
-        false
-    };
-    if paths_and_types.iter().all(in_candidate_enum) {
-        report_single_pattern(cx, ex, arms, expr, els);
     }
+    false
 }
 
-/// Collects paths and their types from the given patterns. Returns true if the given pattern could
-/// be simplified, false otherwise.
-fn collect_pat_paths<'a>(acc: &mut Vec<(String, Ty<'a>)>, cx: &LateContext<'a>, pat: &Pat<'_>, ty: Ty<'a>) -> bool {
+/// Collects types from the given pattern
+fn collect_pat_paths<'a>(acc: &mut Vec<Ty<'a>>, cx: &LateContext<'a>, pat: &Pat<'_>, ty: Ty<'a>) {
     match pat.kind {
-        PatKind::Wild => true,
-        PatKind::Tuple(inner, _) => inner.iter().all(|p| {
+        PatKind::Tuple(inner, _) => inner.iter().for_each(|p| {
             let p_ty = cx.typeck_results().pat_ty(p);
-            collect_pat_paths(acc, cx, p, p_ty)
+            collect_pat_paths(acc, cx, p, p_ty);
         }),
-        PatKind::TupleStruct(ref path, ..) => {
-            let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
-                s.print_qpath(path, false);
-            });
-            acc.push((path, ty));
-            true
-        },
-        PatKind::Binding(BindingAnnotation::Unannotated, .., ident, None) => {
-            acc.push((ident.to_string(), ty));
-            true
+        PatKind::TupleStruct(..) | PatKind::Binding(BindingAnnotation::Unannotated, .., None) | PatKind::Path(_) => {
+            acc.push(ty);
         },
-        PatKind::Path(ref path) => {
-            let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| {
-                s.print_qpath(path, false);
-            });
-            acc.push((path, ty));
-            true
-        },
-        _ => false,
+        _ => {},
     }
 }
 
@@ -218,7 +193,7 @@ fn contains_only_wilds(pat: &Pat<'_>) -> bool {
 
 /// Returns true if the given patterns forms only exhaustive matches that don't contain enum
 /// patterns without a wildcard.
-fn form_exhaustive_matches(left: &Pat<'_>, right: &Pat<'_>) -> bool {
+fn form_exhaustive_matches<'a>(cx: &LateContext<'a>, ty: Ty<'a>, left: &Pat<'_>, right: &Pat<'_>) -> bool {
     match (&left.kind, &right.kind) {
         (PatKind::Wild, _) | (_, PatKind::Wild) => true,
         (PatKind::Tuple(left_in, left_pos), PatKind::Tuple(right_in, right_pos)) => {
@@ -264,6 +239,10 @@ fn form_exhaustive_matches(left: &Pat<'_>, right: &Pat<'_>) -> bool {
             }
             true
         },
+        (PatKind::TupleStruct(..), PatKind::Path(_)) => pat_in_candidate_enum(cx, ty, right),
+        (PatKind::TupleStruct(..), PatKind::TupleStruct(_, inner, _)) => {
+            pat_in_candidate_enum(cx, ty, right) && inner.iter().all(contains_only_wilds)
+        },
         _ => false,
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs
index 8ae84dbb3dc..13853dec99d 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_flatten.rs
@@ -14,14 +14,17 @@ use super::MAP_FLATTEN;
 pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, map_arg: &Expr<'_>, map_span: Span) {
     if let Some((caller_ty_name, method_to_use)) = try_get_caller_ty_name_and_method_name(cx, expr, recv, map_arg) {
         let mut applicability = Applicability::MachineApplicable;
-        
+
         let closure_snippet = snippet_with_applicability(cx, map_arg.span, "..", &mut applicability);
         span_lint_and_sugg(
             cx,
             MAP_FLATTEN,
             expr.span.with_lo(map_span.lo()),
             &format!("called `map(..).flatten()` on `{}`", caller_ty_name),
-            &format!("try replacing `map` with `{}` and remove the `.flatten()`", method_to_use),
+            &format!(
+                "try replacing `map` with `{}` and remove the `.flatten()`",
+                method_to_use
+            ),
             format!("{}({})", method_to_use, closure_snippet),
             applicability,
         );
diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs
index 01bf871198a..df2430ced6b 100644
--- a/src/tools/clippy/clippy_lints/src/misc.rs
+++ b/src/tools/clippy/clippy_lints/src/misc.rs
@@ -1,28 +1,21 @@
-use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_hir_and_then};
 use clippy_utils::source::{snippet, snippet_opt};
-use clippy_utils::ty::{implements_trait, is_copy};
 use if_chain::if_chain;
 use rustc_ast::ast::LitKind;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{
     self as hir, def, BinOpKind, BindingAnnotation, Body, Expr, ExprKind, FnDecl, HirId, Mutability, PatKind, Stmt,
-    StmtKind, TyKind, UnOp,
+    StmtKind, TyKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
-use rustc_middle::ty::{self, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::hygiene::DesugaringKind;
 use rustc_span::source_map::{ExpnKind, Span};
-use rustc_span::symbol::sym;
 
-use clippy_utils::consts::{constant, Constant};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{
-    get_item_name, get_parent_expr, in_constant, is_integer_const, iter_input_pats, last_path_segment,
-    match_any_def_paths, path_def_id, paths, unsext, SpanlessEq,
-};
+use clippy_utils::{get_parent_expr, in_constant, iter_input_pats, last_path_segment, SpanlessEq};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -58,122 +51,6 @@ declare_clippy_lint! {
     style,
     "an entire binding declared as `ref`, in a function argument or a `let` statement"
 }
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for comparisons to NaN.
-    ///
-    /// ### Why is this bad?
-    /// NaN does not compare meaningfully to anything – not
-    /// even itself – so those comparisons are simply wrong.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = 1.0;
-    /// if x == f32::NAN { }
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// # let x = 1.0f32;
-    /// if x.is_nan() { }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub CMP_NAN,
-    correctness,
-    "comparisons to `NAN`, which will always return false, probably not intended"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for (in-)equality comparisons on floating-point
-    /// values (apart from zero), except in functions called `*eq*` (which probably
-    /// implement equality for a type involving floats).
-    ///
-    /// ### Why is this bad?
-    /// Floating point calculations are usually imprecise, so
-    /// asking if two values are *exactly* equal is asking for trouble. For a good
-    /// guide on what to do, see [the floating point
-    /// guide](http://www.floating-point-gui.de/errors/comparison).
-    ///
-    /// ### Example
-    /// ```rust
-    /// let x = 1.2331f64;
-    /// let y = 1.2332f64;
-    ///
-    /// if y == 1.23f64 { }
-    /// if y != x {} // where both are floats
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// # let x = 1.2331f64;
-    /// # let y = 1.2332f64;
-    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
-    /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
-    /// // let error_margin = std::f64::EPSILON;
-    /// if (y - 1.23f64).abs() < error_margin { }
-    /// if (y - x).abs() > error_margin { }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub FLOAT_CMP,
-    pedantic,
-    "using `==` or `!=` on float values instead of comparing difference with an epsilon"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for conversions to owned values just for the sake
-    /// of a comparison.
-    ///
-    /// ### Why is this bad?
-    /// The comparison can operate on a reference, so creating
-    /// an owned value effectively throws it away directly afterwards, which is
-    /// needlessly consuming code and heap space.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = "foo";
-    /// # let y = String::from("foo");
-    /// if x.to_owned() == y {}
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// # let x = "foo";
-    /// # let y = String::from("foo");
-    /// if x == y {}
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub CMP_OWNED,
-    perf,
-    "creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for getting the remainder of a division by one or minus
-    /// one.
-    ///
-    /// ### Why is this bad?
-    /// The result for a divisor of one can only ever be zero; for
-    /// minus one it can cause panic/overflow (if the left operand is the minimal value of
-    /// the respective integer type) or results in zero. No one will write such code
-    /// deliberately, unless trying to win an Underhanded Rust Contest. Even for that
-    /// contest, it's probably a bad idea. Use something more underhanded.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = 1;
-    /// let a = x % 1;
-    /// let a = x % -1;
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub MODULO_ONE,
-    correctness,
-    "taking a number modulo +/-1, which can either panic/overflow or always returns 0"
-}
-
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for the use of bindings with a single leading
@@ -244,51 +121,11 @@ declare_clippy_lint! {
     "using `0 as *{const, mut} T`"
 }
 
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for (in-)equality comparisons on floating-point
-    /// value and constant, except in functions called `*eq*` (which probably
-    /// implement equality for a type involving floats).
-    ///
-    /// ### Why is this bad?
-    /// Floating point calculations are usually imprecise, so
-    /// asking if two values are *exactly* equal is asking for trouble. For a good
-    /// guide on what to do, see [the floating point
-    /// guide](http://www.floating-point-gui.de/errors/comparison).
-    ///
-    /// ### Example
-    /// ```rust
-    /// let x: f64 = 1.0;
-    /// const ONE: f64 = 1.00;
-    ///
-    /// if x == ONE { } // where both are floats
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust
-    /// # let x: f64 = 1.0;
-    /// # const ONE: f64 = 1.00;
-    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
-    /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
-    /// // let error_margin = std::f64::EPSILON;
-    /// if (x - ONE).abs() < error_margin { }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub FLOAT_CMP_CONST,
-    restriction,
-    "using `==` or `!=` on float constants instead of comparing difference with an epsilon"
-}
-
 declare_lint_pass!(MiscLints => [
     TOPLEVEL_REF_ARG,
-    CMP_NAN,
-    FLOAT_CMP,
-    CMP_OWNED,
-    MODULO_ONE,
     USED_UNDERSCORE_BINDING,
     SHORT_CIRCUIT_STATEMENT,
     ZERO_PTR,
-    FLOAT_CMP_CONST
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for MiscLints {
@@ -398,16 +235,9 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
     }
 
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        match expr.kind {
-            ExprKind::Cast(e, ty) => {
-                check_cast(cx, expr.span, e, ty);
-                return;
-            },
-            ExprKind::Binary(ref cmp, left, right) => {
-                check_binary(cx, expr, cmp, left, right);
-                return;
-            },
-            _ => {},
+        if let ExprKind::Cast(e, ty) = expr.kind {
+            check_cast(cx, expr.span, e, ty);
+            return;
         }
         if in_attributes_expansion(expr) || expr.span.is_desugaring(DesugaringKind::Await) {
             // Don't lint things expanded by #[derive(...)], etc or `await` desugaring
@@ -455,236 +285,6 @@ impl<'tcx> LateLintPass<'tcx> for MiscLints {
     }
 }
 
-fn get_lint_and_message(
-    is_comparing_constants: bool,
-    is_comparing_arrays: bool,
-) -> (&'static rustc_lint::Lint, &'static str) {
-    if is_comparing_constants {
-        (
-            FLOAT_CMP_CONST,
-            if is_comparing_arrays {
-                "strict comparison of `f32` or `f64` constant arrays"
-            } else {
-                "strict comparison of `f32` or `f64` constant"
-            },
-        )
-    } else {
-        (
-            FLOAT_CMP,
-            if is_comparing_arrays {
-                "strict comparison of `f32` or `f64` arrays"
-            } else {
-                "strict comparison of `f32` or `f64`"
-            },
-        )
-    }
-}
-
-fn check_nan(cx: &LateContext<'_>, expr: &Expr<'_>, cmp_expr: &Expr<'_>) {
-    if_chain! {
-        if !in_constant(cx, cmp_expr.hir_id);
-        if let Some((value, _)) = constant(cx, cx.typeck_results(), expr);
-        if match value {
-            Constant::F32(num) => num.is_nan(),
-            Constant::F64(num) => num.is_nan(),
-            _ => false,
-        };
-        then {
-            span_lint(
-                cx,
-                CMP_NAN,
-                cmp_expr.span,
-                "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead",
-            );
-        }
-    }
-}
-
-fn is_named_constant<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
-    if let Some((_, res)) = constant(cx, cx.typeck_results(), expr) {
-        res
-    } else {
-        false
-    }
-}
-
-fn is_allowed<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
-    match constant(cx, cx.typeck_results(), expr) {
-        Some((Constant::F32(f), _)) => f == 0.0 || f.is_infinite(),
-        Some((Constant::F64(f), _)) => f == 0.0 || f.is_infinite(),
-        Some((Constant::Vec(vec), _)) => vec.iter().all(|f| match f {
-            Constant::F32(f) => *f == 0.0 || (*f).is_infinite(),
-            Constant::F64(f) => *f == 0.0 || (*f).is_infinite(),
-            _ => false,
-        }),
-        _ => false,
-    }
-}
-
-// Return true if `expr` is the result of `signum()` invoked on a float value.
-fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    // The negation of a signum is still a signum
-    if let ExprKind::Unary(UnOp::Neg, child_expr) = expr.kind {
-        return is_signum(cx, child_expr);
-    }
-
-    if_chain! {
-        if let ExprKind::MethodCall(method_name, [ref self_arg, ..], _) = expr.kind;
-        if sym!(signum) == method_name.ident.name;
-        // Check that the receiver of the signum() is a float (expressions[0] is the receiver of
-        // the method call)
-        then {
-            return is_float(cx, self_arg);
-        }
-    }
-    false
-}
-
-fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    let value = &cx.typeck_results().expr_ty(expr).peel_refs().kind();
-
-    if let ty::Array(arr_ty, _) = value {
-        return matches!(arr_ty.kind(), ty::Float(_));
-    };
-
-    matches!(value, ty::Float(_))
-}
-
-fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
-}
-
-#[expect(clippy::too_many_lines)]
-fn check_to_owned(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
-    #[derive(Default)]
-    struct EqImpl {
-        ty_eq_other: bool,
-        other_eq_ty: bool,
-    }
-
-    impl EqImpl {
-        fn is_implemented(&self) -> bool {
-            self.ty_eq_other || self.other_eq_ty
-        }
-    }
-
-    fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> Option<EqImpl> {
-        cx.tcx.lang_items().eq_trait().map(|def_id| EqImpl {
-            ty_eq_other: implements_trait(cx, ty, def_id, &[other.into()]),
-            other_eq_ty: implements_trait(cx, other, def_id, &[ty.into()]),
-        })
-    }
-
-    let typeck = cx.typeck_results();
-    let (arg, arg_span) = match expr.kind {
-        ExprKind::MethodCall(.., [arg], _)
-            if typeck
-                .type_dependent_def_id(expr.hir_id)
-                .and_then(|id| cx.tcx.trait_of_item(id))
-                .map_or(false, |id| {
-                    matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned))
-                }) =>
-        {
-            (arg, arg.span)
-        },
-        ExprKind::Call(path, [arg])
-            if path_def_id(cx, path)
-                .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM]))
-                .map_or(false, |idx| match idx {
-                    0 => true,
-                    1 => !is_copy(cx, typeck.expr_ty(expr)),
-                    _ => false,
-                }) =>
-        {
-            (arg, arg.span)
-        },
-        _ => return,
-    };
-
-    let arg_ty = typeck.expr_ty(arg);
-    let other_ty = typeck.expr_ty(other);
-
-    let without_deref = symmetric_partial_eq(cx, arg_ty, other_ty).unwrap_or_default();
-    let with_deref = arg_ty
-        .builtin_deref(true)
-        .and_then(|tam| symmetric_partial_eq(cx, tam.ty, other_ty))
-        .unwrap_or_default();
-
-    if !with_deref.is_implemented() && !without_deref.is_implemented() {
-        return;
-    }
-
-    let other_gets_derefed = matches!(other.kind, ExprKind::Unary(UnOp::Deref, _));
-
-    let lint_span = if other_gets_derefed {
-        expr.span.to(other.span)
-    } else {
-        expr.span
-    };
-
-    span_lint_and_then(
-        cx,
-        CMP_OWNED,
-        lint_span,
-        "this creates an owned instance just for comparison",
-        |diag| {
-            // This also catches `PartialEq` implementations that call `to_owned`.
-            if other_gets_derefed {
-                diag.span_label(lint_span, "try implementing the comparison without allocating");
-                return;
-            }
-
-            let arg_snip = snippet(cx, arg_span, "..");
-            let expr_snip;
-            let eq_impl;
-            if with_deref.is_implemented() {
-                expr_snip = format!("*{}", arg_snip);
-                eq_impl = with_deref;
-            } else {
-                expr_snip = arg_snip.to_string();
-                eq_impl = without_deref;
-            };
-
-            let span;
-            let hint;
-            if (eq_impl.ty_eq_other && left) || (eq_impl.other_eq_ty && !left) {
-                span = expr.span;
-                hint = expr_snip;
-            } else {
-                span = expr.span.to(other.span);
-
-                let cmp_span = if other.span < expr.span {
-                    other.span.between(expr.span)
-                } else {
-                    expr.span.between(other.span)
-                };
-                if eq_impl.ty_eq_other {
-                    hint = format!(
-                        "{}{}{}",
-                        expr_snip,
-                        snippet(cx, cmp_span, ".."),
-                        snippet(cx, other.span, "..")
-                    );
-                } else {
-                    hint = format!(
-                        "{}{}{}",
-                        snippet(cx, other.span, ".."),
-                        snippet(cx, cmp_span, ".."),
-                        expr_snip
-                    );
-                }
-            }
-
-            diag.span_suggestion(
-                span,
-                "try",
-                hint,
-                Applicability::MachineApplicable, // snippet
-            );
-        },
-    );
-}
-
 /// Heuristic to see if an expression is used. Should be compatible with
 /// `unused_variables`'s idea
 /// of what it means for an expression to be "used".
@@ -740,74 +340,3 @@ fn check_cast(cx: &LateContext<'_>, span: Span, e: &Expr<'_>, ty: &hir::Ty<'_>)
         }
     }
 }
-
-fn check_binary<'a>(
-    cx: &LateContext<'a>,
-    expr: &Expr<'_>,
-    cmp: &rustc_span::source_map::Spanned<rustc_hir::BinOpKind>,
-    left: &'a Expr<'_>,
-    right: &'a Expr<'_>,
-) {
-    let op = cmp.node;
-    if op.is_comparison() {
-        check_nan(cx, left, expr);
-        check_nan(cx, right, expr);
-        check_to_owned(cx, left, right, true);
-        check_to_owned(cx, right, left, false);
-    }
-    if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
-        if is_allowed(cx, left) || is_allowed(cx, right) {
-            return;
-        }
-
-        // Allow comparing the results of signum()
-        if is_signum(cx, left) && is_signum(cx, right) {
-            return;
-        }
-
-        if let Some(name) = get_item_name(cx, expr) {
-            let name = name.as_str();
-            if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") || name.ends_with("_eq") {
-                return;
-            }
-        }
-        let is_comparing_arrays = is_array(cx, left) || is_array(cx, right);
-        let (lint, msg) = get_lint_and_message(
-            is_named_constant(cx, left) || is_named_constant(cx, right),
-            is_comparing_arrays,
-        );
-        span_lint_and_then(cx, lint, expr.span, msg, |diag| {
-            let lhs = Sugg::hir(cx, left, "..");
-            let rhs = Sugg::hir(cx, right, "..");
-
-            if !is_comparing_arrays {
-                diag.span_suggestion(
-                    expr.span,
-                    "consider comparing them within some margin of error",
-                    format!(
-                        "({}).abs() {} error_margin",
-                        lhs - rhs,
-                        if op == BinOpKind::Eq { '<' } else { '>' }
-                    ),
-                    Applicability::HasPlaceholders, // snippet
-                );
-            }
-            diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`");
-        });
-    } else if op == BinOpKind::Rem {
-        if is_integer_const(cx, right, 1) {
-            span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
-        }
-
-        if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() {
-            if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) {
-                span_lint(
-                    cx,
-                    MODULO_ONE,
-                    expr.span,
-                    "any number modulo -1 will panic/overflow or result in 0",
-                );
-            }
-        };
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs b/src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs
deleted file mode 100644
index 623d22bc9bd..00000000000
--- a/src/tools/clippy/clippy_lints/src/needless_bitwise_bool.rs
+++ /dev/null
@@ -1,85 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_opt;
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using
-    /// a lazy and.
-    ///
-    /// ### Why is this bad?
-    /// The bitwise operators do not support short-circuiting, so it may hinder code performance.
-    /// Additionally, boolean logic "masked" as bitwise logic is not caught by lints like `unnecessary_fold`
-    ///
-    /// ### Known problems
-    /// This lint evaluates only when the right side is determined to have no side effects. At this time, that
-    /// determination is quite conservative.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let (x,y) = (true, false);
-    /// if x & !y {} // where both x and y are booleans
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// let (x,y) = (true, false);
-    /// if x && !y {}
-    /// ```
-    #[clippy::version = "1.54.0"]
-    pub NEEDLESS_BITWISE_BOOL,
-    pedantic,
-    "Boolean expressions that use bitwise rather than lazy operators"
-}
-
-declare_lint_pass!(NeedlessBitwiseBool => [NEEDLESS_BITWISE_BOOL]);
-
-fn is_bitwise_operation(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    let ty = cx.typeck_results().expr_ty(expr);
-    if_chain! {
-        if !expr.span.from_expansion();
-        if let (&ExprKind::Binary(ref op, _, right), &ty::Bool) = (&expr.kind, &ty.kind());
-        if op.node == BinOpKind::BitAnd || op.node == BinOpKind::BitOr;
-        if let ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) | ExprKind::Unary(..) = right.kind;
-        if !right.can_have_side_effects();
-        then {
-            return true;
-        }
-    }
-    false
-}
-
-fn suggestion_snippet(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<String> {
-    if let ExprKind::Binary(ref op, left, right) = expr.kind {
-        if let (Some(l_snippet), Some(r_snippet)) = (snippet_opt(cx, left.span), snippet_opt(cx, right.span)) {
-            let op_snippet = match op.node {
-                BinOpKind::BitAnd => "&&",
-                _ => "||",
-            };
-            return Some(format!("{} {} {}", l_snippet, op_snippet, r_snippet));
-        }
-    }
-    None
-}
-
-impl LateLintPass<'_> for NeedlessBitwiseBool {
-    fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
-        if is_bitwise_operation(cx, expr) {
-            span_lint_and_then(
-                cx,
-                NEEDLESS_BITWISE_BOOL,
-                expr.span,
-                "use of bitwise operator instead of lazy operator between booleans",
-                |diag| {
-                    if let Some(sugg) = suggestion_snippet(cx, expr) {
-                        diag.span_suggestion(expr.span, "try", sugg, Applicability::MachineApplicable);
-                    }
-                },
-            );
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
index ce6bb38b7c0..b087cfb36b1 100644
--- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs
+++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
@@ -1,7 +1,9 @@
 use clippy_utils::consts::{self, Constant};
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::sugg::has_enclosing_paren;
 use if_chain::if_chain;
+use rustc_ast::util::parser::PREC_PREFIX;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
@@ -58,7 +60,12 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
 
         then {
             let mut applicability = Applicability::MachineApplicable;
-            let suggestion = format!("-{}", snippet_with_applicability(cx, exp.span, "..", &mut applicability));
+            let snip = snippet_with_applicability(cx, exp.span, "..", &mut applicability);
+            let suggestion = if exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) {
+                format!("-({})", snip)
+            } else {
+                format!("-{}", snip)
+            };
             span_lint_and_sugg(
                     cx,
                     NEG_MULTIPLY,
diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
index 1727275a4e0..a1ef32ae608 100644
--- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs
+++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
@@ -6,6 +6,7 @@ use std::ptr;
 
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::in_constant;
+use clippy_utils::macros::macro_backtrace;
 use if_chain::if_chain;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
@@ -18,7 +19,7 @@ use rustc_middle::mir::interpret::{ConstValue, ErrorHandled};
 use rustc_middle::ty::adjustment::Adjust;
 use rustc_middle::ty::{self, Ty};
 use rustc_session::{declare_lint_pass, declare_tool_lint};
-use rustc_span::{InnerSpan, Span, DUMMY_SP};
+use rustc_span::{sym, InnerSpan, Span, DUMMY_SP};
 use rustc_typeck::hir_ty_to_ty;
 
 // FIXME: this is a correctness problem but there's no suitable
@@ -250,8 +251,14 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst {
     fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'_>) {
         if let ItemKind::Const(hir_ty, body_id) = it.kind {
             let ty = hir_ty_to_ty(cx.tcx, hir_ty);
-
-            if is_unfrozen(cx, ty) && is_value_unfrozen_poly(cx, body_id, ty) {
+            if !macro_backtrace(it.span).last().map_or(false, |macro_call| {
+                matches!(
+                    cx.tcx.get_diagnostic_name(macro_call.def_id),
+                    Some(sym::thread_local_macro)
+                )
+            }) && is_unfrozen(cx, ty)
+                && is_value_unfrozen_poly(cx, body_id, ty)
+            {
                 lint(cx, Source::Item { item: it.span });
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
index 7f6b535c7b1..b96af06b8d7 100644
--- a/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
+++ b/src/tools/clippy/clippy_lints/src/non_expressive_names.rs
@@ -159,12 +159,10 @@ impl<'a, 'tcx, 'b> Visitor<'tcx> for SimilarNamesNameVisitor<'a, 'tcx, 'b> {
 
 #[must_use]
 fn get_exemptions(interned_name: &str) -> Option<&'static [&'static str]> {
-    for &list in ALLOWED_TO_BE_SIMILAR {
-        if allowed_to_be_similar(interned_name, list) {
-            return Some(list);
-        }
-    }
-    None
+    ALLOWED_TO_BE_SIMILAR
+        .iter()
+        .find(|&&list| allowed_to_be_similar(interned_name, list))
+        .copied()
 }
 
 #[must_use]
@@ -328,7 +326,7 @@ impl<'a, 'tcx> Visitor<'tcx> for SimilarNamesLocalVisitor<'a, 'tcx> {
         // add the pattern after the expression because the bindings aren't available
         // yet in the init
         // expression
-        SimilarNamesNameVisitor(self).visit_pat(&*local.pat);
+        SimilarNamesNameVisitor(self).visit_pat(&local.pat);
     }
     fn visit_block(&mut self, blk: &'tcx Block) {
         self.single_char_names.push(vec![]);
diff --git a/src/tools/clippy/clippy_lints/src/numeric_arithmetic.rs b/src/tools/clippy/clippy_lints/src/numeric_arithmetic.rs
deleted file mode 100644
index 5c4de338149..00000000000
--- a/src/tools/clippy/clippy_lints/src/numeric_arithmetic.rs
+++ /dev/null
@@ -1,170 +0,0 @@
-use clippy_utils::consts::constant_simple;
-use clippy_utils::diagnostics::span_lint;
-use rustc_hir as hir;
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_tool_lint, impl_lint_pass};
-use rustc_span::source_map::Span;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for integer arithmetic operations which could overflow or panic.
-    ///
-    /// Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable
-    /// of overflowing according to the [Rust
-    /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
-    /// or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is
-    /// attempted.
-    ///
-    /// ### Why is this bad?
-    /// Integer overflow will trigger a panic in debug builds or will wrap in
-    /// release mode. Division by zero will cause a panic in either mode. In some applications one
-    /// wants explicitly checked, wrapping or saturating arithmetic.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let a = 0;
-    /// a + 1;
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub INTEGER_ARITHMETIC,
-    restriction,
-    "any integer arithmetic expression which could overflow or panic"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for float arithmetic.
-    ///
-    /// ### Why is this bad?
-    /// For some embedded systems or kernel development, it
-    /// can be useful to rule out floating-point numbers.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let a = 0.0;
-    /// a + 1.0;
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub FLOAT_ARITHMETIC,
-    restriction,
-    "any floating-point arithmetic statement"
-}
-
-#[derive(Copy, Clone, Default)]
-pub struct NumericArithmetic {
-    expr_span: Option<Span>,
-    /// This field is used to check whether expressions are constants, such as in enum discriminants
-    /// and consts
-    const_span: Option<Span>,
-}
-
-impl_lint_pass!(NumericArithmetic => [INTEGER_ARITHMETIC, FLOAT_ARITHMETIC]);
-
-impl<'tcx> LateLintPass<'tcx> for NumericArithmetic {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if self.expr_span.is_some() {
-            return;
-        }
-
-        if let Some(span) = self.const_span {
-            if span.contains(expr.span) {
-                return;
-            }
-        }
-        match &expr.kind {
-            hir::ExprKind::Binary(op, l, r) | hir::ExprKind::AssignOp(op, l, r) => {
-                match op.node {
-                    hir::BinOpKind::And
-                    | hir::BinOpKind::Or
-                    | hir::BinOpKind::BitAnd
-                    | hir::BinOpKind::BitOr
-                    | hir::BinOpKind::BitXor
-                    | hir::BinOpKind::Eq
-                    | hir::BinOpKind::Lt
-                    | hir::BinOpKind::Le
-                    | hir::BinOpKind::Ne
-                    | hir::BinOpKind::Ge
-                    | hir::BinOpKind::Gt => return,
-                    _ => (),
-                }
-
-                let (l_ty, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r));
-                if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() {
-                    match op.node {
-                        hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind {
-                            hir::ExprKind::Lit(_lit) => (),
-                            hir::ExprKind::Unary(hir::UnOp::Neg, expr) => {
-                                if let hir::ExprKind::Lit(lit) = &expr.kind {
-                                    if let rustc_ast::ast::LitKind::Int(1, _) = lit.node {
-                                        span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
-                                        self.expr_span = Some(expr.span);
-                                    }
-                                }
-                            },
-                            _ => {
-                                span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
-                                self.expr_span = Some(expr.span);
-                            },
-                        },
-                        _ => {
-                            span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
-                            self.expr_span = Some(expr.span);
-                        },
-                    }
-                } else if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() {
-                    span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
-                    self.expr_span = Some(expr.span);
-                }
-            },
-            hir::ExprKind::Unary(hir::UnOp::Neg, arg) => {
-                let ty = cx.typeck_results().expr_ty(arg);
-                if constant_simple(cx, cx.typeck_results(), expr).is_none() {
-                    if ty.is_integral() {
-                        span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
-                        self.expr_span = Some(expr.span);
-                    } else if ty.is_floating_point() {
-                        span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
-                        self.expr_span = Some(expr.span);
-                    }
-                }
-            },
-            _ => (),
-        }
-    }
-
-    fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
-        if Some(expr.span) == self.expr_span {
-            self.expr_span = None;
-        }
-    }
-
-    fn check_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
-        let body_owner = cx.tcx.hir().body_owner_def_id(body.id());
-
-        match cx.tcx.hir().body_owner_kind(body_owner) {
-            hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const => {
-                let body_span = cx.tcx.def_span(body_owner);
-
-                if let Some(span) = self.const_span {
-                    if span.contains(body_span) {
-                        return;
-                    }
-                }
-                self.const_span = Some(body_span);
-            },
-            hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure => (),
-        }
-    }
-
-    fn check_body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
-        let body_owner = cx.tcx.hir().body_owner(body.id());
-        let body_span = cx.tcx.hir().span(body_owner);
-
-        if let Some(span) = self.const_span {
-            if span.contains(body_span) {
-                return;
-            }
-        }
-        self.const_span = None;
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/absurd_extreme_comparisons.rs b/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs
index 7665aa8380b..1ec4240afef 100644
--- a/src/tools/clippy/clippy_lints/src/absurd_extreme_comparisons.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/absurd_extreme_comparisons.rs
@@ -1,7 +1,6 @@
 use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
 
 use clippy_utils::comparisons::{normalize_comparison, Rel};
 use clippy_utils::consts::{constant, Constant};
@@ -10,73 +9,41 @@ use clippy_utils::source::snippet;
 use clippy_utils::ty::is_isize_or_usize;
 use clippy_utils::{clip, int_bits, unsext};
 
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for comparisons where one side of the relation is
-    /// either the minimum or maximum value for its type and warns if it involves a
-    /// case that is always true or always false. Only integer and boolean types are
-    /// checked.
-    ///
-    /// ### Why is this bad?
-    /// An expression like `min <= x` may misleadingly imply
-    /// that it is possible for `x` to be less than the minimum. Expressions like
-    /// `max < x` are probably mistakes.
-    ///
-    /// ### Known problems
-    /// For `usize` the size of the current compile target will
-    /// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such
-    /// a comparison to detect target pointer width will trigger this lint. One can
-    /// use `mem::sizeof` and compare its value or conditional compilation
-    /// attributes
-    /// like `#[cfg(target_pointer_width = "64")] ..` instead.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let vec: Vec<isize> = Vec::new();
-    /// if vec.len() <= 0 {}
-    /// if 100 > i32::MAX {}
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub ABSURD_EXTREME_COMPARISONS,
-    correctness,
-    "a comparison with a maximum or minimum value that is always true or false"
-}
+use super::ABSURD_EXTREME_COMPARISONS;
 
-declare_lint_pass!(AbsurdExtremeComparisons => [ABSURD_EXTREME_COMPARISONS]);
-
-impl<'tcx> LateLintPass<'tcx> for AbsurdExtremeComparisons {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if let ExprKind::Binary(ref cmp, lhs, rhs) = expr.kind {
-            if let Some((culprit, result)) = detect_absurd_comparison(cx, cmp.node, lhs, rhs) {
-                if !expr.span.from_expansion() {
-                    let msg = "this comparison involving the minimum or maximum element for this \
-                               type contains a case that is always true or always false";
-
-                    let conclusion = match result {
-                        AbsurdComparisonResult::AlwaysFalse => "this comparison is always false".to_owned(),
-                        AbsurdComparisonResult::AlwaysTrue => "this comparison is always true".to_owned(),
-                        AbsurdComparisonResult::InequalityImpossible => format!(
-                            "the case where the two sides are not equal never occurs, consider using `{} == {}` \
-                             instead",
-                            snippet(cx, lhs.span, "lhs"),
-                            snippet(cx, rhs.span, "rhs")
-                        ),
-                    };
-
-                    let help = format!(
-                        "because `{}` is the {} value for this type, {}",
-                        snippet(cx, culprit.expr.span, "x"),
-                        match culprit.which {
-                            ExtremeType::Minimum => "minimum",
-                            ExtremeType::Maximum => "maximum",
-                        },
-                        conclusion
-                    );
-
-                    span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help);
-                }
-            }
-        }
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    op: BinOpKind,
+    lhs: &'tcx Expr<'_>,
+    rhs: &'tcx Expr<'_>,
+) {
+    if let Some((culprit, result)) = detect_absurd_comparison(cx, op, lhs, rhs) {
+        let msg = "this comparison involving the minimum or maximum element for this \
+                           type contains a case that is always true or always false";
+
+        let conclusion = match result {
+            AbsurdComparisonResult::AlwaysFalse => "this comparison is always false".to_owned(),
+            AbsurdComparisonResult::AlwaysTrue => "this comparison is always true".to_owned(),
+            AbsurdComparisonResult::InequalityImpossible => format!(
+                "the case where the two sides are not equal never occurs, consider using `{} == {}` \
+                         instead",
+                snippet(cx, lhs.span, "lhs"),
+                snippet(cx, rhs.span, "rhs")
+            ),
+        };
+
+        let help = format!(
+            "because `{}` is the {} value for this type, {}",
+            snippet(cx, culprit.expr.span, "x"),
+            match culprit.which {
+                ExtremeType::Minimum => "minimum",
+                ExtremeType::Maximum => "maximum",
+            },
+            conclusion
+        );
+
+        span_lint_and_help(cx, ABSURD_EXTREME_COMPARISONS, expr.span, msg, None, &help);
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs
new file mode 100644
index 00000000000..979e0a66707
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs
@@ -0,0 +1,101 @@
+use clippy_utils::binop_traits;
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::ty::implements_trait;
+use clippy_utils::{eq_expr_value, trait_ref_of_method};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::intravisit::{walk_expr, Visitor};
+use rustc_lint::LateContext;
+
+use super::ASSIGN_OP_PATTERN;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'_>,
+    assignee: &'tcx hir::Expr<'_>,
+    e: &'tcx hir::Expr<'_>,
+) {
+    if let hir::ExprKind::Binary(op, l, r) = &e.kind {
+        let lint = |assignee: &hir::Expr<'_>, rhs: &hir::Expr<'_>| {
+            let ty = cx.typeck_results().expr_ty(assignee);
+            let rty = cx.typeck_results().expr_ty(rhs);
+            if_chain! {
+                if let Some((_, lang_item)) = binop_traits(op.node);
+                if let Ok(trait_id) = cx.tcx.lang_items().require(lang_item);
+                let parent_fn = cx.tcx.hir().get_parent_item(e.hir_id);
+                if trait_ref_of_method(cx, parent_fn)
+                    .map_or(true, |t| t.path.res.def_id() != trait_id);
+                if implements_trait(cx, ty, trait_id, &[rty.into()]);
+                then {
+                    span_lint_and_then(
+                        cx,
+                        ASSIGN_OP_PATTERN,
+                        expr.span,
+                        "manual implementation of an assign operation",
+                        |diag| {
+                            if let (Some(snip_a), Some(snip_r)) =
+                                (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs.span))
+                            {
+                                diag.span_suggestion(
+                                    expr.span,
+                                    "replace it with",
+                                    format!("{} {}= {}", snip_a, op.node.as_str(), snip_r),
+                                    Applicability::MachineApplicable,
+                                );
+                            }
+                        },
+                    );
+                }
+            }
+        };
+
+        let mut visitor = ExprVisitor {
+            assignee,
+            counter: 0,
+            cx,
+        };
+
+        walk_expr(&mut visitor, e);
+
+        if visitor.counter == 1 {
+            // a = a op b
+            if eq_expr_value(cx, assignee, l) {
+                lint(assignee, r);
+            }
+            // a = b commutative_op a
+            // Limited to primitive type as these ops are know to be commutative
+            if eq_expr_value(cx, assignee, r) && cx.typeck_results().expr_ty(assignee).is_primitive_ty() {
+                match op.node {
+                    hir::BinOpKind::Add
+                    | hir::BinOpKind::Mul
+                    | hir::BinOpKind::And
+                    | hir::BinOpKind::Or
+                    | hir::BinOpKind::BitXor
+                    | hir::BinOpKind::BitAnd
+                    | hir::BinOpKind::BitOr => {
+                        lint(assignee, l);
+                    },
+                    _ => {},
+                }
+            }
+        }
+    }
+}
+
+struct ExprVisitor<'a, 'tcx> {
+    assignee: &'a hir::Expr<'a>,
+    counter: u8,
+    cx: &'a LateContext<'tcx>,
+}
+
+impl<'a, 'tcx> Visitor<'tcx> for ExprVisitor<'a, 'tcx> {
+    fn visit_expr(&mut self, expr: &'tcx hir::Expr<'_>) {
+        if eq_expr_value(self.cx, self.assignee, expr) {
+            self.counter += 1;
+        }
+
+        walk_expr(self, expr);
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/bit_mask.rs b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs
index dc7e400fdc2..74387fbc87b 100644
--- a/src/tools/clippy/clippy_lints/src/bit_mask.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/bit_mask.rs
@@ -1,161 +1,23 @@
 use clippy_utils::consts::{constant, Constant};
-use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
-use clippy_utils::sugg::Sugg;
-use rustc_ast::ast::LitKind;
-use rustc_errors::Applicability;
+use clippy_utils::diagnostics::span_lint;
 use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_lint::LateContext;
 use rustc_span::source_map::Span;
 
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for incompatible bit masks in comparisons.
-    ///
-    /// The formula for detecting if an expression of the type `_ <bit_op> m
-    /// <cmp_op> c` (where `<bit_op>` is one of {`&`, `|`} and `<cmp_op>` is one of
-    /// {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following
-    /// table:
-    ///
-    /// |Comparison  |Bit Op|Example      |is always|Formula               |
-    /// |------------|------|-------------|---------|----------------------|
-    /// |`==` or `!=`| `&`  |`x & 2 == 3` |`false`  |`c & m != c`          |
-    /// |`<`  or `>=`| `&`  |`x & 2 < 3`  |`true`   |`m < c`               |
-    /// |`>`  or `<=`| `&`  |`x & 1 > 1`  |`false`  |`m <= c`              |
-    /// |`==` or `!=`| `\|` |`x \| 1 == 0`|`false`  |`c \| m != c`         |
-    /// |`<`  or `>=`| `\|` |`x \| 1 < 1` |`false`  |`m >= c`              |
-    /// |`<=` or `>` | `\|` |`x \| 1 > 0` |`true`   |`m > c`               |
-    ///
-    /// ### Why is this bad?
-    /// If the bits that the comparison cares about are always
-    /// set to zero or one by the bit mask, the comparison is constant `true` or
-    /// `false` (depending on mask, compared value, and operators).
-    ///
-    /// So the code is actively misleading, and the only reason someone would write
-    /// this intentionally is to win an underhanded Rust contest or create a
-    /// test-case for this lint.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = 1;
-    /// if (x & 1 == 2) { }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub BAD_BIT_MASK,
-    correctness,
-    "expressions of the form `_ & mask == select` that will only ever return `true` or `false`"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for bit masks in comparisons which can be removed
-    /// without changing the outcome. The basic structure can be seen in the
-    /// following table:
-    ///
-    /// |Comparison| Bit Op   |Example     |equals |
-    /// |----------|----------|------------|-------|
-    /// |`>` / `<=`|`\|` / `^`|`x \| 2 > 3`|`x > 3`|
-    /// |`<` / `>=`|`\|` / `^`|`x ^ 1 < 4` |`x < 4`|
-    ///
-    /// ### Why is this bad?
-    /// Not equally evil as [`bad_bit_mask`](#bad_bit_mask),
-    /// but still a bit misleading, because the bit mask is ineffective.
-    ///
-    /// ### Known problems
-    /// False negatives: This lint will only match instances
-    /// where we have figured out the math (which is for a power-of-two compared
-    /// value). This means things like `x | 1 >= 7` (which would be better written
-    /// as `x >= 6`) will not be reported (but bit masks like this are fairly
-    /// uncommon).
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = 1;
-    /// if (x | 1 > 3) {  }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub INEFFECTIVE_BIT_MASK,
-    correctness,
-    "expressions where a bit mask will be rendered useless by a comparison, e.g., `(x | 1) > 2`"
-}
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for bit masks that can be replaced by a call
-    /// to `trailing_zeros`
-    ///
-    /// ### Why is this bad?
-    /// `x.trailing_zeros() > 4` is much clearer than `x & 15
-    /// == 0`
-    ///
-    /// ### Known problems
-    /// llvm generates better code for `x & 15 == 0` on x86
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = 1;
-    /// if x & 0b1111 == 0 { }
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub VERBOSE_BIT_MASK,
-    pedantic,
-    "expressions where a bit mask is less readable than the corresponding method call"
-}
+use super::{BAD_BIT_MASK, INEFFECTIVE_BIT_MASK};
 
-#[derive(Copy, Clone)]
-pub struct BitMask {
-    verbose_bit_mask_threshold: u64,
-}
-
-impl BitMask {
-    #[must_use]
-    pub fn new(verbose_bit_mask_threshold: u64) -> Self {
-        Self {
-            verbose_bit_mask_threshold,
-        }
-    }
-}
-
-impl_lint_pass!(BitMask => [BAD_BIT_MASK, INEFFECTIVE_BIT_MASK, VERBOSE_BIT_MASK]);
-
-impl<'tcx> LateLintPass<'tcx> for BitMask {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        if let ExprKind::Binary(cmp, left, right) = &e.kind {
-            if cmp.node.is_comparison() {
-                if let Some(cmp_opt) = fetch_int_literal(cx, right) {
-                    check_compare(cx, left, cmp.node, cmp_opt, e.span);
-                } else if let Some(cmp_val) = fetch_int_literal(cx, left) {
-                    check_compare(cx, right, invert_cmp(cmp.node), cmp_val, e.span);
-                }
-            }
-        }
-
-        if let ExprKind::Binary(op, left, right) = &e.kind
-            && BinOpKind::Eq == op.node
-            && let ExprKind::Binary(op1, left1, right1) = &left.kind
-            && BinOpKind::BitAnd == op1.node
-            && let ExprKind::Lit(lit) = &right1.kind
-            && let LitKind::Int(n, _) = lit.node
-            && let ExprKind::Lit(lit1) = &right.kind
-            && let LitKind::Int(0, _) = lit1.node
-            && n.leading_zeros() == n.count_zeros()
-            && n > u128::from(self.verbose_bit_mask_threshold)
-        {
-            span_lint_and_then(
-                cx,
-                VERBOSE_BIT_MASK,
-                e.span,
-                "bit mask could be simplified with a call to `trailing_zeros`",
-                |diag| {
-                    let sugg = Sugg::hir(cx, left1, "...").maybe_par();
-                    diag.span_suggestion(
-                        e.span,
-                        "try",
-                        format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()),
-                        Applicability::MaybeIncorrect,
-                    );
-                },
-            );
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx Expr<'_>,
+    op: BinOpKind,
+    left: &'tcx Expr<'_>,
+    right: &'tcx Expr<'_>,
+) {
+    if op.is_comparison() {
+        if let Some(cmp_opt) = fetch_int_literal(cx, right) {
+            check_compare(cx, left, op, cmp_opt, e.span);
+        } else if let Some(cmp_val) = fetch_int_literal(cx, left) {
+            check_compare(cx, right, invert_cmp(op), cmp_val, e.span);
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs
new file mode 100644
index 00000000000..786ae1552ad
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/cmp_nan.rs
@@ -0,0 +1,30 @@
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::in_constant;
+use rustc_hir::{BinOpKind, Expr};
+use rustc_lint::LateContext;
+
+use super::CMP_NAN;
+
+pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>) {
+    if op.is_comparison() && !in_constant(cx, e.hir_id) && (is_nan(cx, lhs) || is_nan(cx, rhs)) {
+        span_lint(
+            cx,
+            CMP_NAN,
+            e.span,
+            "doomed comparison with `NAN`, use `{f32,f64}::is_nan()` instead",
+        );
+    }
+}
+
+fn is_nan(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
+    if let Some((value, _)) = constant(cx, cx.typeck_results(), e) {
+        match value {
+            Constant::F32(num) => num.is_nan(),
+            Constant::F64(num) => num.is_nan(),
+            _ => false,
+        }
+    } else {
+        false
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs
new file mode 100644
index 00000000000..e1f9b5906f6
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs
@@ -0,0 +1,147 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::{implements_trait, is_copy};
+use clippy_utils::{match_any_def_paths, path_def_id, paths};
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
+use rustc_lint::LateContext;
+use rustc_middle::ty::Ty;
+use rustc_span::symbol::sym;
+
+use super::CMP_OWNED;
+
+pub(super) fn check(cx: &LateContext<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>) {
+    if op.is_comparison() {
+        check_op(cx, lhs, rhs, true);
+        check_op(cx, rhs, lhs, false);
+    }
+}
+
+#[derive(Default)]
+struct EqImpl {
+    ty_eq_other: bool,
+    other_eq_ty: bool,
+}
+impl EqImpl {
+    fn is_implemented(&self) -> bool {
+        self.ty_eq_other || self.other_eq_ty
+    }
+}
+
+fn symmetric_partial_eq<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, other: Ty<'tcx>) -> Option<EqImpl> {
+    cx.tcx.lang_items().eq_trait().map(|def_id| EqImpl {
+        ty_eq_other: implements_trait(cx, ty, def_id, &[other.into()]),
+        other_eq_ty: implements_trait(cx, other, def_id, &[ty.into()]),
+    })
+}
+
+fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool) {
+    let typeck = cx.typeck_results();
+    let (arg, arg_span) = match expr.kind {
+        ExprKind::MethodCall(.., [arg], _)
+            if typeck
+                .type_dependent_def_id(expr.hir_id)
+                .and_then(|id| cx.tcx.trait_of_item(id))
+                .map_or(false, |id| {
+                    matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned))
+                }) =>
+        {
+            (arg, arg.span)
+        },
+        ExprKind::Call(path, [arg])
+            if path_def_id(cx, path)
+                .and_then(|id| match_any_def_paths(cx, id, &[&paths::FROM_STR_METHOD, &paths::FROM_FROM]))
+                .map_or(false, |idx| match idx {
+                    0 => true,
+                    1 => !is_copy(cx, typeck.expr_ty(expr)),
+                    _ => false,
+                }) =>
+        {
+            (arg, arg.span)
+        },
+        _ => return,
+    };
+
+    let arg_ty = typeck.expr_ty(arg);
+    let other_ty = typeck.expr_ty(other);
+
+    let without_deref = symmetric_partial_eq(cx, arg_ty, other_ty).unwrap_or_default();
+    let with_deref = arg_ty
+        .builtin_deref(true)
+        .and_then(|tam| symmetric_partial_eq(cx, tam.ty, other_ty))
+        .unwrap_or_default();
+
+    if !with_deref.is_implemented() && !without_deref.is_implemented() {
+        return;
+    }
+
+    let other_gets_derefed = matches!(other.kind, ExprKind::Unary(UnOp::Deref, _));
+
+    let lint_span = if other_gets_derefed {
+        expr.span.to(other.span)
+    } else {
+        expr.span
+    };
+
+    span_lint_and_then(
+        cx,
+        CMP_OWNED,
+        lint_span,
+        "this creates an owned instance just for comparison",
+        |diag| {
+            // This also catches `PartialEq` implementations that call `to_owned`.
+            if other_gets_derefed {
+                diag.span_label(lint_span, "try implementing the comparison without allocating");
+                return;
+            }
+
+            let arg_snip = snippet(cx, arg_span, "..");
+            let expr_snip;
+            let eq_impl;
+            if with_deref.is_implemented() {
+                expr_snip = format!("*{}", arg_snip);
+                eq_impl = with_deref;
+            } else {
+                expr_snip = arg_snip.to_string();
+                eq_impl = without_deref;
+            };
+
+            let span;
+            let hint;
+            if (eq_impl.ty_eq_other && left) || (eq_impl.other_eq_ty && !left) {
+                span = expr.span;
+                hint = expr_snip;
+            } else {
+                span = expr.span.to(other.span);
+
+                let cmp_span = if other.span < expr.span {
+                    other.span.between(expr.span)
+                } else {
+                    expr.span.between(other.span)
+                };
+                if eq_impl.ty_eq_other {
+                    hint = format!(
+                        "{}{}{}",
+                        expr_snip,
+                        snippet(cx, cmp_span, ".."),
+                        snippet(cx, other.span, "..")
+                    );
+                } else {
+                    hint = format!(
+                        "{}{}{}",
+                        snippet(cx, other.span, ".."),
+                        snippet(cx, cmp_span, ".."),
+                        expr_snip
+                    );
+                }
+            }
+
+            diag.span_suggestion(
+                span,
+                "try",
+                hint,
+                Applicability::MachineApplicable, // snippet
+            );
+        },
+    );
+}
diff --git a/src/tools/clippy/clippy_lints/src/operators/double_comparison.rs b/src/tools/clippy/clippy_lints/src/operators/double_comparison.rs
new file mode 100644
index 00000000000..56a86d0ffa2
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/double_comparison.rs
@@ -0,0 +1,54 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::eq_expr_value;
+use clippy_utils::source::snippet_with_applicability;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::source_map::Span;
+
+use super::DOUBLE_COMPARISONS;
+
+#[expect(clippy::similar_names)]
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, op: BinOpKind, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>, span: Span) {
+    let (lkind, llhs, lrhs, rkind, rlhs, rrhs) = match (&lhs.kind, &rhs.kind) {
+        (ExprKind::Binary(lb, llhs, lrhs), ExprKind::Binary(rb, rlhs, rrhs)) => {
+            (lb.node, llhs, lrhs, rb.node, rlhs, rrhs)
+        },
+        _ => return,
+    };
+    if !(eq_expr_value(cx, llhs, rlhs) && eq_expr_value(cx, lrhs, rrhs)) {
+        return;
+    }
+    macro_rules! lint_double_comparison {
+        ($op:tt) => {{
+            let mut applicability = Applicability::MachineApplicable;
+            let lhs_str = snippet_with_applicability(cx, llhs.span, "", &mut applicability);
+            let rhs_str = snippet_with_applicability(cx, lrhs.span, "", &mut applicability);
+            let sugg = format!("{} {} {}", lhs_str, stringify!($op), rhs_str);
+            span_lint_and_sugg(
+                cx,
+                DOUBLE_COMPARISONS,
+                span,
+                "this binary expression can be simplified",
+                "try",
+                sugg,
+                applicability,
+            );
+        }};
+    }
+    match (op, lkind, rkind) {
+        (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Lt) | (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Eq) => {
+            lint_double_comparison!(<=);
+        },
+        (BinOpKind::Or, BinOpKind::Eq, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Eq) => {
+            lint_double_comparison!(>=);
+        },
+        (BinOpKind::Or, BinOpKind::Lt, BinOpKind::Gt) | (BinOpKind::Or, BinOpKind::Gt, BinOpKind::Lt) => {
+            lint_double_comparison!(!=);
+        },
+        (BinOpKind::And, BinOpKind::Le, BinOpKind::Ge) | (BinOpKind::And, BinOpKind::Ge, BinOpKind::Le) => {
+            lint_double_comparison!(==);
+        },
+        _ => (),
+    };
+}
diff --git a/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs b/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs
new file mode 100644
index 00000000000..0d067d1e196
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/duration_subsec.rs
@@ -0,0 +1,44 @@
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::is_type_diagnostic_item;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::DURATION_SUBSEC;
+
+pub(crate) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    op: BinOpKind,
+    left: &'tcx Expr<'_>,
+    right: &'tcx Expr<'_>,
+) {
+    if op == BinOpKind::Div
+        && let ExprKind::MethodCall(method_path, [self_arg], _) = left.kind
+        && is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(self_arg).peel_refs(), sym::Duration)
+        && let Some((Constant::Int(divisor), _)) = constant(cx, cx.typeck_results(), right)
+    {
+        let suggested_fn = match (method_path.ident.as_str(), divisor) {
+            ("subsec_micros", 1_000) | ("subsec_nanos", 1_000_000) => "subsec_millis",
+            ("subsec_nanos", 1_000) => "subsec_micros",
+            _ => return,
+        };
+        let mut applicability = Applicability::MachineApplicable;
+        span_lint_and_sugg(
+            cx,
+            DURATION_SUBSEC,
+            expr.span,
+            &format!("calling `{}()` is more concise than this calculation", suggested_fn),
+            "try",
+            format!(
+                "{}.{}()",
+                snippet_with_applicability(cx, self_arg.span, "_", &mut applicability),
+                suggested_fn
+            ),
+            applicability,
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/operators/eq_op.rs b/src/tools/clippy/clippy_lints/src/operators/eq_op.rs
new file mode 100644
index 00000000000..44cf0bb0612
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/eq_op.rs
@@ -0,0 +1,45 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::macros::{find_assert_eq_args, first_node_macro_backtrace};
+use clippy_utils::{ast_utils::is_useless_with_eq_exprs, eq_expr_value, is_in_test_function};
+use rustc_hir::{BinOpKind, Expr};
+use rustc_lint::LateContext;
+
+use super::EQ_OP;
+
+pub(crate) fn check_assert<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+    if let Some((macro_call, macro_name))
+        = first_node_macro_backtrace(cx, e).find_map(|macro_call| {
+            let name = cx.tcx.item_name(macro_call.def_id);
+            matches!(name.as_str(), "assert_eq" | "assert_ne" | "debug_assert_eq" | "debug_assert_ne")
+                .then(|| (macro_call, name))
+        })
+        && let Some((lhs, rhs, _)) = find_assert_eq_args(cx, e, macro_call.expn)
+        && eq_expr_value(cx, lhs, rhs)
+        && macro_call.is_local()
+        && !is_in_test_function(cx.tcx, e.hir_id)
+    {
+        span_lint(
+            cx,
+            EQ_OP,
+            lhs.span.to(rhs.span),
+            &format!("identical args used in this `{}!` macro call", macro_name),
+        );
+    }
+}
+
+pub(crate) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx Expr<'_>,
+    op: BinOpKind,
+    left: &'tcx Expr<'_>,
+    right: &'tcx Expr<'_>,
+) {
+    if is_useless_with_eq_exprs(op.into()) && eq_expr_value(cx, left, right) && !is_in_test_function(cx.tcx, e.hir_id) {
+        span_lint(
+            cx,
+            EQ_OP,
+            e.span,
+            &format!("equal expressions as operands to `{}`", op.as_str()),
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/operators/erasing_op.rs b/src/tools/clippy/clippy_lints/src/operators/erasing_op.rs
new file mode 100644
index 00000000000..066e08f3bd4
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/erasing_op.rs
@@ -0,0 +1,53 @@
+use clippy_utils::consts::{constant_simple, Constant};
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::same_type_and_consts;
+
+use rustc_hir::{BinOpKind, Expr};
+use rustc_lint::LateContext;
+use rustc_middle::ty::TypeckResults;
+
+use super::ERASING_OP;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx Expr<'_>,
+    op: BinOpKind,
+    left: &'tcx Expr<'_>,
+    right: &'tcx Expr<'_>,
+) {
+    let tck = cx.typeck_results();
+    match op {
+        BinOpKind::Mul | BinOpKind::BitAnd => {
+            check_op(cx, tck, left, right, e);
+            check_op(cx, tck, right, left, e);
+        },
+        BinOpKind::Div => check_op(cx, tck, left, right, e),
+        _ => (),
+    }
+}
+
+fn different_types(tck: &TypeckResults<'_>, input: &Expr<'_>, output: &Expr<'_>) -> bool {
+    let input_ty = tck.expr_ty(input).peel_refs();
+    let output_ty = tck.expr_ty(output).peel_refs();
+    !same_type_and_consts(input_ty, output_ty)
+}
+
+fn check_op<'tcx>(
+    cx: &LateContext<'tcx>,
+    tck: &TypeckResults<'tcx>,
+    op: &Expr<'tcx>,
+    other: &Expr<'tcx>,
+    parent: &Expr<'tcx>,
+) {
+    if constant_simple(cx, tck, op) == Some(Constant::Int(0)) {
+        if different_types(tck, other, parent) {
+            return;
+        }
+        span_lint(
+            cx,
+            ERASING_OP,
+            parent.span,
+            "this operation will always return zero. This is likely not the intended outcome",
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs
new file mode 100644
index 00000000000..0ef793443ff
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs
@@ -0,0 +1,139 @@
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::get_item_name;
+use clippy_utils::sugg::Sugg;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind, UnOp};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+
+use super::{FLOAT_CMP, FLOAT_CMP_CONST};
+
+pub(crate) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    op: BinOpKind,
+    left: &'tcx Expr<'_>,
+    right: &'tcx Expr<'_>,
+) {
+    if (op == BinOpKind::Eq || op == BinOpKind::Ne) && (is_float(cx, left) || is_float(cx, right)) {
+        if is_allowed(cx, left) || is_allowed(cx, right) {
+            return;
+        }
+
+        // Allow comparing the results of signum()
+        if is_signum(cx, left) && is_signum(cx, right) {
+            return;
+        }
+
+        if let Some(name) = get_item_name(cx, expr) {
+            let name = name.as_str();
+            if name == "eq" || name == "ne" || name == "is_nan" || name.starts_with("eq_") || name.ends_with("_eq") {
+                return;
+            }
+        }
+        let is_comparing_arrays = is_array(cx, left) || is_array(cx, right);
+        let (lint, msg) = get_lint_and_message(
+            is_named_constant(cx, left) || is_named_constant(cx, right),
+            is_comparing_arrays,
+        );
+        span_lint_and_then(cx, lint, expr.span, msg, |diag| {
+            let lhs = Sugg::hir(cx, left, "..");
+            let rhs = Sugg::hir(cx, right, "..");
+
+            if !is_comparing_arrays {
+                diag.span_suggestion(
+                    expr.span,
+                    "consider comparing them within some margin of error",
+                    format!(
+                        "({}).abs() {} error_margin",
+                        lhs - rhs,
+                        if op == BinOpKind::Eq { '<' } else { '>' }
+                    ),
+                    Applicability::HasPlaceholders, // snippet
+                );
+            }
+            diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`");
+        });
+    }
+}
+
+fn get_lint_and_message(
+    is_comparing_constants: bool,
+    is_comparing_arrays: bool,
+) -> (&'static rustc_lint::Lint, &'static str) {
+    if is_comparing_constants {
+        (
+            FLOAT_CMP_CONST,
+            if is_comparing_arrays {
+                "strict comparison of `f32` or `f64` constant arrays"
+            } else {
+                "strict comparison of `f32` or `f64` constant"
+            },
+        )
+    } else {
+        (
+            FLOAT_CMP,
+            if is_comparing_arrays {
+                "strict comparison of `f32` or `f64` arrays"
+            } else {
+                "strict comparison of `f32` or `f64`"
+            },
+        )
+    }
+}
+
+fn is_named_constant<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+    if let Some((_, res)) = constant(cx, cx.typeck_results(), expr) {
+        res
+    } else {
+        false
+    }
+}
+
+fn is_allowed<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) -> bool {
+    match constant(cx, cx.typeck_results(), expr) {
+        Some((Constant::F32(f), _)) => f == 0.0 || f.is_infinite(),
+        Some((Constant::F64(f), _)) => f == 0.0 || f.is_infinite(),
+        Some((Constant::Vec(vec), _)) => vec.iter().all(|f| match f {
+            Constant::F32(f) => *f == 0.0 || (*f).is_infinite(),
+            Constant::F64(f) => *f == 0.0 || (*f).is_infinite(),
+            _ => false,
+        }),
+        _ => false,
+    }
+}
+
+// Return true if `expr` is the result of `signum()` invoked on a float value.
+fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    // The negation of a signum is still a signum
+    if let ExprKind::Unary(UnOp::Neg, child_expr) = expr.kind {
+        return is_signum(cx, child_expr);
+    }
+
+    if_chain! {
+        if let ExprKind::MethodCall(method_name, [ref self_arg, ..], _) = expr.kind;
+        if sym!(signum) == method_name.ident.name;
+        // Check that the receiver of the signum() is a float (expressions[0] is the receiver of
+        // the method call)
+        then {
+            return is_float(cx, self_arg);
+        }
+    }
+    false
+}
+
+fn is_float(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    let value = &cx.typeck_results().expr_ty(expr).peel_refs().kind();
+
+    if let ty::Array(arr_ty, _) = value {
+        return matches!(arr_ty.kind(), ty::Float(_));
+    };
+
+    matches!(value, ty::Float(_))
+}
+
+fn is_array(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    matches!(&cx.typeck_results().expr_ty(expr).peel_refs().kind(), ty::Array(_, _))
+}
diff --git a/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs
new file mode 100644
index 00000000000..a0a8b6aabd9
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/float_equality_without_abs.rs
@@ -0,0 +1,71 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::{match_def_path, paths, sugg};
+use if_chain::if_chain;
+use rustc_ast::util::parser::AssocOp;
+use rustc_errors::Applicability;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+use rustc_span::source_map::Spanned;
+
+use super::FLOAT_EQUALITY_WITHOUT_ABS;
+
+pub(crate) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    op: BinOpKind,
+    lhs: &'tcx Expr<'_>,
+    rhs: &'tcx Expr<'_>,
+) {
+    let (lhs, rhs) = match op {
+        BinOpKind::Lt => (lhs, rhs),
+        BinOpKind::Gt => (rhs, lhs),
+        _ => return,
+    };
+
+    if_chain! {
+        // left hand side is a subtraction
+        if let ExprKind::Binary(
+            Spanned {
+                node: BinOpKind::Sub,
+                ..
+            },
+            val_l,
+            val_r,
+        ) = lhs.kind;
+
+        // right hand side matches either f32::EPSILON or f64::EPSILON
+        if let ExprKind::Path(ref epsilon_path) = rhs.kind;
+        if let Res::Def(DefKind::AssocConst, def_id) = cx.qpath_res(epsilon_path, rhs.hir_id);
+        if match_def_path(cx, def_id, &paths::F32_EPSILON) || match_def_path(cx, def_id, &paths::F64_EPSILON);
+
+        // values of the subtractions on the left hand side are of the type float
+        let t_val_l = cx.typeck_results().expr_ty(val_l);
+        let t_val_r = cx.typeck_results().expr_ty(val_r);
+        if let ty::Float(_) = t_val_l.kind();
+        if let ty::Float(_) = t_val_r.kind();
+
+        then {
+            let sug_l = sugg::Sugg::hir(cx, val_l, "..");
+            let sug_r = sugg::Sugg::hir(cx, val_r, "..");
+            // format the suggestion
+            let suggestion = format!("{}.abs()", sugg::make_assoc(AssocOp::Subtract, &sug_l, &sug_r).maybe_par());
+            // spans the lint
+            span_lint_and_then(
+                cx,
+                FLOAT_EQUALITY_WITHOUT_ABS,
+                expr.span,
+                "float equality check without `.abs()`",
+                | diag | {
+                    diag.span_suggestion(
+                        lhs.span,
+                        "add `.abs()`",
+                        suggestion,
+                        Applicability::MaybeIncorrect,
+                    );
+                }
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/identity_op.rs b/src/tools/clippy/clippy_lints/src/operators/identity_op.rs
index 419ea5a6811..b48d6c4e2e2 100644
--- a/src/tools/clippy/clippy_lints/src/identity_op.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/identity_op.rs
@@ -3,61 +3,40 @@ use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::{clip, unsext};
 use rustc_errors::Applicability;
-use rustc_hir::{BinOp, BinOpKind, Expr, ExprKind, Node};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_hir::{BinOpKind, Expr, ExprKind, Node};
+use rustc_lint::LateContext;
 use rustc_middle::ty;
-use rustc_session::{declare_lint_pass, declare_tool_lint};
 use rustc_span::source_map::Span;
 
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for identity operations, e.g., `x + 0`.
-    ///
-    /// ### Why is this bad?
-    /// This code can be removed without changing the
-    /// meaning. So it just obscures what's going on. Delete it mercilessly.
-    ///
-    /// ### Example
-    /// ```rust
-    /// # let x = 1;
-    /// x / 1 + 0 * 1 - 0 | 0;
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub IDENTITY_OP,
-    complexity,
-    "using identity operations, e.g., `x + 0` or `y / 1`"
-}
-
-declare_lint_pass!(IdentityOp => [IDENTITY_OP]);
+use super::IDENTITY_OP;
 
-impl<'tcx> LateLintPass<'tcx> for IdentityOp {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if expr.span.from_expansion() {
-            return;
-        }
-        if let ExprKind::Binary(cmp, left, right) = &expr.kind {
-            if !is_allowed(cx, *cmp, left, right) {
-                match cmp.node {
-                    BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
-                        check(cx, left, 0, expr.span, right.span, needs_parenthesis(cx, expr, right));
-                        check(cx, right, 0, expr.span, left.span, Parens::Unneeded);
-                    },
-                    BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
-                        check(cx, right, 0, expr.span, left.span, Parens::Unneeded);
-                    },
-                    BinOpKind::Mul => {
-                        check(cx, left, 1, expr.span, right.span, needs_parenthesis(cx, expr, right));
-                        check(cx, right, 1, expr.span, left.span, Parens::Unneeded);
-                    },
-                    BinOpKind::Div => check(cx, right, 1, expr.span, left.span, Parens::Unneeded),
-                    BinOpKind::BitAnd => {
-                        check(cx, left, -1, expr.span, right.span, needs_parenthesis(cx, expr, right));
-                        check(cx, right, -1, expr.span, left.span, Parens::Unneeded);
-                    },
-                    BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span),
-                    _ => (),
-                }
-            }
+pub(crate) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    op: BinOpKind,
+    left: &'tcx Expr<'_>,
+    right: &'tcx Expr<'_>,
+) {
+    if !is_allowed(cx, op, left, right) {
+        match op {
+            BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
+                check_op(cx, left, 0, expr.span, right.span, needs_parenthesis(cx, expr, right));
+                check_op(cx, right, 0, expr.span, left.span, Parens::Unneeded);
+            },
+            BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
+                check_op(cx, right, 0, expr.span, left.span, Parens::Unneeded);
+            },
+            BinOpKind::Mul => {
+                check_op(cx, left, 1, expr.span, right.span, needs_parenthesis(cx, expr, right));
+                check_op(cx, right, 1, expr.span, left.span, Parens::Unneeded);
+            },
+            BinOpKind::Div => check_op(cx, right, 1, expr.span, left.span, Parens::Unneeded),
+            BinOpKind::BitAnd => {
+                check_op(cx, left, -1, expr.span, right.span, needs_parenthesis(cx, expr, right));
+                check_op(cx, right, -1, expr.span, left.span, Parens::Unneeded);
+            },
+            BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span),
+            _ => (),
         }
     }
 }
@@ -108,12 +87,12 @@ fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>)
     Parens::Needed
 }
 
-fn is_allowed(cx: &LateContext<'_>, cmp: BinOp, left: &Expr<'_>, right: &Expr<'_>) -> bool {
+fn is_allowed(cx: &LateContext<'_>, cmp: BinOpKind, left: &Expr<'_>, right: &Expr<'_>) -> bool {
     // This lint applies to integers
     !cx.typeck_results().expr_ty(left).peel_refs().is_integral()
         || !cx.typeck_results().expr_ty(right).peel_refs().is_integral()
         // `1 << 0` is a common pattern in bit manipulation code
-        || (cmp.node == BinOpKind::Shl
+        || (cmp == BinOpKind::Shl
             && constant_simple(cx, cx.typeck_results(), right) == Some(Constant::Int(0))
             && constant_simple(cx, cx.typeck_results(), left) == Some(Constant::Int(1)))
 }
@@ -130,7 +109,7 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span
     }
 }
 
-fn check(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens) {
+fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens) {
     if let Some(Constant::Int(v)) = constant_simple(cx, cx.typeck_results(), e).map(Constant::peel_refs) {
         let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
             ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
diff --git a/src/tools/clippy/clippy_lints/src/operators/integer_division.rs b/src/tools/clippy/clippy_lints/src/operators/integer_division.rs
new file mode 100644
index 00000000000..631d10f4a72
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/integer_division.rs
@@ -0,0 +1,27 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+
+use super::INTEGER_DIVISION;
+
+pub(crate) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'_>,
+    op: hir::BinOpKind,
+    left: &'tcx hir::Expr<'_>,
+    right: &'tcx hir::Expr<'_>,
+) {
+    if op == hir::BinOpKind::Div
+        && cx.typeck_results().expr_ty(left).is_integral()
+        && cx.typeck_results().expr_ty(right).is_integral()
+    {
+        span_lint_and_help(
+            cx,
+            INTEGER_DIVISION,
+            expr.span,
+            "integer division",
+            None,
+            "division of integers may cause loss of precision. consider using floats",
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs b/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs
new file mode 100644
index 00000000000..0024384d927
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/misrefactored_assign_op.rs
@@ -0,0 +1,84 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::eq_expr_value;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::sugg;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+
+use super::MISREFACTORED_ASSIGN_OP;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx hir::Expr<'_>,
+    op: hir::BinOpKind,
+    lhs: &'tcx hir::Expr<'_>,
+    rhs: &'tcx hir::Expr<'_>,
+) {
+    if let hir::ExprKind::Binary(binop, l, r) = &rhs.kind {
+        if op != binop.node {
+            return;
+        }
+        // lhs op= l op r
+        if eq_expr_value(cx, lhs, l) {
+            lint_misrefactored_assign_op(cx, expr, op, rhs, lhs, r);
+        }
+        // lhs op= l commutative_op r
+        if is_commutative(op) && eq_expr_value(cx, lhs, r) {
+            lint_misrefactored_assign_op(cx, expr, op, rhs, lhs, l);
+        }
+    }
+}
+
+fn lint_misrefactored_assign_op(
+    cx: &LateContext<'_>,
+    expr: &hir::Expr<'_>,
+    op: hir::BinOpKind,
+    rhs: &hir::Expr<'_>,
+    assignee: &hir::Expr<'_>,
+    rhs_other: &hir::Expr<'_>,
+) {
+    span_lint_and_then(
+        cx,
+        MISREFACTORED_ASSIGN_OP,
+        expr.span,
+        "variable appears on both sides of an assignment operation",
+        |diag| {
+            if let (Some(snip_a), Some(snip_r)) = (snippet_opt(cx, assignee.span), snippet_opt(cx, rhs_other.span)) {
+                let a = &sugg::Sugg::hir(cx, assignee, "..");
+                let r = &sugg::Sugg::hir(cx, rhs, "..");
+                let long = format!("{} = {}", snip_a, sugg::make_binop(op.into(), a, r));
+                diag.span_suggestion(
+                    expr.span,
+                    &format!(
+                        "did you mean `{} = {} {} {}` or `{}`? Consider replacing it with",
+                        snip_a,
+                        snip_a,
+                        op.as_str(),
+                        snip_r,
+                        long
+                    ),
+                    format!("{} {}= {}", snip_a, op.as_str(), snip_r),
+                    Applicability::MaybeIncorrect,
+                );
+                diag.span_suggestion(
+                    expr.span,
+                    "or",
+                    long,
+                    Applicability::MaybeIncorrect, // snippet
+                );
+            }
+        },
+    );
+}
+
+#[must_use]
+fn is_commutative(op: hir::BinOpKind) -> bool {
+    use rustc_hir::BinOpKind::{
+        Add, And, BitAnd, BitOr, BitXor, Div, Eq, Ge, Gt, Le, Lt, Mul, Ne, Or, Rem, Shl, Shr, Sub,
+    };
+    match op {
+        Add | Mul | And | Or | BitXor | BitAnd | BitOr | Eq | Ne => true,
+        Sub | Div | Rem | Shl | Shr | Lt | Le | Ge | Gt => false,
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs
new file mode 100644
index 00000000000..35fe405bcf1
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs
@@ -0,0 +1,849 @@
+use rustc_hir::{Body, Expr, ExprKind, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::{declare_tool_lint, impl_lint_pass};
+
+mod absurd_extreme_comparisons;
+mod assign_op_pattern;
+mod bit_mask;
+mod cmp_nan;
+mod cmp_owned;
+mod double_comparison;
+mod duration_subsec;
+mod eq_op;
+mod erasing_op;
+mod float_cmp;
+mod float_equality_without_abs;
+mod identity_op;
+mod integer_division;
+mod misrefactored_assign_op;
+mod modulo_arithmetic;
+mod modulo_one;
+mod needless_bitwise_bool;
+mod numeric_arithmetic;
+mod op_ref;
+mod ptr_eq;
+mod self_assignment;
+mod verbose_bit_mask;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for comparisons where one side of the relation is
+    /// either the minimum or maximum value for its type and warns if it involves a
+    /// case that is always true or always false. Only integer and boolean types are
+    /// checked.
+    ///
+    /// ### Why is this bad?
+    /// An expression like `min <= x` may misleadingly imply
+    /// that it is possible for `x` to be less than the minimum. Expressions like
+    /// `max < x` are probably mistakes.
+    ///
+    /// ### Known problems
+    /// For `usize` the size of the current compile target will
+    /// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such
+    /// a comparison to detect target pointer width will trigger this lint. One can
+    /// use `mem::sizeof` and compare its value or conditional compilation
+    /// attributes
+    /// like `#[cfg(target_pointer_width = "64")] ..` instead.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let vec: Vec<isize> = Vec::new();
+    /// if vec.len() <= 0 {}
+    /// if 100 > i32::MAX {}
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub ABSURD_EXTREME_COMPARISONS,
+    correctness,
+    "a comparison with a maximum or minimum value that is always true or false"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for integer arithmetic operations which could overflow or panic.
+    ///
+    /// Specifically, checks for any operators (`+`, `-`, `*`, `<<`, etc) which are capable
+    /// of overflowing according to the [Rust
+    /// Reference](https://doc.rust-lang.org/reference/expressions/operator-expr.html#overflow),
+    /// or which can panic (`/`, `%`). No bounds analysis or sophisticated reasoning is
+    /// attempted.
+    ///
+    /// ### Why is this bad?
+    /// Integer overflow will trigger a panic in debug builds or will wrap in
+    /// release mode. Division by zero will cause a panic in either mode. In some applications one
+    /// wants explicitly checked, wrapping or saturating arithmetic.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let a = 0;
+    /// a + 1;
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub INTEGER_ARITHMETIC,
+    restriction,
+    "any integer arithmetic expression which could overflow or panic"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for float arithmetic.
+    ///
+    /// ### Why is this bad?
+    /// For some embedded systems or kernel development, it
+    /// can be useful to rule out floating-point numbers.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let a = 0.0;
+    /// a + 1.0;
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub FLOAT_ARITHMETIC,
+    restriction,
+    "any floating-point arithmetic statement"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `a = a op b` or `a = b commutative_op a`
+    /// patterns.
+    ///
+    /// ### Why is this bad?
+    /// These can be written as the shorter `a op= b`.
+    ///
+    /// ### Known problems
+    /// While forbidden by the spec, `OpAssign` traits may have
+    /// implementations that differ from the regular `Op` impl.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let mut a = 5;
+    /// let b = 0;
+    /// // ...
+    ///
+    /// a = a + b;
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// let mut a = 5;
+    /// let b = 0;
+    /// // ...
+    ///
+    /// a += b;
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub ASSIGN_OP_PATTERN,
+    style,
+    "assigning the result of an operation on a variable to that same variable"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for `a op= a op b` or `a op= b op a` patterns.
+    ///
+    /// ### Why is this bad?
+    /// Most likely these are bugs where one meant to write `a
+    /// op= b`.
+    ///
+    /// ### Known problems
+    /// Clippy cannot know for sure if `a op= a op b` should have
+    /// been `a = a op a op b` or `a = a op b`/`a op= b`. Therefore, it suggests both.
+    /// If `a op= a op b` is really the correct behavior it should be
+    /// written as `a = a op a op b` as it's less confusing.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let mut a = 5;
+    /// let b = 2;
+    /// // ...
+    /// a += a + b;
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub MISREFACTORED_ASSIGN_OP,
+    suspicious,
+    "having a variable on both sides of an assign op"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for incompatible bit masks in comparisons.
+    ///
+    /// The formula for detecting if an expression of the type `_ <bit_op> m
+    /// <cmp_op> c` (where `<bit_op>` is one of {`&`, `|`} and `<cmp_op>` is one of
+    /// {`!=`, `>=`, `>`, `!=`, `>=`, `>`}) can be determined from the following
+    /// table:
+    ///
+    /// |Comparison  |Bit Op|Example      |is always|Formula               |
+    /// |------------|------|-------------|---------|----------------------|
+    /// |`==` or `!=`| `&`  |`x & 2 == 3` |`false`  |`c & m != c`          |
+    /// |`<`  or `>=`| `&`  |`x & 2 < 3`  |`true`   |`m < c`               |
+    /// |`>`  or `<=`| `&`  |`x & 1 > 1`  |`false`  |`m <= c`              |
+    /// |`==` or `!=`| `\|` |`x \| 1 == 0`|`false`  |`c \| m != c`         |
+    /// |`<`  or `>=`| `\|` |`x \| 1 < 1` |`false`  |`m >= c`              |
+    /// |`<=` or `>` | `\|` |`x \| 1 > 0` |`true`   |`m > c`               |
+    ///
+    /// ### Why is this bad?
+    /// If the bits that the comparison cares about are always
+    /// set to zero or one by the bit mask, the comparison is constant `true` or
+    /// `false` (depending on mask, compared value, and operators).
+    ///
+    /// So the code is actively misleading, and the only reason someone would write
+    /// this intentionally is to win an underhanded Rust contest or create a
+    /// test-case for this lint.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = 1;
+    /// if (x & 1 == 2) { }
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub BAD_BIT_MASK,
+    correctness,
+    "expressions of the form `_ & mask == select` that will only ever return `true` or `false`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for bit masks in comparisons which can be removed
+    /// without changing the outcome. The basic structure can be seen in the
+    /// following table:
+    ///
+    /// |Comparison| Bit Op   |Example     |equals |
+    /// |----------|----------|------------|-------|
+    /// |`>` / `<=`|`\|` / `^`|`x \| 2 > 3`|`x > 3`|
+    /// |`<` / `>=`|`\|` / `^`|`x ^ 1 < 4` |`x < 4`|
+    ///
+    /// ### Why is this bad?
+    /// Not equally evil as [`bad_bit_mask`](#bad_bit_mask),
+    /// but still a bit misleading, because the bit mask is ineffective.
+    ///
+    /// ### Known problems
+    /// False negatives: This lint will only match instances
+    /// where we have figured out the math (which is for a power-of-two compared
+    /// value). This means things like `x | 1 >= 7` (which would be better written
+    /// as `x >= 6`) will not be reported (but bit masks like this are fairly
+    /// uncommon).
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = 1;
+    /// if (x | 1 > 3) {  }
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub INEFFECTIVE_BIT_MASK,
+    correctness,
+    "expressions where a bit mask will be rendered useless by a comparison, e.g., `(x | 1) > 2`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for bit masks that can be replaced by a call
+    /// to `trailing_zeros`
+    ///
+    /// ### Why is this bad?
+    /// `x.trailing_zeros() > 4` is much clearer than `x & 15
+    /// == 0`
+    ///
+    /// ### Known problems
+    /// llvm generates better code for `x & 15 == 0` on x86
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = 1;
+    /// if x & 0b1111 == 0 { }
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub VERBOSE_BIT_MASK,
+    pedantic,
+    "expressions where a bit mask is less readable than the corresponding method call"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for double comparisons that could be simplified to a single expression.
+    ///
+    ///
+    /// ### Why is this bad?
+    /// Readability.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = 1;
+    /// # let y = 2;
+    /// if x == y || x < y {}
+    /// ```
+    ///
+    /// Use instead:
+    ///
+    /// ```rust
+    /// # let x = 1;
+    /// # let y = 2;
+    /// if x <= y {}
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub DOUBLE_COMPARISONS,
+    complexity,
+    "unnecessary double comparisons that can be simplified"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for calculation of subsecond microseconds or milliseconds
+    /// from other `Duration` methods.
+    ///
+    /// ### Why is this bad?
+    /// It's more concise to call `Duration::subsec_micros()` or
+    /// `Duration::subsec_millis()` than to calculate them.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # use std::time::Duration;
+    /// # let duration = Duration::new(5, 0);
+    /// let micros = duration.subsec_nanos() / 1_000;
+    /// let millis = duration.subsec_nanos() / 1_000_000;
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// # use std::time::Duration;
+    /// # let duration = Duration::new(5, 0);
+    /// let micros = duration.subsec_micros();
+    /// let millis = duration.subsec_millis();
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub DURATION_SUBSEC,
+    complexity,
+    "checks for calculation of subsecond microseconds or milliseconds"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for equal operands to comparison, logical and
+    /// bitwise, difference and division binary operators (`==`, `>`, etc., `&&`,
+    /// `||`, `&`, `|`, `^`, `-` and `/`).
+    ///
+    /// ### Why is this bad?
+    /// This is usually just a typo or a copy and paste error.
+    ///
+    /// ### Known problems
+    /// False negatives: We had some false positives regarding
+    /// calls (notably [racer](https://github.com/phildawes/racer) had one instance
+    /// of `x.pop() && x.pop()`), so we removed matching any function or method
+    /// calls. We may introduce a list of known pure functions in the future.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = 1;
+    /// if x + 1 == x + 1 {}
+    ///
+    /// // or
+    ///
+    /// # let a = 3;
+    /// # let b = 4;
+    /// assert_eq!(a, a);
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub EQ_OP,
+    correctness,
+    "equal operands on both sides of a comparison or bitwise combination (e.g., `x == x`)"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for arguments to `==` which have their address
+    /// taken to satisfy a bound
+    /// and suggests to dereference the other argument instead
+    ///
+    /// ### Why is this bad?
+    /// It is more idiomatic to dereference the other argument.
+    ///
+    /// ### Example
+    /// ```rust,ignore
+    /// &x == y
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust,ignore
+    /// x == *y
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub OP_REF,
+    style,
+    "taking a reference to satisfy the type constraints on `==`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for erasing operations, e.g., `x * 0`.
+    ///
+    /// ### Why is this bad?
+    /// The whole expression can be replaced by zero.
+    /// This is most likely not the intended outcome and should probably be
+    /// corrected
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x = 1;
+    /// 0 / x;
+    /// 0 * x;
+    /// x & 0;
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub ERASING_OP,
+    correctness,
+    "using erasing operations, e.g., `x * 0` or `y & 0`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for statements of the form `(a - b) < f32::EPSILON` or
+    /// `(a - b) < f64::EPSILON`. Notes the missing `.abs()`.
+    ///
+    /// ### Why is this bad?
+    /// The code without `.abs()` is more likely to have a bug.
+    ///
+    /// ### Known problems
+    /// If the user can ensure that b is larger than a, the `.abs()` is
+    /// technically unnecessary. However, it will make the code more robust and doesn't have any
+    /// large performance implications. If the abs call was deliberately left out for performance
+    /// reasons, it is probably better to state this explicitly in the code, which then can be done
+    /// with an allow.
+    ///
+    /// ### Example
+    /// ```rust
+    /// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
+    ///     (a - b) < f32::EPSILON
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// pub fn is_roughly_equal(a: f32, b: f32) -> bool {
+    ///     (a - b).abs() < f32::EPSILON
+    /// }
+    /// ```
+    #[clippy::version = "1.48.0"]
+    pub FLOAT_EQUALITY_WITHOUT_ABS,
+    suspicious,
+    "float equality check without `.abs()`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for identity operations, e.g., `x + 0`.
+    ///
+    /// ### Why is this bad?
+    /// This code can be removed without changing the
+    /// meaning. So it just obscures what's going on. Delete it mercilessly.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = 1;
+    /// x / 1 + 0 * 1 - 0 | 0;
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub IDENTITY_OP,
+    complexity,
+    "using identity operations, e.g., `x + 0` or `y / 1`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for division of integers
+    ///
+    /// ### Why is this bad?
+    /// When outside of some very specific algorithms,
+    /// integer division is very often a mistake because it discards the
+    /// remainder.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x = 3 / 2;
+    /// println!("{}", x);
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// let x = 3f32 / 2f32;
+    /// println!("{}", x);
+    /// ```
+    #[clippy::version = "1.37.0"]
+    pub INTEGER_DIVISION,
+    restriction,
+    "integer division may cause loss of precision"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for comparisons to NaN.
+    ///
+    /// ### Why is this bad?
+    /// NaN does not compare meaningfully to anything – not
+    /// even itself – so those comparisons are simply wrong.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = 1.0;
+    /// if x == f32::NAN { }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// # let x = 1.0f32;
+    /// if x.is_nan() { }
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub CMP_NAN,
+    correctness,
+    "comparisons to `NAN`, which will always return false, probably not intended"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for conversions to owned values just for the sake
+    /// of a comparison.
+    ///
+    /// ### Why is this bad?
+    /// The comparison can operate on a reference, so creating
+    /// an owned value effectively throws it away directly afterwards, which is
+    /// needlessly consuming code and heap space.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = "foo";
+    /// # let y = String::from("foo");
+    /// if x.to_owned() == y {}
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// # let x = "foo";
+    /// # let y = String::from("foo");
+    /// if x == y {}
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub CMP_OWNED,
+    perf,
+    "creating owned instances for comparing with others, e.g., `x == \"foo\".to_string()`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for (in-)equality comparisons on floating-point
+    /// values (apart from zero), except in functions called `*eq*` (which probably
+    /// implement equality for a type involving floats).
+    ///
+    /// ### Why is this bad?
+    /// Floating point calculations are usually imprecise, so
+    /// asking if two values are *exactly* equal is asking for trouble. For a good
+    /// guide on what to do, see [the floating point
+    /// guide](http://www.floating-point-gui.de/errors/comparison).
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x = 1.2331f64;
+    /// let y = 1.2332f64;
+    ///
+    /// if y == 1.23f64 { }
+    /// if y != x {} // where both are floats
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// # let x = 1.2331f64;
+    /// # let y = 1.2332f64;
+    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
+    /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
+    /// // let error_margin = std::f64::EPSILON;
+    /// if (y - 1.23f64).abs() < error_margin { }
+    /// if (y - x).abs() > error_margin { }
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub FLOAT_CMP,
+    pedantic,
+    "using `==` or `!=` on float values instead of comparing difference with an epsilon"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for (in-)equality comparisons on floating-point
+    /// value and constant, except in functions called `*eq*` (which probably
+    /// implement equality for a type involving floats).
+    ///
+    /// ### Why is this bad?
+    /// Floating point calculations are usually imprecise, so
+    /// asking if two values are *exactly* equal is asking for trouble. For a good
+    /// guide on what to do, see [the floating point
+    /// guide](http://www.floating-point-gui.de/errors/comparison).
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x: f64 = 1.0;
+    /// const ONE: f64 = 1.00;
+    ///
+    /// if x == ONE { } // where both are floats
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// # let x: f64 = 1.0;
+    /// # const ONE: f64 = 1.00;
+    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
+    /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
+    /// // let error_margin = std::f64::EPSILON;
+    /// if (x - ONE).abs() < error_margin { }
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub FLOAT_CMP_CONST,
+    restriction,
+    "using `==` or `!=` on float constants instead of comparing difference with an epsilon"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for getting the remainder of a division by one or minus
+    /// one.
+    ///
+    /// ### Why is this bad?
+    /// The result for a divisor of one can only ever be zero; for
+    /// minus one it can cause panic/overflow (if the left operand is the minimal value of
+    /// the respective integer type) or results in zero. No one will write such code
+    /// deliberately, unless trying to win an Underhanded Rust Contest. Even for that
+    /// contest, it's probably a bad idea. Use something more underhanded.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # let x = 1;
+    /// let a = x % 1;
+    /// let a = x % -1;
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub MODULO_ONE,
+    correctness,
+    "taking a number modulo +/-1, which can either panic/overflow or always returns 0"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for modulo arithmetic.
+    ///
+    /// ### Why is this bad?
+    /// The results of modulo (%) operation might differ
+    /// depending on the language, when negative numbers are involved.
+    /// If you interop with different languages it might be beneficial
+    /// to double check all places that use modulo arithmetic.
+    ///
+    /// For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let x = -17 % 3;
+    /// ```
+    #[clippy::version = "1.42.0"]
+    pub MODULO_ARITHMETIC,
+    restriction,
+    "any modulo arithmetic statement"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for uses of bitwise and/or operators between booleans, where performance may be improved by using
+    /// a lazy and.
+    ///
+    /// ### Why is this bad?
+    /// The bitwise operators do not support short-circuiting, so it may hinder code performance.
+    /// Additionally, boolean logic "masked" as bitwise logic is not caught by lints like `unnecessary_fold`
+    ///
+    /// ### Known problems
+    /// This lint evaluates only when the right side is determined to have no side effects. At this time, that
+    /// determination is quite conservative.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let (x,y) = (true, false);
+    /// if x & !y {} // where both x and y are booleans
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let (x,y) = (true, false);
+    /// if x && !y {}
+    /// ```
+    #[clippy::version = "1.54.0"]
+    pub NEEDLESS_BITWISE_BOOL,
+    pedantic,
+    "Boolean expressions that use bitwise rather than lazy operators"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Use `std::ptr::eq` when applicable
+    ///
+    /// ### Why is this bad?
+    /// `ptr::eq` can be used to compare `&T` references
+    /// (which coerce to `*const T` implicitly) by their address rather than
+    /// comparing the values they point to.
+    ///
+    /// ### Example
+    /// ```rust
+    /// let a = &[1, 2, 3];
+    /// let b = &[1, 2, 3];
+    ///
+    /// assert!(a as *const _ as usize == b as *const _ as usize);
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// let a = &[1, 2, 3];
+    /// let b = &[1, 2, 3];
+    ///
+    /// assert!(std::ptr::eq(a, b));
+    /// ```
+    #[clippy::version = "1.49.0"]
+    pub PTR_EQ,
+    style,
+    "use `std::ptr::eq` when comparing raw pointers"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for explicit self-assignments.
+    ///
+    /// ### Why is this bad?
+    /// Self-assignments are redundant and unlikely to be
+    /// intentional.
+    ///
+    /// ### Known problems
+    /// If expression contains any deref coercions or
+    /// indexing operations they are assumed not to have any side effects.
+    ///
+    /// ### Example
+    /// ```rust
+    /// struct Event {
+    ///     x: i32,
+    /// }
+    ///
+    /// fn copy_position(a: &mut Event, b: &Event) {
+    ///     a.x = a.x;
+    /// }
+    /// ```
+    ///
+    /// Should be:
+    /// ```rust
+    /// struct Event {
+    ///     x: i32,
+    /// }
+    ///
+    /// fn copy_position(a: &mut Event, b: &Event) {
+    ///     a.x = b.x;
+    /// }
+    /// ```
+    #[clippy::version = "1.48.0"]
+    pub SELF_ASSIGNMENT,
+    correctness,
+    "explicit self-assignment"
+}
+
+pub struct Operators {
+    arithmetic_context: numeric_arithmetic::Context,
+    verbose_bit_mask_threshold: u64,
+}
+impl_lint_pass!(Operators => [
+    ABSURD_EXTREME_COMPARISONS,
+    INTEGER_ARITHMETIC,
+    FLOAT_ARITHMETIC,
+    ASSIGN_OP_PATTERN,
+    MISREFACTORED_ASSIGN_OP,
+    BAD_BIT_MASK,
+    INEFFECTIVE_BIT_MASK,
+    VERBOSE_BIT_MASK,
+    DOUBLE_COMPARISONS,
+    DURATION_SUBSEC,
+    EQ_OP,
+    OP_REF,
+    ERASING_OP,
+    FLOAT_EQUALITY_WITHOUT_ABS,
+    IDENTITY_OP,
+    INTEGER_DIVISION,
+    CMP_NAN,
+    CMP_OWNED,
+    FLOAT_CMP,
+    FLOAT_CMP_CONST,
+    MODULO_ONE,
+    MODULO_ARITHMETIC,
+    NEEDLESS_BITWISE_BOOL,
+    PTR_EQ,
+    SELF_ASSIGNMENT,
+]);
+impl Operators {
+    pub fn new(verbose_bit_mask_threshold: u64) -> Self {
+        Self {
+            arithmetic_context: numeric_arithmetic::Context::default(),
+            verbose_bit_mask_threshold,
+        }
+    }
+}
+impl<'tcx> LateLintPass<'tcx> for Operators {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+        eq_op::check_assert(cx, e);
+        match e.kind {
+            ExprKind::Binary(op, lhs, rhs) => {
+                if !e.span.from_expansion() {
+                    absurd_extreme_comparisons::check(cx, e, op.node, lhs, rhs);
+                    if !(macro_with_not_op(lhs) || macro_with_not_op(rhs)) {
+                        eq_op::check(cx, e, op.node, lhs, rhs);
+                        op_ref::check(cx, e, op.node, lhs, rhs);
+                    }
+                    erasing_op::check(cx, e, op.node, lhs, rhs);
+                    identity_op::check(cx, e, op.node, lhs, rhs);
+                    needless_bitwise_bool::check(cx, e, op.node, lhs, rhs);
+                    ptr_eq::check(cx, e, op.node, lhs, rhs);
+                }
+                self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs);
+                bit_mask::check(cx, e, op.node, lhs, rhs);
+                verbose_bit_mask::check(cx, e, op.node, lhs, rhs, self.verbose_bit_mask_threshold);
+                double_comparison::check(cx, op.node, lhs, rhs, e.span);
+                duration_subsec::check(cx, e, op.node, lhs, rhs);
+                float_equality_without_abs::check(cx, e, op.node, lhs, rhs);
+                integer_division::check(cx, e, op.node, lhs, rhs);
+                cmp_nan::check(cx, e, op.node, lhs, rhs);
+                cmp_owned::check(cx, op.node, lhs, rhs);
+                float_cmp::check(cx, e, op.node, lhs, rhs);
+                modulo_one::check(cx, e, op.node, rhs);
+                modulo_arithmetic::check(cx, e, op.node, lhs, rhs);
+            },
+            ExprKind::AssignOp(op, lhs, rhs) => {
+                self.arithmetic_context.check_binary(cx, e, op.node, lhs, rhs);
+                misrefactored_assign_op::check(cx, e, op.node, lhs, rhs);
+                modulo_arithmetic::check(cx, e, op.node, lhs, rhs);
+            },
+            ExprKind::Assign(lhs, rhs, _) => {
+                assign_op_pattern::check(cx, e, lhs, rhs);
+                self_assignment::check(cx, e, lhs, rhs);
+            },
+            ExprKind::Unary(op, arg) => {
+                if op == UnOp::Neg {
+                    self.arithmetic_context.check_negate(cx, e, arg);
+                }
+            },
+            _ => (),
+        }
+    }
+
+    fn check_expr_post(&mut self, _: &LateContext<'_>, e: &Expr<'_>) {
+        self.arithmetic_context.expr_post(e.hir_id);
+    }
+
+    fn check_body(&mut self, cx: &LateContext<'tcx>, b: &'tcx Body<'_>) {
+        self.arithmetic_context.enter_body(cx, b);
+    }
+
+    fn check_body_post(&mut self, cx: &LateContext<'tcx>, b: &'tcx Body<'_>) {
+        self.arithmetic_context.body_post(cx, b);
+    }
+}
+
+fn macro_with_not_op(e: &Expr<'_>) -> bool {
+    if let ExprKind::Unary(_, e) = e.kind {
+        e.span.from_expansion()
+    } else {
+        false
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs
index 195b2e5c2ee..af4e74947f4 100644
--- a/src/tools/clippy/clippy_lints/src/modulo_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs
@@ -2,35 +2,35 @@ use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::sext;
 use if_chain::if_chain;
-use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
+use rustc_hir::{BinOpKind, Expr};
+use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
 use std::fmt::Display;
 
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for modulo arithmetic.
-    ///
-    /// ### Why is this bad?
-    /// The results of modulo (%) operation might differ
-    /// depending on the language, when negative numbers are involved.
-    /// If you interop with different languages it might be beneficial
-    /// to double check all places that use modulo arithmetic.
-    ///
-    /// For example, in Rust `17 % -3 = 2`, but in Python `17 % -3 = -1`.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let x = -17 % 3;
-    /// ```
-    #[clippy::version = "1.42.0"]
-    pub MODULO_ARITHMETIC,
-    restriction,
-    "any modulo arithmetic statement"
-}
+use super::MODULO_ARITHMETIC;
 
-declare_lint_pass!(ModuloArithmetic => [MODULO_ARITHMETIC]);
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx Expr<'_>,
+    op: BinOpKind,
+    lhs: &'tcx Expr<'_>,
+    rhs: &'tcx Expr<'_>,
+) {
+    if op == BinOpKind::Rem {
+        let lhs_operand = analyze_operand(lhs, cx, e);
+        let rhs_operand = analyze_operand(rhs, cx, e);
+        if_chain! {
+            if let Some(lhs_operand) = lhs_operand;
+            if let Some(rhs_operand) = rhs_operand;
+            then {
+                check_const_operands(cx, e, &lhs_operand, &rhs_operand);
+            }
+            else {
+                check_non_const_operands(cx, e, lhs);
+            }
+        }
+    };
+}
 
 struct OperandInfo {
     string_representation: Option<String>,
@@ -124,27 +124,3 @@ fn check_non_const_operands<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>,
         );
     }
 }
-
-impl<'tcx> LateLintPass<'tcx> for ModuloArithmetic {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        match &expr.kind {
-            ExprKind::Binary(op, lhs, rhs) | ExprKind::AssignOp(op, lhs, rhs) => {
-                if op.node == BinOpKind::Rem {
-                    let lhs_operand = analyze_operand(lhs, cx, expr);
-                    let rhs_operand = analyze_operand(rhs, cx, expr);
-                    if_chain! {
-                        if let Some(lhs_operand) = lhs_operand;
-                        if let Some(rhs_operand) = rhs_operand;
-                        then {
-                            check_const_operands(cx, expr, &lhs_operand, &rhs_operand);
-                        }
-                        else {
-                            check_non_const_operands(cx, expr, lhs);
-                        }
-                    }
-                };
-            },
-            _ => {},
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/operators/modulo_one.rs b/src/tools/clippy/clippy_lints/src/operators/modulo_one.rs
new file mode 100644
index 00000000000..54eea14833f
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/modulo_one.rs
@@ -0,0 +1,26 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::{is_integer_const, unsext};
+use rustc_hir::{BinOpKind, Expr};
+use rustc_lint::LateContext;
+use rustc_middle::ty;
+
+use super::MODULO_ONE;
+
+pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, op: BinOpKind, right: &Expr<'_>) {
+    if op == BinOpKind::Rem {
+        if is_integer_const(cx, right, 1) {
+            span_lint(cx, MODULO_ONE, expr.span, "any number modulo 1 will be 0");
+        }
+
+        if let ty::Int(ity) = cx.typeck_results().expr_ty(right).kind() {
+            if is_integer_const(cx, right, unsext(cx.tcx, -1, *ity)) {
+                span_lint(
+                    cx,
+                    MODULO_ONE,
+                    expr.span,
+                    "any number modulo -1 will panic/overflow or result in 0",
+                );
+            }
+        };
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/operators/needless_bitwise_bool.rs b/src/tools/clippy/clippy_lints/src/operators/needless_bitwise_bool.rs
new file mode 100644
index 00000000000..e902235a014
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/needless_bitwise_bool.rs
@@ -0,0 +1,36 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_opt;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+
+use super::NEEDLESS_BITWISE_BOOL;
+
+pub(super) fn check(cx: &LateContext<'_>, e: &Expr<'_>, op: BinOpKind, lhs: &Expr<'_>, rhs: &Expr<'_>) {
+    let op_str = match op {
+        BinOpKind::BitAnd => "&&",
+        BinOpKind::BitOr => "||",
+        _ => return,
+    };
+    if matches!(
+        rhs.kind,
+        ExprKind::Call(..) | ExprKind::MethodCall(..) | ExprKind::Binary(..) | ExprKind::Unary(..)
+    ) && cx.typeck_results().expr_ty(e).is_bool()
+        && !rhs.can_have_side_effects()
+    {
+        span_lint_and_then(
+            cx,
+            NEEDLESS_BITWISE_BOOL,
+            e.span,
+            "use of bitwise operator instead of lazy operator between booleans",
+            |diag| {
+                if let Some(lhs_snip) = snippet_opt(cx, lhs.span)
+                    && let Some(rhs_snip) = snippet_opt(cx, rhs.span)
+                {
+                    let sugg = format!("{} {} {}", lhs_snip, op_str, rhs_snip);
+                    diag.span_suggestion(e.span, "try", sugg, Applicability::MachineApplicable);
+                }
+            },
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs
new file mode 100644
index 00000000000..82f454d02f7
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs
@@ -0,0 +1,127 @@
+use clippy_utils::consts::constant_simple;
+use clippy_utils::diagnostics::span_lint;
+use rustc_hir as hir;
+use rustc_lint::LateContext;
+use rustc_span::source_map::Span;
+
+use super::{FLOAT_ARITHMETIC, INTEGER_ARITHMETIC};
+
+#[derive(Default)]
+pub struct Context {
+    expr_id: Option<hir::HirId>,
+    /// This field is used to check whether expressions are constants, such as in enum discriminants
+    /// and consts
+    const_span: Option<Span>,
+}
+impl Context {
+    fn skip_expr(&mut self, e: &hir::Expr<'_>) -> bool {
+        self.expr_id.is_some() || self.const_span.map_or(false, |span| span.contains(e.span))
+    }
+
+    pub fn check_binary<'tcx>(
+        &mut self,
+        cx: &LateContext<'tcx>,
+        expr: &'tcx hir::Expr<'_>,
+        op: hir::BinOpKind,
+        l: &'tcx hir::Expr<'_>,
+        r: &'tcx hir::Expr<'_>,
+    ) {
+        if self.skip_expr(expr) {
+            return;
+        }
+        match op {
+            hir::BinOpKind::And
+            | hir::BinOpKind::Or
+            | hir::BinOpKind::BitAnd
+            | hir::BinOpKind::BitOr
+            | hir::BinOpKind::BitXor
+            | hir::BinOpKind::Eq
+            | hir::BinOpKind::Lt
+            | hir::BinOpKind::Le
+            | hir::BinOpKind::Ne
+            | hir::BinOpKind::Ge
+            | hir::BinOpKind::Gt => return,
+            _ => (),
+        }
+
+        let (l_ty, r_ty) = (cx.typeck_results().expr_ty(l), cx.typeck_results().expr_ty(r));
+        if l_ty.peel_refs().is_integral() && r_ty.peel_refs().is_integral() {
+            match op {
+                hir::BinOpKind::Div | hir::BinOpKind::Rem => match &r.kind {
+                    hir::ExprKind::Lit(_lit) => (),
+                    hir::ExprKind::Unary(hir::UnOp::Neg, expr) => {
+                        if let hir::ExprKind::Lit(lit) = &expr.kind {
+                            if let rustc_ast::ast::LitKind::Int(1, _) = lit.node {
+                                span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
+                                self.expr_id = Some(expr.hir_id);
+                            }
+                        }
+                    },
+                    _ => {
+                        span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
+                        self.expr_id = Some(expr.hir_id);
+                    },
+                },
+                _ => {
+                    span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
+                    self.expr_id = Some(expr.hir_id);
+                },
+            }
+        } else if r_ty.peel_refs().is_floating_point() && r_ty.peel_refs().is_floating_point() {
+            span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
+            self.expr_id = Some(expr.hir_id);
+        }
+    }
+
+    pub fn check_negate<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>, arg: &'tcx hir::Expr<'_>) {
+        if self.skip_expr(expr) {
+            return;
+        }
+        let ty = cx.typeck_results().expr_ty(arg);
+        if constant_simple(cx, cx.typeck_results(), expr).is_none() {
+            if ty.is_integral() {
+                span_lint(cx, INTEGER_ARITHMETIC, expr.span, "integer arithmetic detected");
+                self.expr_id = Some(expr.hir_id);
+            } else if ty.is_floating_point() {
+                span_lint(cx, FLOAT_ARITHMETIC, expr.span, "floating-point arithmetic detected");
+                self.expr_id = Some(expr.hir_id);
+            }
+        }
+    }
+
+    pub fn expr_post(&mut self, id: hir::HirId) {
+        if Some(id) == self.expr_id {
+            self.expr_id = None;
+        }
+    }
+
+    pub fn enter_body(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
+        let body_owner = cx.tcx.hir().body_owner_def_id(body.id());
+
+        match cx.tcx.hir().body_owner_kind(body_owner) {
+            hir::BodyOwnerKind::Static(_) | hir::BodyOwnerKind::Const => {
+                let body_span = cx.tcx.def_span(body_owner);
+
+                if let Some(span) = self.const_span {
+                    if span.contains(body_span) {
+                        return;
+                    }
+                }
+                self.const_span = Some(body_span);
+            },
+            hir::BodyOwnerKind::Fn | hir::BodyOwnerKind::Closure => (),
+        }
+    }
+
+    pub fn body_post(&mut self, cx: &LateContext<'_>, body: &hir::Body<'_>) {
+        let body_owner = cx.tcx.hir().body_owner(body.id());
+        let body_span = cx.tcx.hir().span(body_owner);
+
+        if let Some(span) = self.const_span {
+            if span.contains(body_span) {
+                return;
+            }
+        }
+        self.const_span = None;
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/operators/op_ref.rs b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs
new file mode 100644
index 00000000000..1805672e372
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/op_ref.rs
@@ -0,0 +1,218 @@
+use clippy_utils::diagnostics::{multispan_sugg, span_lint_and_then};
+use clippy_utils::get_enclosing_block;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::{implements_trait, is_copy};
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{def::Res, def_id::DefId, BinOpKind, BorrowKind, Expr, ExprKind, GenericArg, ItemKind, QPath, TyKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, Ty};
+
+use super::OP_REF;
+
+#[expect(clippy::similar_names, clippy::too_many_lines)]
+pub(crate) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx Expr<'_>,
+    op: BinOpKind,
+    left: &'tcx Expr<'_>,
+    right: &'tcx Expr<'_>,
+) {
+    let (trait_id, requires_ref) = match op {
+        BinOpKind::Add => (cx.tcx.lang_items().add_trait(), false),
+        BinOpKind::Sub => (cx.tcx.lang_items().sub_trait(), false),
+        BinOpKind::Mul => (cx.tcx.lang_items().mul_trait(), false),
+        BinOpKind::Div => (cx.tcx.lang_items().div_trait(), false),
+        BinOpKind::Rem => (cx.tcx.lang_items().rem_trait(), false),
+        // don't lint short circuiting ops
+        BinOpKind::And | BinOpKind::Or => return,
+        BinOpKind::BitXor => (cx.tcx.lang_items().bitxor_trait(), false),
+        BinOpKind::BitAnd => (cx.tcx.lang_items().bitand_trait(), false),
+        BinOpKind::BitOr => (cx.tcx.lang_items().bitor_trait(), false),
+        BinOpKind::Shl => (cx.tcx.lang_items().shl_trait(), false),
+        BinOpKind::Shr => (cx.tcx.lang_items().shr_trait(), false),
+        BinOpKind::Ne | BinOpKind::Eq => (cx.tcx.lang_items().eq_trait(), true),
+        BinOpKind::Lt | BinOpKind::Le | BinOpKind::Ge | BinOpKind::Gt => {
+            (cx.tcx.lang_items().partial_ord_trait(), true)
+        },
+    };
+    if let Some(trait_id) = trait_id {
+        match (&left.kind, &right.kind) {
+            // do not suggest to dereference literals
+            (&ExprKind::Lit(..), _) | (_, &ExprKind::Lit(..)) => {},
+            // &foo == &bar
+            (&ExprKind::AddrOf(BorrowKind::Ref, _, l), &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => {
+                let lty = cx.typeck_results().expr_ty(l);
+                let rty = cx.typeck_results().expr_ty(r);
+                let lcpy = is_copy(cx, lty);
+                let rcpy = is_copy(cx, rty);
+                if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
+                    if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
+                        || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
+                    {
+                        return; // Don't lint
+                    }
+                }
+                // either operator autorefs or both args are copyable
+                if (requires_ref || (lcpy && rcpy)) && implements_trait(cx, lty, trait_id, &[rty.into()]) {
+                    span_lint_and_then(
+                        cx,
+                        OP_REF,
+                        e.span,
+                        "needlessly taken reference of both operands",
+                        |diag| {
+                            let lsnip = snippet(cx, l.span, "...").to_string();
+                            let rsnip = snippet(cx, r.span, "...").to_string();
+                            multispan_sugg(
+                                diag,
+                                "use the values directly",
+                                vec![(left.span, lsnip), (right.span, rsnip)],
+                            );
+                        },
+                    );
+                } else if lcpy
+                    && !rcpy
+                    && implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()])
+                {
+                    span_lint_and_then(
+                        cx,
+                        OP_REF,
+                        e.span,
+                        "needlessly taken reference of left operand",
+                        |diag| {
+                            let lsnip = snippet(cx, l.span, "...").to_string();
+                            diag.span_suggestion(
+                                left.span,
+                                "use the left value directly",
+                                lsnip,
+                                Applicability::MaybeIncorrect, // FIXME #2597
+                            );
+                        },
+                    );
+                } else if !lcpy
+                    && rcpy
+                    && implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()])
+                {
+                    span_lint_and_then(
+                        cx,
+                        OP_REF,
+                        e.span,
+                        "needlessly taken reference of right operand",
+                        |diag| {
+                            let rsnip = snippet(cx, r.span, "...").to_string();
+                            diag.span_suggestion(
+                                right.span,
+                                "use the right value directly",
+                                rsnip,
+                                Applicability::MaybeIncorrect, // FIXME #2597
+                            );
+                        },
+                    );
+                }
+            },
+            // &foo == bar
+            (&ExprKind::AddrOf(BorrowKind::Ref, _, l), _) => {
+                let lty = cx.typeck_results().expr_ty(l);
+                if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
+                    let rty = cx.typeck_results().expr_ty(right);
+                    if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
+                        || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
+                    {
+                        return; // Don't lint
+                    }
+                }
+                let lcpy = is_copy(cx, lty);
+                if (requires_ref || lcpy)
+                    && implements_trait(cx, lty, trait_id, &[cx.typeck_results().expr_ty(right).into()])
+                {
+                    span_lint_and_then(
+                        cx,
+                        OP_REF,
+                        e.span,
+                        "needlessly taken reference of left operand",
+                        |diag| {
+                            let lsnip = snippet(cx, l.span, "...").to_string();
+                            diag.span_suggestion(
+                                left.span,
+                                "use the left value directly",
+                                lsnip,
+                                Applicability::MaybeIncorrect, // FIXME #2597
+                            );
+                        },
+                    );
+                }
+            },
+            // foo == &bar
+            (_, &ExprKind::AddrOf(BorrowKind::Ref, _, r)) => {
+                let rty = cx.typeck_results().expr_ty(r);
+                if let Some((self_ty, other_ty)) = in_impl(cx, e, trait_id) {
+                    let lty = cx.typeck_results().expr_ty(left);
+                    if (are_equal(cx, rty, self_ty) && are_equal(cx, lty, other_ty))
+                        || (are_equal(cx, rty, other_ty) && are_equal(cx, lty, self_ty))
+                    {
+                        return; // Don't lint
+                    }
+                }
+                let rcpy = is_copy(cx, rty);
+                if (requires_ref || rcpy)
+                    && implements_trait(cx, cx.typeck_results().expr_ty(left), trait_id, &[rty.into()])
+                {
+                    span_lint_and_then(cx, OP_REF, e.span, "taken reference of right operand", |diag| {
+                        let rsnip = snippet(cx, r.span, "...").to_string();
+                        diag.span_suggestion(
+                            right.span,
+                            "use the right value directly",
+                            rsnip,
+                            Applicability::MaybeIncorrect, // FIXME #2597
+                        );
+                    });
+                }
+            },
+            _ => {},
+        }
+    }
+}
+
+fn in_impl<'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx Expr<'_>,
+    bin_op: DefId,
+) -> Option<(&'tcx rustc_hir::Ty<'tcx>, &'tcx rustc_hir::Ty<'tcx>)> {
+    if_chain! {
+        if let Some(block) = get_enclosing_block(cx, e.hir_id);
+        if let Some(impl_def_id) = cx.tcx.impl_of_method(block.hir_id.owner.to_def_id());
+        let item = cx.tcx.hir().expect_item(impl_def_id.expect_local());
+        if let ItemKind::Impl(item) = &item.kind;
+        if let Some(of_trait) = &item.of_trait;
+        if let Some(seg) = of_trait.path.segments.last();
+        if let Some(Res::Def(_, trait_id)) = seg.res;
+        if trait_id == bin_op;
+        if let Some(generic_args) = seg.args;
+        if let Some(GenericArg::Type(other_ty)) = generic_args.args.last();
+
+        then {
+            Some((item.self_ty, other_ty))
+        }
+        else {
+            None
+        }
+    }
+}
+
+fn are_equal<'tcx>(cx: &LateContext<'tcx>, middle_ty: Ty<'_>, hir_ty: &rustc_hir::Ty<'_>) -> bool {
+    if_chain! {
+        if let ty::Adt(adt_def, _) = middle_ty.kind();
+        if let Some(local_did) = adt_def.did().as_local();
+        let item = cx.tcx.hir().expect_item(local_did);
+        let middle_ty_id = item.def_id.to_def_id();
+        if let TyKind::Path(QPath::Resolved(_, path)) = hir_ty.kind;
+        if let Res::Def(_, hir_ty_id) = path.res;
+
+        then {
+            hir_ty_id == middle_ty_id
+        }
+        else {
+            false
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs b/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs
new file mode 100644
index 00000000000..1aefc2741c2
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/ptr_eq.rs
@@ -0,0 +1,65 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_opt;
+use if_chain::if_chain;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+
+use super::PTR_EQ;
+
+static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers";
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    op: BinOpKind,
+    left: &'tcx Expr<'_>,
+    right: &'tcx Expr<'_>,
+) {
+    if BinOpKind::Eq == op {
+        let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) {
+            (Some(lhs), Some(rhs)) => (lhs, rhs),
+            _ => (left, right),
+        };
+
+        if_chain! {
+            if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left);
+            if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right);
+            if let Some(left_snip) = snippet_opt(cx, left_var.span);
+            if let Some(right_snip) = snippet_opt(cx, right_var.span);
+            then {
+                span_lint_and_sugg(
+                    cx,
+                    PTR_EQ,
+                    expr.span,
+                    LINT_MSG,
+                    "try",
+                    format!("std::ptr::eq({}, {})", left_snip, right_snip),
+                    Applicability::MachineApplicable,
+                    );
+            }
+        }
+    }
+}
+
+// If the given expression is a cast to a usize, return the lhs of the cast
+// E.g., `foo as *const _ as usize` returns `foo as *const _`.
+fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+    if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize {
+        if let ExprKind::Cast(expr, _) = cast_expr.kind {
+            return Some(expr);
+        }
+    }
+    None
+}
+
+// If the given expression is a cast to a `*const` pointer, return the lhs of the cast
+// E.g., `foo as *const _` returns `foo`.
+fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
+    if cx.typeck_results().expr_ty(cast_expr).is_unsafe_ptr() {
+        if let ExprKind::Cast(expr, _) = cast_expr.kind {
+            return Some(expr);
+        }
+    }
+    None
+}
diff --git a/src/tools/clippy/clippy_lints/src/operators/self_assignment.rs b/src/tools/clippy/clippy_lints/src/operators/self_assignment.rs
new file mode 100644
index 00000000000..9d6bec05bf0
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/self_assignment.rs
@@ -0,0 +1,20 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::eq_expr_value;
+use clippy_utils::source::snippet;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+
+use super::SELF_ASSIGNMENT;
+
+pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>, lhs: &'tcx Expr<'_>, rhs: &'tcx Expr<'_>) {
+    if eq_expr_value(cx, lhs, rhs) {
+        let lhs = snippet(cx, lhs.span, "<lhs>");
+        let rhs = snippet(cx, rhs.span, "<rhs>");
+        span_lint(
+            cx,
+            SELF_ASSIGNMENT,
+            e.span,
+            &format!("self-assignment of `{}` to `{}`", rhs, lhs),
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/operators/verbose_bit_mask.rs b/src/tools/clippy/clippy_lints/src/operators/verbose_bit_mask.rs
new file mode 100644
index 00000000000..ff85fd55429
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/operators/verbose_bit_mask.rs
@@ -0,0 +1,44 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::sugg::Sugg;
+use rustc_ast::ast::LitKind;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::LateContext;
+
+use super::VERBOSE_BIT_MASK;
+
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx Expr<'_>,
+    op: BinOpKind,
+    left: &'tcx Expr<'_>,
+    right: &'tcx Expr<'_>,
+    threshold: u64,
+) {
+    if BinOpKind::Eq == op
+        && let ExprKind::Binary(op1, left1, right1) = &left.kind
+        && BinOpKind::BitAnd == op1.node
+        && let ExprKind::Lit(lit) = &right1.kind
+        && let LitKind::Int(n, _) = lit.node
+        && let ExprKind::Lit(lit1) = &right.kind
+        && let LitKind::Int(0, _) = lit1.node
+        && n.leading_zeros() == n.count_zeros()
+        && n > u128::from(threshold)
+    {
+        span_lint_and_then(
+            cx,
+            VERBOSE_BIT_MASK,
+            e.span,
+            "bit mask could be simplified with a call to `trailing_zeros`",
+            |diag| {
+                let sugg = Sugg::hir(cx, left1, "...").maybe_par();
+                diag.span_suggestion(
+                    e.span,
+                    "try",
+                    format!("{}.trailing_zeros() >= {}", sugg, n.count_ones()),
+                    Applicability::MaybeIncorrect,
+                );
+            },
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
index 05ab62786f4..5fa4fd74853 100644
--- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
+++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
@@ -3,17 +3,20 @@ use std::iter;
 
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet;
-use clippy_utils::ty::is_copy;
+use clippy_utils::ty::{for_each_top_level_late_bound_region, is_copy};
 use clippy_utils::{is_self, is_self_ty};
+use core::ops::ControlFlow;
 use if_chain::if_chain;
 use rustc_ast::attr;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{BindingAnnotation, Body, FnDecl, HirId, Impl, ItemKind, MutTy, Mutability, Node, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty;
+use rustc_middle::ty::adjustment::{Adjust, PointerCast};
 use rustc_middle::ty::layout::LayoutOf;
+use rustc_middle::ty::{self, RegionKind};
 use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::def_id::LocalDefId;
 use rustc_span::{sym, Span};
@@ -141,50 +144,76 @@ impl<'tcx> PassByRefOrValue {
         }
 
         let fn_sig = cx.tcx.fn_sig(def_id);
-        let fn_sig = cx.tcx.erase_late_bound_regions(fn_sig);
-
         let fn_body = cx.enclosing_body.map(|id| cx.tcx.hir().body(id));
 
-        for (index, (input, &ty)) in iter::zip(decl.inputs, fn_sig.inputs()).enumerate() {
+        // Gather all the lifetimes found in the output type which may affect whether
+        // `TRIVIALLY_COPY_PASS_BY_REF` should be linted.
+        let mut output_regions = FxHashSet::default();
+        for_each_top_level_late_bound_region(fn_sig.skip_binder().output(), |region| -> ControlFlow<!> {
+            output_regions.insert(region);
+            ControlFlow::Continue(())
+        });
+
+        for (index, (input, ty)) in iter::zip(
+            decl.inputs,
+            fn_sig.skip_binder().inputs().iter().map(|&ty| fn_sig.rebind(ty)),
+        )
+        .enumerate()
+        {
             // All spans generated from a proc-macro invocation are the same...
             match span {
-                Some(s) if s == input.span => return,
+                Some(s) if s == input.span => continue,
                 _ => (),
             }
 
-            match ty.kind() {
-                ty::Ref(input_lt, ty, Mutability::Not) => {
-                    // Use lifetimes to determine if we're returning a reference to the
-                    // argument. In that case we can't switch to pass-by-value as the
-                    // argument will not live long enough.
-                    let output_lts = match *fn_sig.output().kind() {
-                        ty::Ref(output_lt, _, _) => vec![output_lt],
-                        ty::Adt(_, substs) => substs.regions().collect(),
-                        _ => vec![],
-                    };
+            match *ty.skip_binder().kind() {
+                ty::Ref(lt, ty, Mutability::Not) => {
+                    match lt.kind() {
+                        RegionKind::ReLateBound(index, region)
+                            if index.as_u32() == 0 && output_regions.contains(&region) =>
+                        {
+                            continue;
+                        },
+                        // Early bound regions on functions are either from the containing item, are bounded by another
+                        // lifetime, or are used as a bound for a type or lifetime.
+                        RegionKind::ReEarlyBound(..) => continue,
+                        _ => (),
+                    }
 
-                    if_chain! {
-                        if !output_lts.contains(input_lt);
-                        if is_copy(cx, *ty);
-                        if let Some(size) = cx.layout_of(*ty).ok().map(|l| l.size.bytes());
-                        if size <= self.ref_min_size;
-                        if let hir::TyKind::Rptr(_, MutTy { ty: decl_ty, .. }) = input.kind;
-                        then {
-                            let value_type = if fn_body.and_then(|body| body.params.get(index)).map_or(false, is_self) {
-                                "self".into()
-                            } else {
-                                snippet(cx, decl_ty.span, "_").into()
-                            };
-                            span_lint_and_sugg(
-                                cx,
-                                TRIVIALLY_COPY_PASS_BY_REF,
-                                input.span,
-                                &format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.ref_min_size),
-                                "consider passing by value instead",
-                                value_type,
-                                Applicability::Unspecified,
-                            );
+                    let ty = cx.tcx.erase_late_bound_regions(fn_sig.rebind(ty));
+                    if is_copy(cx, ty)
+                        && let Some(size) = cx.layout_of(ty).ok().map(|l| l.size.bytes())
+                        && size <= self.ref_min_size
+                        && let hir::TyKind::Rptr(_, MutTy { ty: decl_ty, .. }) = input.kind
+                    {
+                        if let Some(typeck) = cx.maybe_typeck_results() {
+                            // Don't lint if an unsafe pointer is created.
+                            // TODO: Limit the check only to unsafe pointers to the argument (or part of the argument)
+                            //       which escape the current function.
+                            if typeck.node_types().iter().any(|(_, &ty)| ty.is_unsafe_ptr())
+                                || typeck
+                                    .adjustments()
+                                    .iter()
+                                    .flat_map(|(_, a)| a)
+                                    .any(|a| matches!(a.kind, Adjust::Pointer(PointerCast::UnsafeFnPointer)))
+                            {
+                                continue;
+                            }
                         }
+                        let value_type = if fn_body.and_then(|body| body.params.get(index)).map_or(false, is_self) {
+                            "self".into()
+                        } else {
+                            snippet(cx, decl_ty.span, "_").into()
+                        };
+                        span_lint_and_sugg(
+                            cx,
+                            TRIVIALLY_COPY_PASS_BY_REF,
+                            input.span,
+                            &format!("this argument ({} byte) is passed by reference, but would be more efficient if passed by value (limit: {} byte)", size, self.ref_min_size),
+                            "consider passing by value instead",
+                            value_type,
+                            Applicability::Unspecified,
+                        );
                     }
                 },
 
@@ -196,6 +225,7 @@ impl<'tcx> PassByRefOrValue {
                             _ => continue,
                         }
                     }
+                    let ty = cx.tcx.erase_late_bound_regions(ty);
 
                     if_chain! {
                         if is_copy(cx, ty);
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index b06eba13d2f..25b73918c0a 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -1,6 +1,6 @@
 //! Checks for usage of  `&Vec[_]` and `&String`.
 
-use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::{span_lint, span_lint_and_sugg, span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::expr_sig;
 use clippy_utils::visitors::contains_unsafe_block;
@@ -166,15 +166,14 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
             )
             .filter(|arg| arg.mutability() == Mutability::Not)
             {
-                span_lint_and_sugg(
-                    cx,
-                    PTR_ARG,
-                    arg.span,
-                    &arg.build_msg(),
-                    "change this to",
-                    format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)),
-                    Applicability::Unspecified,
-                );
+                span_lint_hir_and_then(cx, PTR_ARG, arg.emission_id, arg.span, &arg.build_msg(), |diag| {
+                    diag.span_suggestion(
+                        arg.span,
+                        "change this to",
+                        format!("{}{}", arg.ref_prefix, arg.deref_ty.display(cx)),
+                        Applicability::Unspecified,
+                    );
+                });
             }
         }
     }
@@ -221,7 +220,7 @@ impl<'tcx> LateLintPass<'tcx> for Ptr {
         let results = check_ptr_arg_usage(cx, body, &lint_args);
 
         for (result, args) in results.iter().zip(lint_args.iter()).filter(|(r, _)| !r.skip) {
-            span_lint_and_then(cx, PTR_ARG, args.span, &args.build_msg(), |diag| {
+            span_lint_hir_and_then(cx, PTR_ARG, args.emission_id, args.span, &args.build_msg(), |diag| {
                 diag.multipart_suggestion(
                     "change this to",
                     iter::once((args.span, format!("{}{}", args.ref_prefix, args.deref_ty.display(cx))))
@@ -315,6 +314,7 @@ struct PtrArgReplacement {
 
 struct PtrArg<'tcx> {
     idx: usize,
+    emission_id: hir::HirId,
     span: Span,
     ty_did: DefId,
     ty_name: Symbol,
@@ -419,10 +419,8 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                 if let [.., name] = path.segments;
                 if cx.tcx.item_name(adt.did()) == name.ident.name;
 
-                if !is_lint_allowed(cx, PTR_ARG, hir_ty.hir_id);
-                if params.get(i).map_or(true, |p| !is_lint_allowed(cx, PTR_ARG, p.hir_id));
-
                 then {
+                    let emission_id = params.get(i).map_or(hir_ty.hir_id, |param| param.hir_id);
                     let (method_renames, deref_ty) = match cx.tcx.get_diagnostic_name(adt.did()) {
                         Some(sym::Vec) => (
                             [("clone", ".to_owned()")].as_slice(),
@@ -455,14 +453,20 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                                 })
                                 .and_then(|arg| snippet_opt(cx, arg.span))
                                 .unwrap_or_else(|| substs.type_at(1).to_string());
-                            span_lint_and_sugg(
+                            span_lint_hir_and_then(
                                 cx,
                                 PTR_ARG,
+                                emission_id,
                                 hir_ty.span,
                                 "using a reference to `Cow` is not recommended",
-                                "change this to",
-                                format!("&{}{}", mutability.prefix_str(), ty_name),
-                                Applicability::Unspecified,
+                                |diag| {
+                                    diag.span_suggestion(
+                                        hir_ty.span,
+                                        "change this to",
+                                        format!("&{}{}", mutability.prefix_str(), ty_name),
+                                        Applicability::Unspecified,
+                                    );
+                                }
                             );
                             return None;
                         },
@@ -470,6 +474,7 @@ fn check_fn_args<'cx, 'tcx: 'cx>(
                     };
                     return Some(PtrArg {
                         idx: i,
+                        emission_id,
                         span: hir_ty.span,
                         ty_did: adt.did(),
                         ty_name: name.ident.name,
@@ -574,14 +579,13 @@ fn check_ptr_arg_usage<'tcx>(cx: &LateContext<'tcx>, body: &'tcx Body<'_>, args:
                 Some((Node::Expr(e), child_id)) => match e.kind {
                     ExprKind::Call(f, expr_args) => {
                         let i = expr_args.iter().position(|arg| arg.hir_id == child_id).unwrap_or(0);
-                        if expr_sig(self.cx, f)
-                            .map(|sig| sig.input(i).skip_binder().peel_refs())
-                            .map_or(true, |ty| match *ty.kind() {
+                        if expr_sig(self.cx, f).and_then(|sig| sig.input(i)).map_or(true, |ty| {
+                            match *ty.skip_binder().peel_refs().kind() {
                                 ty::Param(_) => true,
                                 ty::Adt(def, _) => def.did() == args.ty_did,
                                 _ => false,
-                            })
-                        {
+                            }
+                        }) {
                             // Passed to a function taking the non-dereferenced type.
                             set_skip_flag();
                         }
diff --git a/src/tools/clippy/clippy_lints/src/ptr_eq.rs b/src/tools/clippy/clippy_lints/src/ptr_eq.rs
deleted file mode 100644
index 2bec93ac606..00000000000
--- a/src/tools/clippy/clippy_lints/src/ptr_eq.rs
+++ /dev/null
@@ -1,97 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
-use if_chain::if_chain;
-use rustc_errors::Applicability;
-use rustc_hir::{BinOpKind, Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Use `std::ptr::eq` when applicable
-    ///
-    /// ### Why is this bad?
-    /// `ptr::eq` can be used to compare `&T` references
-    /// (which coerce to `*const T` implicitly) by their address rather than
-    /// comparing the values they point to.
-    ///
-    /// ### Example
-    /// ```rust
-    /// let a = &[1, 2, 3];
-    /// let b = &[1, 2, 3];
-    ///
-    /// assert!(a as *const _ as usize == b as *const _ as usize);
-    /// ```
-    /// Use instead:
-    /// ```rust
-    /// let a = &[1, 2, 3];
-    /// let b = &[1, 2, 3];
-    ///
-    /// assert!(std::ptr::eq(a, b));
-    /// ```
-    #[clippy::version = "1.49.0"]
-    pub PTR_EQ,
-    style,
-    "use `std::ptr::eq` when comparing raw pointers"
-}
-
-declare_lint_pass!(PtrEq => [PTR_EQ]);
-
-static LINT_MSG: &str = "use `std::ptr::eq` when comparing raw pointers";
-
-impl<'tcx> LateLintPass<'tcx> for PtrEq {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if expr.span.from_expansion() {
-            return;
-        }
-
-        if let ExprKind::Binary(ref op, left, right) = expr.kind {
-            if BinOpKind::Eq == op.node {
-                let (left, right) = match (expr_as_cast_to_usize(cx, left), expr_as_cast_to_usize(cx, right)) {
-                    (Some(lhs), Some(rhs)) => (lhs, rhs),
-                    _ => (left, right),
-                };
-
-                if_chain! {
-                    if let Some(left_var) = expr_as_cast_to_raw_pointer(cx, left);
-                    if let Some(right_var) = expr_as_cast_to_raw_pointer(cx, right);
-                    if let Some(left_snip) = snippet_opt(cx, left_var.span);
-                    if let Some(right_snip) = snippet_opt(cx, right_var.span);
-                    then {
-                        span_lint_and_sugg(
-                            cx,
-                            PTR_EQ,
-                            expr.span,
-                            LINT_MSG,
-                            "try",
-                            format!("std::ptr::eq({}, {})", left_snip, right_snip),
-                            Applicability::MachineApplicable,
-                            );
-                    }
-                }
-            }
-        }
-    }
-}
-
-// If the given expression is a cast to a usize, return the lhs of the cast
-// E.g., `foo as *const _ as usize` returns `foo as *const _`.
-fn expr_as_cast_to_usize<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
-    if cx.typeck_results().expr_ty(cast_expr) == cx.tcx.types.usize {
-        if let ExprKind::Cast(expr, _) = cast_expr.kind {
-            return Some(expr);
-        }
-    }
-    None
-}
-
-// If the given expression is a cast to a `*const` pointer, return the lhs of the cast
-// E.g., `foo as *const _` returns `foo`.
-fn expr_as_cast_to_raw_pointer<'tcx>(cx: &LateContext<'tcx>, cast_expr: &'tcx Expr<'_>) -> Option<&'tcx Expr<'tcx>> {
-    if cx.typeck_results().expr_ty(cast_expr).is_unsafe_ptr() {
-        if let ExprKind::Cast(expr, _) = cast_expr.kind {
-            return Some(expr);
-        }
-    }
-    None
-}
diff --git a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
index 0825f00f421..f8801f769e8 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_static_lifetimes.rs
@@ -87,7 +87,7 @@ impl RedundantStaticLifetimes {
                         _ => {},
                     }
                 }
-                self.visit_type(&*borrow_type.ty, cx, reason);
+                self.visit_type(&borrow_type.ty, cx, reason);
             },
             _ => {},
         }
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index e525eba53e2..5ae04947b82 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::source::{snippet_opt, snippet_with_context};
 use clippy_utils::{fn_def_id, path_to_local_id};
 use if_chain::if_chain;
@@ -94,9 +94,10 @@ impl<'tcx> LateLintPass<'tcx> for Return {
             if !in_external_macro(cx.sess(), retexpr.span);
             if !local.span.from_expansion();
             then {
-                span_lint_and_then(
+                span_lint_hir_and_then(
                     cx,
                     LET_AND_RETURN,
+                    retexpr.hir_id,
                     retexpr.span,
                     "returning the result of a `let` binding from a block",
                     |err| {
@@ -185,6 +186,7 @@ fn check_final_expr<'tcx>(
                 if !borrows {
                     emit_return_lint(
                         cx,
+                        inner.map_or(expr.hir_id, |inner| inner.hir_id),
                         span.expect("`else return` is not possible"),
                         inner.as_ref().map(|i| i.span),
                         replacement,
@@ -220,50 +222,81 @@ fn check_final_expr<'tcx>(
     }
 }
 
-fn emit_return_lint(cx: &LateContext<'_>, ret_span: Span, inner_span: Option<Span>, replacement: RetReplacement) {
+fn emit_return_lint(
+    cx: &LateContext<'_>,
+    emission_place: HirId,
+    ret_span: Span,
+    inner_span: Option<Span>,
+    replacement: RetReplacement,
+) {
     if ret_span.from_expansion() {
         return;
     }
     match inner_span {
         Some(inner_span) => {
             let mut applicability = Applicability::MachineApplicable;
-            span_lint_and_then(cx, NEEDLESS_RETURN, ret_span, "unneeded `return` statement", |diag| {
-                let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability);
-                diag.span_suggestion(ret_span, "remove `return`", snippet, applicability);
-            });
+            span_lint_hir_and_then(
+                cx,
+                NEEDLESS_RETURN,
+                emission_place,
+                ret_span,
+                "unneeded `return` statement",
+                |diag| {
+                    let (snippet, _) = snippet_with_context(cx, inner_span, ret_span.ctxt(), "..", &mut applicability);
+                    diag.span_suggestion(ret_span, "remove `return`", snippet, applicability);
+                },
+            );
         },
         None => match replacement {
             RetReplacement::Empty => {
-                span_lint_and_sugg(
+                span_lint_hir_and_then(
                     cx,
                     NEEDLESS_RETURN,
+                    emission_place,
                     ret_span,
                     "unneeded `return` statement",
-                    "remove `return`",
-                    String::new(),
-                    Applicability::MachineApplicable,
+                    |diag| {
+                        diag.span_suggestion(
+                            ret_span,
+                            "remove `return`",
+                            String::new(),
+                            Applicability::MachineApplicable,
+                        );
+                    },
                 );
             },
             RetReplacement::Block => {
-                span_lint_and_sugg(
+                span_lint_hir_and_then(
                     cx,
                     NEEDLESS_RETURN,
+                    emission_place,
                     ret_span,
                     "unneeded `return` statement",
-                    "replace `return` with an empty block",
-                    "{}".to_string(),
-                    Applicability::MachineApplicable,
+                    |diag| {
+                        diag.span_suggestion(
+                            ret_span,
+                            "replace `return` with an empty block",
+                            "{}".to_string(),
+                            Applicability::MachineApplicable,
+                        );
+                    },
                 );
             },
             RetReplacement::Unit => {
-                span_lint_and_sugg(
+                span_lint_hir_and_then(
                     cx,
                     NEEDLESS_RETURN,
+                    emission_place,
                     ret_span,
                     "unneeded `return` statement",
-                    "replace `return` with a unit value",
-                    "()".to_string(),
-                    Applicability::MachineApplicable,
+                    |diag| {
+                        diag.span_suggestion(
+                            ret_span,
+                            "replace `return` with a unit value",
+                            "()".to_string(),
+                            Applicability::MachineApplicable,
+                        );
+                    },
                 );
             },
         },
diff --git a/src/tools/clippy/clippy_lints/src/self_assignment.rs b/src/tools/clippy/clippy_lints/src/self_assignment.rs
deleted file mode 100644
index b14f0518bdb..00000000000
--- a/src/tools/clippy/clippy_lints/src/self_assignment.rs
+++ /dev/null
@@ -1,56 +0,0 @@
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::eq_expr_value;
-use clippy_utils::source::snippet;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for explicit self-assignments.
-    ///
-    /// ### Why is this bad?
-    /// Self-assignments are redundant and unlikely to be
-    /// intentional.
-    ///
-    /// ### Known problems
-    /// If expression contains any deref coercions or
-    /// indexing operations they are assumed not to have any side effects.
-    ///
-    /// ### Example
-    /// ```rust
-    /// struct Event {
-    ///     id: usize,
-    ///     x: i32,
-    ///     y: i32,
-    /// }
-    ///
-    /// fn copy_position(a: &mut Event, b: &Event) {
-    ///     a.x = b.x;
-    ///     a.y = a.y;
-    /// }
-    /// ```
-    #[clippy::version = "1.48.0"]
-    pub SELF_ASSIGNMENT,
-    correctness,
-    "explicit self-assignment"
-}
-
-declare_lint_pass!(SelfAssignment => [SELF_ASSIGNMENT]);
-
-impl<'tcx> LateLintPass<'tcx> for SelfAssignment {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if let ExprKind::Assign(lhs, rhs, _) = &expr.kind {
-            if eq_expr_value(cx, lhs, rhs) {
-                let lhs = snippet(cx, lhs.span, "<lhs>");
-                let rhs = snippet(cx, rhs.span, "<rhs>");
-                span_lint(
-                    cx,
-                    SELF_ASSIGNMENT,
-                    expr.span,
-                    &format!("self-assignment of `{}` to `{}`", rhs, lhs),
-                );
-            }
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
index 975a0a06e38..2c8aa17e80d 100644
--- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
@@ -26,6 +26,9 @@ declare_clippy_lint! {
     /// let mut vec1 = Vec::with_capacity(len);
     /// vec1.resize(len, 0);
     ///
+    /// let mut vec1 = Vec::with_capacity(len);
+    /// vec1.resize(vec1.capacity(), 0);
+    ///
     /// let mut vec2 = Vec::with_capacity(len);
     /// vec2.extend(repeat(0).take(len));
     /// ```
@@ -211,23 +214,20 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
 
     /// Checks if the given expression is resizing a vector with 0
     fn search_slow_resize_filling(&mut self, expr: &'tcx Expr<'_>) {
-        if_chain! {
-            if self.initialization_found;
-            if let ExprKind::MethodCall(path, [self_arg, len_arg, fill_arg], _) = expr.kind;
-            if path_to_local_id(self_arg, self.vec_alloc.local_id);
-            if path.ident.name == sym!(resize);
-
+        if self.initialization_found
+            && let ExprKind::MethodCall(path, [self_arg, len_arg, fill_arg], _) = expr.kind
+            && path_to_local_id(self_arg, self.vec_alloc.local_id)
+            && path.ident.name == sym!(resize)
             // Check that is filled with 0
-            if let ExprKind::Lit(ref lit) = fill_arg.kind;
-            if let LitKind::Int(0, _) = lit.node;
-
-            // Check that len expression is equals to `with_capacity` expression
-            if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr);
-
-            then {
-                self.slow_expression = Some(InitializationType::Resize(expr));
+            && let ExprKind::Lit(ref lit) = fill_arg.kind
+            && let LitKind::Int(0, _) = lit.node {
+                // Check that len expression is equals to `with_capacity` expression
+                if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) {
+                    self.slow_expression = Some(InitializationType::Resize(expr));
+                } else if let ExprKind::MethodCall(path, _, _) = len_arg.kind && path.ident.as_str() == "capacity" {
+                    self.slow_expression = Some(InitializationType::Resize(expr));
+                }
             }
-        }
     }
 
     /// Returns `true` if give expression is `repeat(0).take(...)`
@@ -240,12 +240,15 @@ impl<'a, 'tcx> VectorInitializationVisitor<'a, 'tcx> {
             if let Some(repeat_expr) = take_args.get(0);
             if self.is_repeat_zero(repeat_expr);
 
-            // Check that len expression is equals to `with_capacity` expression
             if let Some(len_arg) = take_args.get(1);
-            if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr);
 
             then {
-                return true;
+                // Check that len expression is equals to `with_capacity` expression
+                if SpanlessEq::new(self.cx).eq_expr(len_arg, self.vec_alloc.len_expr) {
+                    return true;
+                } else if let ExprKind::MethodCall(path, _, _) = len_arg.kind && path.ident.as_str() == "capacity" {
+                    return true;
+                }
             }
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs
index 71f3e6b6a6e..eb704a07451 100644
--- a/src/tools/clippy/clippy_lints/src/strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strings.rs
@@ -60,6 +60,12 @@ declare_clippy_lint! {
     /// let x = "Hello".to_owned();
     /// x + ", World";
     /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// let mut x = "Hello".to_owned();
+    /// x.push_str(", World");
+    /// ```
     #[clippy::version = "pre 1.29.0"]
     pub STRING_ADD,
     restriction,
diff --git a/src/tools/clippy/clippy_lints/src/transmute/mod.rs b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
index cbe1406728b..5f3e98144f4 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/mod.rs
@@ -16,9 +16,10 @@ mod wrong_transmute;
 
 use clippy_utils::in_constant;
 use if_chain::if_chain;
-use rustc_hir::{Expr, ExprKind};
+use rustc_hir::{Expr, ExprKind, QPath};
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::{declare_lint_pass, declare_tool_lint};
+use rustc_semver::RustcVersion;
+use rustc_session::{declare_tool_lint, impl_lint_pass};
 use rustc_span::symbol::sym;
 
 declare_clippy_lint! {
@@ -385,7 +386,10 @@ declare_clippy_lint! {
     "transmute to or from a type with an undefined representation"
 }
 
-declare_lint_pass!(Transmute => [
+pub struct Transmute {
+    msrv: Option<RustcVersion>,
+}
+impl_lint_pass!(Transmute => [
     CROSSPOINTER_TRANSMUTE,
     TRANSMUTE_PTR_TO_REF,
     TRANSMUTE_PTR_TO_PTR,
@@ -401,13 +405,18 @@ declare_lint_pass!(Transmute => [
     TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS,
     TRANSMUTE_UNDEFINED_REPR,
 ]);
-
+impl Transmute {
+    #[must_use]
+    pub fn new(msrv: Option<RustcVersion>) -> Self {
+        Self { msrv }
+    }
+}
 impl<'tcx> LateLintPass<'tcx> for Transmute {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
         if_chain! {
             if let ExprKind::Call(path_expr, [arg]) = e.kind;
-            if let ExprKind::Path(ref qpath) = path_expr.kind;
-            if let Some(def_id) = cx.qpath_res(qpath, path_expr.hir_id).opt_def_id();
+            if let ExprKind::Path(QPath::Resolved(None, path)) = path_expr.kind;
+            if let Some(def_id) = path.res.opt_def_id();
             if cx.tcx.is_diagnostic_item(sym::transmute, def_id);
             then {
                 // Avoid suggesting non-const operations in const contexts:
@@ -427,7 +436,7 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
 
                 let linted = wrong_transmute::check(cx, e, from_ty, to_ty)
                     | crosspointer_transmute::check(cx, e, from_ty, to_ty)
-                    | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, qpath)
+                    | transmute_ptr_to_ref::check(cx, e, from_ty, to_ty, arg, path, self.msrv)
                     | transmute_int_to_char::check(cx, e, from_ty, to_ty, arg, const_context)
                     | transmute_ref_to_ref::check(cx, e, from_ty, to_ty, arg, const_context)
                     | transmute_ptr_to_ptr::check(cx, e, from_ty, to_ty, arg)
@@ -446,4 +455,6 @@ impl<'tcx> LateLintPass<'tcx> for Transmute {
             }
         }
     }
+
+    extract_msrv_attr!(LateContext);
 }
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
index f3653199b37..3ed5d5c6950 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ref.rs
@@ -1,11 +1,12 @@
-use super::utils::get_type_snippet;
 use super::TRANSMUTE_PTR_TO_REF;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::{meets_msrv, msrvs, sugg};
 use rustc_errors::Applicability;
-use rustc_hir::{Expr, Mutability, QPath};
+use rustc_hir::{self as hir, Expr, GenericArg, Mutability, Path, TyKind};
 use rustc_lint::LateContext;
-use rustc_middle::ty::{self, Ty};
+use rustc_middle::ty::{self, Ty, TypeFoldable};
+use rustc_semver::RustcVersion;
 
 /// Checks for `transmute_ptr_to_ref` lint.
 /// Returns `true` if it's triggered, otherwise returns `false`.
@@ -15,7 +16,8 @@ pub(super) fn check<'tcx>(
     from_ty: Ty<'tcx>,
     to_ty: Ty<'tcx>,
     arg: &'tcx Expr<'_>,
-    qpath: &'tcx QPath<'_>,
+    path: &'tcx Path<'_>,
+    msrv: Option<RustcVersion>,
 ) -> bool {
     match (&from_ty.kind(), &to_ty.kind()) {
         (ty::RawPtr(from_ptr_ty), ty::Ref(_, to_ref_ty, mutbl)) => {
@@ -34,19 +36,34 @@ pub(super) fn check<'tcx>(
                     } else {
                         ("&*", "*const")
                     };
+                    let mut app = Applicability::MachineApplicable;
 
-                    let arg = if from_ptr_ty.ty == *to_ref_ty {
-                        arg
+                    let sugg = if let Some(ty) = get_explicit_type(path) {
+                        let ty_snip = snippet_with_applicability(cx, ty.span, "..", &mut app);
+                        if meets_msrv(msrv, msrvs::POINTER_CAST) {
+                            format!("{}{}.cast::<{}>()", deref, arg.maybe_par(), ty_snip)
+                        } else if from_ptr_ty.has_erased_regions() {
+                            sugg::make_unop(deref, arg.as_ty(format!("{} () as {} {}", cast, cast, ty_snip)))
+                                .to_string()
+                        } else {
+                            sugg::make_unop(deref, arg.as_ty(format!("{} {}", cast, ty_snip))).to_string()
+                        }
+                    } else if from_ptr_ty.ty == *to_ref_ty {
+                        if from_ptr_ty.has_erased_regions() {
+                            if meets_msrv(msrv, msrvs::POINTER_CAST) {
+                                format!("{}{}.cast::<{}>()", deref, arg.maybe_par(), to_ref_ty)
+                            } else {
+                                sugg::make_unop(deref, arg.as_ty(format!("{} () as {} {}", cast, cast, to_ref_ty)))
+                                    .to_string()
+                            }
+                        } else {
+                            sugg::make_unop(deref, arg).to_string()
+                        }
                     } else {
-                        arg.as_ty(&format!("{} {}", cast, get_type_snippet(cx, qpath, *to_ref_ty)))
+                        sugg::make_unop(deref, arg.as_ty(format!("{} {}", cast, to_ref_ty))).to_string()
                     };
 
-                    diag.span_suggestion(
-                        e.span,
-                        "try",
-                        sugg::make_unop(deref, arg).to_string(),
-                        Applicability::Unspecified,
-                    );
+                    diag.span_suggestion(e.span, "try", sugg, app);
                 },
             );
             true
@@ -54,3 +71,14 @@ pub(super) fn check<'tcx>(
         _ => false,
     }
 }
+
+/// Gets the type `Bar` in `…::transmute<Foo, &Bar>`.
+fn get_explicit_type<'tcx>(path: &'tcx Path<'tcx>) -> Option<&'tcx hir::Ty<'tcx>> {
+    if let GenericArg::Type(ty) = path.segments.last()?.args?.args.get(1)?
+        && let TyKind::Rptr(_, ty) = &ty.kind
+    {
+        Some(ty.ty)
+    } else {
+        None
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/transmute/utils.rs b/src/tools/clippy/clippy_lints/src/transmute/utils.rs
index 0cbf5ccefa6..74927570b40 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/utils.rs
@@ -1,35 +1,9 @@
-use clippy_utils::last_path_segment;
-use clippy_utils::source::snippet;
-use if_chain::if_chain;
-use rustc_hir::{Expr, GenericArg, QPath, TyKind};
+use rustc_hir::Expr;
 use rustc_lint::LateContext;
 use rustc_middle::ty::{cast::CastKind, Ty};
 use rustc_span::DUMMY_SP;
 use rustc_typeck::check::{cast::CastCheck, FnCtxt, Inherited};
 
-/// Gets the snippet of `Bar` in `…::transmute<Foo, &Bar>`. If that snippet is
-/// not available , use
-/// the type's `ToString` implementation. In weird cases it could lead to types
-/// with invalid `'_`
-/// lifetime, but it should be rare.
-pub(super) fn get_type_snippet(cx: &LateContext<'_>, path: &QPath<'_>, to_ref_ty: Ty<'_>) -> String {
-    let seg = last_path_segment(path);
-    if_chain! {
-        if let Some(params) = seg.args;
-        if !params.parenthesized;
-        if let Some(to_ty) = params.args.iter().filter_map(|arg| match arg {
-            GenericArg::Type(ty) => Some(ty),
-            _ => None,
-        }).nth(1);
-        if let TyKind::Rptr(_, ref to_ty) = to_ty.kind;
-        then {
-            return snippet(cx, to_ty.ty.span, &to_ref_ty.to_string()).to_string();
-        }
-    }
-
-    to_ref_ty.to_string()
-}
-
 // check if the component types of the transmuted collection and the result have different ABI,
 // size or alignment
 pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {
diff --git a/src/tools/clippy/clippy_lints/src/unused_async.rs b/src/tools/clippy/clippy_lints/src/unused_async.rs
index c8ec4442ab1..a832dfcccaf 100644
--- a/src/tools/clippy/clippy_lints/src/unused_async.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_async.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_help;
 use rustc_hir::intravisit::{walk_expr, walk_fn, FnKind, Visitor};
-use rustc_hir::{Body, Expr, ExprKind, FnDecl, FnHeader, HirId, IsAsync, YieldSource};
+use rustc_hir::{Body, Expr, ExprKind, FnDecl, HirId, IsAsync, YieldSource};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
 use rustc_session::{declare_lint_pass, declare_tool_lint};
@@ -68,20 +68,18 @@ impl<'tcx> LateLintPass<'tcx> for UnusedAsync {
         span: Span,
         hir_id: HirId,
     ) {
-        if let FnKind::ItemFn(_, _, FnHeader { asyncness, .. }) = &fn_kind {
-            if matches!(asyncness, IsAsync::Async) {
-                let mut visitor = AsyncFnVisitor { cx, found_await: false };
-                walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), span, hir_id);
-                if !visitor.found_await {
-                    span_lint_and_help(
-                        cx,
-                        UNUSED_ASYNC,
-                        span,
-                        "unused `async` for function with no await statements",
-                        None,
-                        "consider removing the `async` from this function",
-                    );
-                }
+        if !span.from_expansion() && fn_kind.asyncness() == IsAsync::Async {
+            let mut visitor = AsyncFnVisitor { cx, found_await: false };
+            walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), span, hir_id);
+            if !visitor.found_await {
+                span_lint_and_help(
+                    cx,
+                    UNUSED_ASYNC,
+                    span,
+                    "unused `async` for function with no await statements",
+                    None,
+                    "consider removing the `async` from this function",
+                );
             }
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs
index 9b9e25326f9..d3f9e5abfd7 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap.rs
@@ -1,4 +1,4 @@
-use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::higher;
 use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::{path_to_local, usage::is_potentially_mutated};
@@ -251,9 +251,10 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
                             unwrappable.kind.error_variant_pattern()
                         };
 
-                        span_lint_and_then(
+                        span_lint_hir_and_then(
                             self.cx,
                             UNNECESSARY_UNWRAP,
+                            expr.hir_id,
                             expr.span,
                             &format!(
                                 "called `{}` on `{}` after checking its variant with `{}`",
@@ -283,9 +284,10 @@ impl<'a, 'tcx> Visitor<'tcx> for UnwrappableVariablesVisitor<'a, 'tcx> {
                             },
                         );
                     } else {
-                        span_lint_and_then(
+                        span_lint_hir_and_then(
                             self.cx,
                             PANICKING_UNWRAP,
+                            expr.hir_id,
                             expr.span,
                             &format!("this call to `{}()` will always panic",
                             method_name.ident.name),
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
index b885e5132f1..a94f0357977 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
@@ -1,3 +1,4 @@
+use crate::utils::internal_lints::metadata_collector::is_deprecated_lint;
 use clippy_utils::consts::{constant_simple, Constant};
 use clippy_utils::diagnostics::{span_lint, span_lint_and_help, span_lint_and_sugg, span_lint_and_then};
 use clippy_utils::macros::root_macro_call_first_node;
@@ -338,6 +339,46 @@ declare_clippy_lint! {
     "checking if all necessary steps were taken when adding a MSRV to a lint"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for cases of an auto-generated deprecated lint without an updated reason,
+    /// i.e. `"default deprecation note"`.
+    ///
+    /// ### Why is this bad?
+    /// Indicates that the documentation is incomplete.
+    ///
+    /// ### Example
+    /// ```rust,ignore
+    /// declare_deprecated_lint! {
+    ///     /// ### What it does
+    ///     /// Nothing. This lint has been deprecated.
+    ///     ///
+    ///     /// ### Deprecation reason
+    ///     /// TODO
+    ///     #[clippy::version = "1.63.0"]
+    ///     pub COOL_LINT,
+    ///     "default deprecation note"
+    /// }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```rust,ignore
+    /// declare_deprecated_lint! {
+    ///     /// ### What it does
+    ///     /// Nothing. This lint has been deprecated.
+    ///     ///
+    ///     /// ### Deprecation reason
+    ///     /// This lint has been replaced by `cooler_lint`
+    ///     #[clippy::version = "1.63.0"]
+    ///     pub COOL_LINT,
+    ///     "this lint has been replaced by `cooler_lint`"
+    /// }
+    /// ```
+    pub DEFAULT_DEPRECATION_REASON,
+    internal,
+    "found 'default deprecation note' in a deprecated lint declaration"
+}
+
 declare_lint_pass!(ClippyLintsInternal => [CLIPPY_LINTS_INTERNAL]);
 
 impl EarlyLintPass for ClippyLintsInternal {
@@ -375,42 +416,67 @@ pub struct LintWithoutLintPass {
     registered_lints: FxHashSet<Symbol>,
 }
 
-impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE]);
+impl_lint_pass!(LintWithoutLintPass => [DEFAULT_LINT, LINT_WITHOUT_LINT_PASS, INVALID_CLIPPY_VERSION_ATTRIBUTE, MISSING_CLIPPY_VERSION_ATTRIBUTE, DEFAULT_DEPRECATION_REASON]);
 
 impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-        if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id()) {
+        if is_lint_allowed(cx, DEFAULT_LINT, item.hir_id())
+            || is_lint_allowed(cx, DEFAULT_DEPRECATION_REASON, item.hir_id())
+        {
             return;
         }
 
         if let hir::ItemKind::Static(ty, Mutability::Not, body_id) = item.kind {
-            if is_lint_ref_type(cx, ty) {
+            let is_lint_ref_ty = is_lint_ref_type(cx, ty);
+            if is_deprecated_lint(cx, ty) || is_lint_ref_ty {
                 check_invalid_clippy_version_attribute(cx, item);
 
                 let expr = &cx.tcx.hir().body(body_id).value;
-                if_chain! {
-                    if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind;
-                    if let ExprKind::Struct(_, fields, _) = inner_exp.kind;
-                    let field = fields
-                        .iter()
-                        .find(|f| f.ident.as_str() == "desc")
-                        .expect("lints must have a description field");
-                    if let ExprKind::Lit(Spanned {
-                        node: LitKind::Str(ref sym, _),
-                        ..
-                    }) = field.expr.kind;
-                    if sym.as_str() == "default lint description";
-
-                    then {
+                let fields;
+                if is_lint_ref_ty {
+                    if let ExprKind::AddrOf(_, _, inner_exp) = expr.kind
+                        && let ExprKind::Struct(_, struct_fields, _) = inner_exp.kind {
+                            fields = struct_fields;
+                    } else {
+                        return;
+                    }
+                } else if let ExprKind::Struct(_, struct_fields, _) = expr.kind {
+                    fields = struct_fields;
+                } else {
+                    return;
+                }
+
+                let field = fields
+                    .iter()
+                    .find(|f| f.ident.as_str() == "desc")
+                    .expect("lints must have a description field");
+
+                if let ExprKind::Lit(Spanned {
+                    node: LitKind::Str(ref sym, _),
+                    ..
+                }) = field.expr.kind
+                {
+                    let sym_str = sym.as_str();
+                    if is_lint_ref_ty {
+                        if sym_str == "default lint description" {
+                            span_lint(
+                                cx,
+                                DEFAULT_LINT,
+                                item.span,
+                                &format!("the lint `{}` has the default lint description", item.ident.name),
+                            );
+                        }
+
+                        self.declared_lints.insert(item.ident.name, item.span);
+                    } else if sym_str == "default deprecation note" {
                         span_lint(
                             cx,
-                            DEFAULT_LINT,
+                            DEFAULT_DEPRECATION_REASON,
                             item.span,
-                            &format!("the lint `{}` has the default lint description", item.ident.name),
+                            &format!("the lint `{}` has the default deprecation reason", item.ident.name),
                         );
                     }
                 }
-                self.declared_lints.insert(item.ident.name, item.span);
             }
         } else if let Some(macro_call) = root_macro_call_first_node(cx, item) {
             if !matches!(
@@ -668,6 +734,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
             let body = cx.tcx.hir().body(*body);
             let only_expr = peel_blocks_with_stmt(&body.value);
             if let ExprKind::MethodCall(ps, span_call_args, _) = &only_expr.kind;
+            if let ExprKind::Path(..) = span_call_args[0].kind;
             then {
                 let and_then_snippets = get_and_then_snippets(cx, and_then_args);
                 let mut sle = SpanlessEq::new(cx).deny_side_effects();
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
index 2564099f4db..6518e0a6ea0 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/metadata_collector.rs
@@ -104,7 +104,7 @@ macro_rules! RENAME_VALUE_TEMPLATE {
     };
 }
 
-const LINT_EMISSION_FUNCTIONS: [&[&str]; 8] = [
+const LINT_EMISSION_FUNCTIONS: [&[&str]; 7] = [
     &["clippy_utils", "diagnostics", "span_lint"],
     &["clippy_utils", "diagnostics", "span_lint_and_help"],
     &["clippy_utils", "diagnostics", "span_lint_and_note"],
@@ -190,7 +190,12 @@ impl MetadataCollector {
             lints: BinaryHeap::<LintMetadata>::default(),
             applicability_info: FxHashMap::<String, ApplicabilityInfo>::default(),
             config: collect_configs(),
-            clippy_project_root: clippy_dev::clippy_project_root(),
+            clippy_project_root: std::env::current_dir()
+                .expect("failed to get current dir")
+                .ancestors()
+                .nth(1)
+                .expect("failed to get project root")
+                .to_path_buf(),
         }
     }
 
@@ -841,7 +846,7 @@ fn get_lint_level_from_group(lint_group: &str) -> Option<&'static str> {
         .find_map(|(group_name, group_level)| (*group_name == lint_group).then(|| *group_level))
 }
 
-fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
+pub(super) fn is_deprecated_lint(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
     if let hir::TyKind::Path(ref path) = ty.kind {
         if let hir::def::Res::Def(DefKind::Struct, def_id) = cx.qpath_res(path, ty.hir_id) {
             return match_def_path(cx, def_id, &DEPRECATED_LINT_TYPE);
diff --git a/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs b/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs
index 4d86abd0fa1..0fee3e812d2 100644
--- a/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/vec_resize_to_zero.rs
@@ -20,6 +20,11 @@ declare_clippy_lint! {
     /// ```rust
     /// vec!(1, 2, 3, 4, 5).resize(0, 5)
     /// ```
+    ///
+    /// Use instead:
+    /// ```rust
+    /// vec!(1, 2, 3, 4, 5).clear()
+    /// ```
     #[clippy::version = "1.46.0"]
     pub VEC_RESIZE_TO_ZERO,
     correctness,
diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml
index c4e0b8448ab..bb443bdc116 100644
--- a/src/tools/clippy/clippy_utils/Cargo.toml
+++ b/src/tools/clippy/clippy_utils/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_utils"
-version = "0.1.63"
+version = "0.1.64"
 edition = "2021"
 publish = false
 
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index af62c4afd5a..793e3cc58c2 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -1,4 +1,5 @@
 use crate::consts::constant_simple;
+use crate::macros::macro_backtrace;
 use crate::source::snippet_opt;
 use rustc_ast::ast::InlineAsmTemplatePiece;
 use rustc_data_structures::fx::FxHasher;
@@ -12,9 +13,13 @@ use rustc_hir::{
 use rustc_lexer::{tokenize, TokenKind};
 use rustc_lint::LateContext;
 use rustc_middle::ty::TypeckResults;
-use rustc_span::Symbol;
+use rustc_span::{sym, Symbol};
 use std::hash::{Hash, Hasher};
 
+/// Callback that is called when two expressions are not equal in the sense of `SpanlessEq`, but
+/// other conditions would make them equal.
+type SpanlessEqCallback<'a> = dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a;
+
 /// Type used to check whether two ast are the same. This is different from the
 /// operator `==` on ast types as this operator would compare true equality with
 /// ID and span.
@@ -25,7 +30,7 @@ pub struct SpanlessEq<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
     maybe_typeck_results: Option<(&'tcx TypeckResults<'tcx>, &'tcx TypeckResults<'tcx>)>,
     allow_side_effects: bool,
-    expr_fallback: Option<Box<dyn FnMut(&Expr<'_>, &Expr<'_>) -> bool + 'a>>,
+    expr_fallback: Option<Box<SpanlessEqCallback<'a>>>,
 }
 
 impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
@@ -121,6 +126,9 @@ impl HirEqInterExpr<'_, '_, '_> {
 
     /// Checks whether two blocks are the same.
     fn eq_block(&mut self, left: &Block<'_>, right: &Block<'_>) -> bool {
+        if self.cannot_be_compared_block(left) || self.cannot_be_compared_block(right) {
+            return false;
+        }
         match (left.stmts, left.expr, right.stmts, right.expr) {
             ([], None, [], None) => {
                 // For empty blocks, check to see if the tokens are equal. This will catch the case where a macro
@@ -171,6 +179,38 @@ impl HirEqInterExpr<'_, '_, '_> {
         }
     }
 
+    fn cannot_be_compared_block(&mut self, block: &Block<'_>) -> bool {
+        if block.stmts.last().map_or(false, |stmt| {
+            matches!(
+                stmt.kind,
+                StmtKind::Semi(semi_expr) if self.should_ignore(semi_expr)
+            )
+        }) {
+            return true;
+        }
+
+        if let Some(block_expr) = block.expr
+            && self.should_ignore(block_expr)
+        {
+            return true
+        }
+
+        false
+    }
+
+    fn should_ignore(&mut self, expr: &Expr<'_>) -> bool {
+        if macro_backtrace(expr.span).last().map_or(false, |macro_call| {
+            matches!(
+                &self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id),
+                Some(sym::todo_macro | sym::unimplemented_macro)
+            )
+        }) {
+            return true;
+        }
+
+        false
+    }
+
     pub fn eq_array_length(&mut self, left: ArrayLen, right: ArrayLen) -> bool {
         match (left, right) {
             (ArrayLen::Infer(..), ArrayLen::Infer(..)) => true,
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 5106c39b5c6..9fa28e137f9 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -1539,9 +1539,13 @@ pub fn is_try<'tcx>(cx: &LateContext<'_>, expr: &'tcx Expr<'tcx>) -> Option<&'tc
     None
 }
 
-/// Returns `true` if the lint is allowed in the current context
+/// Returns `true` if the lint is allowed in the current context. This is useful for
+/// skipping long running code when it's unnecessary
 ///
-/// Useful for skipping long running code when it's unnecessary
+/// This function should check the lint level for the same node, that the lint will
+/// be emitted at. If the information is buffered to be emitted at a later point, please
+/// make sure to use `span_lint_hir` functions to emit the lint. This ensures that
+/// expectations at the checked nodes will be fulfilled.
 pub fn is_lint_allowed(cx: &LateContext<'_>, lint: &'static Lint, id: HirId) -> bool {
     cx.tcx.lint_level_at_node(lint, id).0 == Level::Allow
 }
@@ -2058,6 +2062,21 @@ pub fn peel_hir_expr_refs<'a>(expr: &'a Expr<'a>) -> (&'a Expr<'a>, usize) {
     (e, count)
 }
 
+/// Peels off all references on the type. Returns the underlying type and the number of references
+/// removed.
+pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize) {
+    let mut count = 0;
+    loop {
+        match &ty.kind {
+            TyKind::Rptr(_, ref_ty) => {
+                ty = ref_ty.ty;
+                count += 1;
+            },
+            _ => break (ty, count),
+        }
+    }
+}
+
 /// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
 /// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
 pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
@@ -2110,7 +2129,7 @@ fn with_test_item_names<'tcx>(tcx: TyCtxt<'tcx>, module: LocalDefId, f: impl Fn(
                 }
             }
             names.sort_unstable();
-            f(&*entry.insert(names))
+            f(entry.insert(names))
         },
     }
 }
@@ -2168,6 +2187,50 @@ pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
             && item.ident.name.as_str().split('_').any(|a| a == "test" || a == "tests")
 }
 
+/// Walks the HIR tree from the given expression, up to the node where the value produced by the
+/// expression is consumed. Calls the function for every node encountered this way until it returns
+/// `Some`.
+///
+/// This allows walking through `if`, `match`, `break`, block expressions to find where the value
+/// produced by the expression is consumed.
+pub fn walk_to_expr_usage<'tcx, T>(
+    cx: &LateContext<'tcx>,
+    e: &Expr<'tcx>,
+    mut f: impl FnMut(Node<'tcx>, HirId) -> Option<T>,
+) -> Option<T> {
+    let map = cx.tcx.hir();
+    let mut iter = map.parent_iter(e.hir_id);
+    let mut child_id = e.hir_id;
+
+    while let Some((parent_id, parent)) = iter.next() {
+        if let Some(x) = f(parent, child_id) {
+            return Some(x);
+        }
+        let parent = match parent {
+            Node::Expr(e) => e,
+            Node::Block(Block { expr: Some(body), .. }) | Node::Arm(Arm { body, .. }) if body.hir_id == child_id => {
+                child_id = parent_id;
+                continue;
+            },
+            Node::Arm(a) if a.body.hir_id == child_id => {
+                child_id = parent_id;
+                continue;
+            },
+            _ => return None,
+        };
+        match parent.kind {
+            ExprKind::If(child, ..) | ExprKind::Match(child, ..) if child.hir_id != child_id => child_id = parent_id,
+            ExprKind::Break(Destination { target_id: Ok(id), .. }, _) => {
+                child_id = id;
+                iter = map.parent_iter(id);
+            },
+            ExprKind::Block(..) => child_id = parent_id,
+            _ => return None,
+        }
+    }
+    None
+}
+
 macro_rules! op_utils {
     ($($name:ident $assign:ident)*) => {
         /// Binary operation traits like `LangItem::Add`
diff --git a/src/tools/clippy/clippy_utils/src/msrvs.rs b/src/tools/clippy/clippy_utils/src/msrvs.rs
index b9ec2c19fdd..b09c929f76e 100644
--- a/src/tools/clippy/clippy_utils/src/msrvs.rs
+++ b/src/tools/clippy/clippy_utils/src/msrvs.rs
@@ -12,8 +12,8 @@ macro_rules! msrv_aliases {
 
 // names may refer to stabilized feature flags or library items
 msrv_aliases! {
-    1,53,0 { OR_PATTERNS, MANUAL_BITS }
-    1,52,0 { STR_SPLIT_ONCE }
+    1,53,0 { OR_PATTERNS, MANUAL_BITS, BTREE_MAP_RETAIN, BTREE_SET_RETAIN }
+    1,52,0 { STR_SPLIT_ONCE, REM_EUCLID_CONST }
     1,51,0 { BORROW_AS_PTR, UNSIGNED_ABS }
     1,50,0 { BOOL_THEN }
     1,47,0 { TAU }
@@ -23,14 +23,15 @@ msrv_aliases! {
     1,42,0 { MATCHES_MACRO, SLICE_PATTERNS, PTR_SLICE_RAW_PARTS }
     1,41,0 { RE_REBALANCING_COHERENCE, RESULT_MAP_OR_ELSE }
     1,40,0 { MEM_TAKE, NON_EXHAUSTIVE, OPTION_AS_DEREF }
-    1,38,0 { POINTER_CAST }
+    1,38,0 { POINTER_CAST, REM_EUCLID }
     1,37,0 { TYPE_ALIAS_ENUM_VARIANTS }
     1,36,0 { ITERATOR_COPIED }
     1,35,0 { OPTION_COPIED, RANGE_CONTAINS }
     1,34,0 { TRY_FROM }
     1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
     1,28,0 { FROM_BOOL }
-    1,26,0 { RANGE_INCLUSIVE }
+    1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN }
+    1,18,0 { HASH_MAP_RETAIN, HASH_SET_RETAIN }
     1,17,0 { FIELD_INIT_SHORTHAND, STATIC_IN_CONST, EXPECT_ERR }
     1,16,0 { STR_REPEAT }
     1,24,0 { IS_ASCII_DIGIT }
diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs
index 89789c3d851..6542e77113b 100644
--- a/src/tools/clippy/clippy_utils/src/paths.rs
+++ b/src/tools/clippy/clippy_utils/src/paths.rs
@@ -21,8 +21,14 @@ pub const ASREF_TRAIT: [&str; 3] = ["core", "convert", "AsRef"];
 pub const BTREEMAP_CONTAINS_KEY: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "contains_key"];
 pub const BTREEMAP_ENTRY: [&str; 6] = ["alloc", "collections", "btree", "map", "entry", "Entry"];
 pub const BTREEMAP_INSERT: [&str; 6] = ["alloc", "collections", "btree", "map", "BTreeMap", "insert"];
+pub const BTREESET_ITER: [&str; 6] = ["alloc", "collections", "btree", "set", "BTreeSet", "iter"];
 pub const CLONE_TRAIT_METHOD: [&str; 4] = ["core", "clone", "Clone", "clone"];
 pub const COW: [&str; 3] = ["alloc", "borrow", "Cow"];
+pub const CORE_ITER_COLLECT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "collect"];
+pub const CORE_ITER_CLONED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "cloned"];
+pub const CORE_ITER_COPIED: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "copied"];
+pub const CORE_ITER_FILTER: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "filter"];
+pub const CORE_ITER_INTO_ITER: [&str; 6] = ["core", "iter", "traits", "collect", "IntoIterator", "into_iter"];
 pub const CSTRING_AS_C_STR: [&str; 5] = ["alloc", "ffi", "c_str", "CString", "as_c_str"];
 pub const DEFAULT_TRAIT_METHOD: [&str; 4] = ["core", "default", "Default", "default"];
 pub const DEREF_MUT_TRAIT_METHOD: [&str; 5] = ["core", "ops", "deref", "DerefMut", "deref_mut"];
@@ -50,6 +56,7 @@ pub const FUTURES_IO_ASYNCWRITEEXT: [&str; 3] = ["futures_util", "io", "AsyncWri
 pub const HASHMAP_CONTAINS_KEY: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "contains_key"];
 pub const HASHMAP_ENTRY: [&str; 5] = ["std", "collections", "hash", "map", "Entry"];
 pub const HASHMAP_INSERT: [&str; 6] = ["std", "collections", "hash", "map", "HashMap", "insert"];
+pub const HASHSET_ITER: [&str; 6] = ["std", "collections", "hash", "set", "HashSet", "iter"];
 #[cfg(feature = "internal")]
 pub const IDENT: [&str; 3] = ["rustc_span", "symbol", "Ident"];
 #[cfg(feature = "internal")]
@@ -62,6 +69,7 @@ pub const IO_WRITE: [&str; 3] = ["std", "io", "Write"];
 pub const IPADDR_V4: [&str; 5] = ["std", "net", "ip", "IpAddr", "V4"];
 pub const IPADDR_V6: [&str; 5] = ["std", "net", "ip", "IpAddr", "V6"];
 pub const ITER_COUNT: [&str; 6] = ["core", "iter", "traits", "iterator", "Iterator", "count"];
+pub const ITER_EMPTY: [&str; 5] = ["core", "iter", "sources", "empty", "Empty"];
 pub const ITER_REPEAT: [&str; 5] = ["core", "iter", "sources", "repeat", "repeat"];
 #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 pub const ITERTOOLS_NEXT_TUPLE: [&str; 3] = ["itertools", "Itertools", "next_tuple"];
@@ -83,8 +91,6 @@ pub const OPTION_SOME: [&str; 4] = ["core", "option", "Option", "Some"];
 pub const ORD: [&str; 3] = ["core", "cmp", "Ord"];
 pub const OS_STRING_AS_OS_STR: [&str; 5] = ["std", "ffi", "os_str", "OsString", "as_os_str"];
 pub const OS_STR_TO_OS_STRING: [&str; 5] = ["std", "ffi", "os_str", "OsStr", "to_os_string"];
-pub const PARKING_LOT_RAWMUTEX: [&str; 3] = ["parking_lot", "raw_mutex", "RawMutex"];
-pub const PARKING_LOT_RAWRWLOCK: [&str; 3] = ["parking_lot", "raw_rwlock", "RawRwLock"];
 pub const PARKING_LOT_MUTEX_GUARD: [&str; 3] = ["lock_api", "mutex", "MutexGuard"];
 pub const PARKING_LOT_RWLOCK_READ_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockReadGuard"];
 pub const PARKING_LOT_RWLOCK_WRITE_GUARD: [&str; 3] = ["lock_api", "rwlock", "RwLockWriteGuard"];
@@ -144,6 +150,7 @@ pub const SLICE_FROM_RAW_PARTS: [&str; 4] = ["core", "slice", "raw", "from_raw_p
 pub const SLICE_FROM_RAW_PARTS_MUT: [&str; 4] = ["core", "slice", "raw", "from_raw_parts_mut"];
 pub const SLICE_GET: [&str; 4] = ["core", "slice", "<impl [T]>", "get"];
 pub const SLICE_INTO_VEC: [&str; 4] = ["alloc", "slice", "<impl [T]>", "into_vec"];
+pub const SLICE_INTO: [&str; 4] = ["core", "slice", "<impl [T]>", "iter"];
 pub const SLICE_ITER: [&str; 4] = ["core", "slice", "iter", "Iter"];
 pub const STDERR: [&str; 4] = ["std", "io", "stdio", "stderr"];
 pub const STDOUT: [&str; 4] = ["std", "io", "stdio", "stdout"];
@@ -153,6 +160,7 @@ pub const STRING_AS_MUT_STR: [&str; 4] = ["alloc", "string", "String", "as_mut_s
 pub const STRING_AS_STR: [&str; 4] = ["alloc", "string", "String", "as_str"];
 pub const STRING_NEW: [&str; 4] = ["alloc", "string", "String", "new"];
 pub const STR_BYTES: [&str; 4] = ["core", "str", "<impl str>", "bytes"];
+pub const STR_CHARS: [&str; 4] = ["core", "str", "<impl str>", "chars"];
 pub const STR_ENDS_WITH: [&str; 4] = ["core", "str", "<impl str>", "ends_with"];
 pub const STR_FROM_UTF8: [&str; 4] = ["core", "str", "converts", "from_utf8"];
 pub const STR_LEN: [&str; 4] = ["core", "str", "<impl str>", "len"];
@@ -178,6 +186,7 @@ pub const TOKIO_IO_ASYNCWRITEEXT: [&str; 5] = ["tokio", "io", "util", "async_wri
 pub const TRY_FROM: [&str; 4] = ["core", "convert", "TryFrom", "try_from"];
 pub const VEC_AS_MUT_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_mut_slice"];
 pub const VEC_AS_SLICE: [&str; 4] = ["alloc", "vec", "Vec", "as_slice"];
+pub const VEC_DEQUE_ITER: [&str; 5] = ["alloc", "collections", "vec_deque", "VecDeque", "iter"];
 pub const VEC_FROM_ELEM: [&str; 3] = ["alloc", "vec", "from_elem"];
 pub const VEC_NEW: [&str; 4] = ["alloc", "vec", "Vec", "new"];
 pub const VEC_RESIZE: [&str; 4] = ["alloc", "vec", "Vec", "resize"];
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 227e97d37ec..6ca36eed4e6 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -2,19 +2,20 @@
 
 #![allow(clippy::module_name_repetitions)]
 
+use core::ops::ControlFlow;
 use rustc_ast::ast::Mutability;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir as hir;
-use rustc_hir::def::{CtorKind, DefKind, Res};
+use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
 use rustc_hir::def_id::DefId;
-use rustc_hir::{Expr, LangItem, TyKind, Unsafety};
+use rustc_hir::{Expr, FnDecl, LangItem, TyKind, Unsafety};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::LateContext;
 use rustc_middle::mir::interpret::{ConstValue, Scalar};
 use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
 use rustc_middle::ty::{
-    self, AdtDef, Binder, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, Ty, TyCtxt, TypeFoldable, UintTy,
-    VariantDiscr,
+    self, AdtDef, Binder, BoundRegion, DefIdTree, FnSig, IntTy, ParamEnv, Predicate, PredicateKind, ProjectionTy,
+    Region, RegionKind, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable, TypeVisitor, UintTy, VariantDef, VariantDiscr,
 };
 use rustc_span::symbol::Ident;
 use rustc_span::{sym, Span, Symbol, DUMMY_SP};
@@ -501,16 +502,46 @@ pub fn all_predicates_of(tcx: TyCtxt<'_>, id: DefId) -> impl Iterator<Item = &(P
 #[derive(Clone, Copy)]
 pub enum ExprFnSig<'tcx> {
     Sig(Binder<'tcx, FnSig<'tcx>>),
-    Closure(Binder<'tcx, FnSig<'tcx>>),
+    Closure(Option<&'tcx FnDecl<'tcx>>, Binder<'tcx, FnSig<'tcx>>),
     Trait(Binder<'tcx, Ty<'tcx>>, Option<Binder<'tcx, Ty<'tcx>>>),
 }
 impl<'tcx> ExprFnSig<'tcx> {
-    /// Gets the argument type at the given offset.
-    pub fn input(self, i: usize) -> Binder<'tcx, Ty<'tcx>> {
+    /// Gets the argument type at the given offset. This will return `None` when the index is out of
+    /// bounds only for variadic functions, otherwise this will panic.
+    pub fn input(self, i: usize) -> Option<Binder<'tcx, Ty<'tcx>>> {
         match self {
-            Self::Sig(sig) => sig.input(i),
-            Self::Closure(sig) => sig.input(0).map_bound(|ty| ty.tuple_fields()[i]),
-            Self::Trait(inputs, _) => inputs.map_bound(|ty| ty.tuple_fields()[i]),
+            Self::Sig(sig) => {
+                if sig.c_variadic() {
+                    sig.inputs().map_bound(|inputs| inputs.get(i).copied()).transpose()
+                } else {
+                    Some(sig.input(i))
+                }
+            },
+            Self::Closure(_, sig) => Some(sig.input(0).map_bound(|ty| ty.tuple_fields()[i])),
+            Self::Trait(inputs, _) => Some(inputs.map_bound(|ty| ty.tuple_fields()[i])),
+        }
+    }
+
+    /// Gets the argument type at the given offset. For closures this will also get the type as
+    /// written. This will return `None` when the index is out of bounds only for variadic
+    /// functions, otherwise this will panic.
+    pub fn input_with_hir(self, i: usize) -> Option<(Option<&'tcx hir::Ty<'tcx>>, Binder<'tcx, Ty<'tcx>>)> {
+        match self {
+            Self::Sig(sig) => {
+                if sig.c_variadic() {
+                    sig.inputs()
+                        .map_bound(|inputs| inputs.get(i).copied())
+                        .transpose()
+                        .map(|arg| (None, arg))
+                } else {
+                    Some((None, sig.input(i)))
+                }
+            },
+            Self::Closure(decl, sig) => Some((
+                decl.and_then(|decl| decl.inputs.get(i)),
+                sig.input(0).map_bound(|ty| ty.tuple_fields()[i]),
+            )),
+            Self::Trait(inputs, _) => Some((None, inputs.map_bound(|ty| ty.tuple_fields()[i]))),
         }
     }
 
@@ -518,7 +549,7 @@ impl<'tcx> ExprFnSig<'tcx> {
     /// specified.
     pub fn output(self) -> Option<Binder<'tcx, Ty<'tcx>>> {
         match self {
-            Self::Sig(sig) | Self::Closure(sig) => Some(sig.output()),
+            Self::Sig(sig) | Self::Closure(_, sig) => Some(sig.output()),
             Self::Trait(_, output) => output,
         }
     }
@@ -529,74 +560,123 @@ pub fn expr_sig<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<ExprFnS
     if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) = path_res(cx, expr) {
         Some(ExprFnSig::Sig(cx.tcx.fn_sig(id)))
     } else {
-        let ty = cx.typeck_results().expr_ty_adjusted(expr).peel_refs();
-        match *ty.kind() {
-            ty::Closure(_, subs) => Some(ExprFnSig::Closure(subs.as_closure().sig())),
-            ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs))),
-            ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)),
-            ty::Dynamic(bounds, _) => {
-                let lang_items = cx.tcx.lang_items();
-                match bounds.principal() {
-                    Some(bound)
-                        if Some(bound.def_id()) == lang_items.fn_trait()
-                            || Some(bound.def_id()) == lang_items.fn_once_trait()
-                            || Some(bound.def_id()) == lang_items.fn_mut_trait() =>
-                    {
-                        let output = bounds
-                            .projection_bounds()
-                            .find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
-                            .map(|p| p.map_bound(|p| p.term.ty().expect("return type was a const")));
-                        Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output))
-                    },
-                    _ => None,
+        ty_sig(cx, cx.typeck_results().expr_ty_adjusted(expr).peel_refs())
+    }
+}
+
+fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
+    match *ty.kind() {
+        ty::Closure(id, subs) => {
+            let decl = id
+                .as_local()
+                .and_then(|id| cx.tcx.hir().fn_decl_by_hir_id(cx.tcx.hir().local_def_id_to_hir_id(id)));
+            Some(ExprFnSig::Closure(decl, subs.as_closure().sig()))
+        },
+        ty::FnDef(id, subs) => Some(ExprFnSig::Sig(cx.tcx.bound_fn_sig(id).subst(cx.tcx, subs))),
+        ty::FnPtr(sig) => Some(ExprFnSig::Sig(sig)),
+        ty::Dynamic(bounds, _) => {
+            let lang_items = cx.tcx.lang_items();
+            match bounds.principal() {
+                Some(bound)
+                    if Some(bound.def_id()) == lang_items.fn_trait()
+                        || Some(bound.def_id()) == lang_items.fn_once_trait()
+                        || Some(bound.def_id()) == lang_items.fn_mut_trait() =>
+                {
+                    let output = bounds
+                        .projection_bounds()
+                        .find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
+                        .map(|p| p.map_bound(|p| p.term.ty().unwrap()));
+                    Some(ExprFnSig::Trait(bound.map_bound(|b| b.substs.type_at(0)), output))
+                },
+                _ => None,
+            }
+        },
+        ty::Projection(proj) => match cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) {
+            Ok(normalized_ty) if normalized_ty != ty => ty_sig(cx, normalized_ty),
+            _ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty)),
+        },
+        ty::Param(_) => sig_from_bounds(cx, ty),
+        _ => None,
+    }
+}
+
+fn sig_from_bounds<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'tcx>> {
+    let mut inputs = None;
+    let mut output = None;
+    let lang_items = cx.tcx.lang_items();
+
+    for (pred, _) in all_predicates_of(cx.tcx, cx.typeck_results().hir_owner.to_def_id()) {
+        match pred.kind().skip_binder() {
+            PredicateKind::Trait(p)
+                if (lang_items.fn_trait() == Some(p.def_id())
+                    || lang_items.fn_mut_trait() == Some(p.def_id())
+                    || lang_items.fn_once_trait() == Some(p.def_id()))
+                    && p.self_ty() == ty =>
+            {
+                if inputs.is_some() {
+                    // Multiple different fn trait impls. Is this even allowed?
+                    return None;
                 }
+                inputs = Some(pred.kind().rebind(p.trait_ref.substs.type_at(1)));
             },
-            ty::Param(_) | ty::Projection(..) => {
-                let mut inputs = None;
-                let mut output = None;
-                let lang_items = cx.tcx.lang_items();
-
-                for (pred, _) in all_predicates_of(cx.tcx, cx.typeck_results().hir_owner.to_def_id()) {
-                    let mut is_input = false;
-                    if let Some(ty) = pred
-                        .kind()
-                        .map_bound(|pred| match pred {
-                            PredicateKind::Trait(p)
-                                if (lang_items.fn_trait() == Some(p.def_id())
-                                    || lang_items.fn_mut_trait() == Some(p.def_id())
-                                    || lang_items.fn_once_trait() == Some(p.def_id()))
-                                    && p.self_ty() == ty =>
-                            {
-                                is_input = true;
-                                Some(p.trait_ref.substs.type_at(1))
-                            },
-                            PredicateKind::Projection(p)
-                                if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output()
-                                    && p.projection_ty.self_ty() == ty =>
-                            {
-                                is_input = false;
-                                p.term.ty()
-                            },
-                            _ => None,
-                        })
-                        .transpose()
-                    {
-                        if is_input && inputs.is_none() {
-                            inputs = Some(ty);
-                        } else if !is_input && output.is_none() {
-                            output = Some(ty);
-                        } else {
-                            // Multiple different fn trait impls. Is this even allowed?
-                            return None;
-                        }
-                    }
+            PredicateKind::Projection(p)
+                if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output()
+                    && p.projection_ty.self_ty() == ty =>
+            {
+                if output.is_some() {
+                    // Multiple different fn trait impls. Is this even allowed?
+                    return None;
                 }
+                output = Some(pred.kind().rebind(p.term.ty().unwrap()));
+            },
+            _ => (),
+        }
+    }
+
+    inputs.map(|ty| ExprFnSig::Trait(ty, output))
+}
+
+fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: ProjectionTy<'tcx>) -> Option<ExprFnSig<'tcx>> {
+    let mut inputs = None;
+    let mut output = None;
+    let lang_items = cx.tcx.lang_items();
 
-                inputs.map(|ty| ExprFnSig::Trait(ty, output))
+    for pred in cx
+        .tcx
+        .bound_explicit_item_bounds(ty.item_def_id)
+        .transpose_iter()
+        .map(|x| x.map_bound(|(p, _)| p))
+    {
+        match pred.0.kind().skip_binder() {
+            PredicateKind::Trait(p)
+                if (lang_items.fn_trait() == Some(p.def_id())
+                    || lang_items.fn_mut_trait() == Some(p.def_id())
+                    || lang_items.fn_once_trait() == Some(p.def_id())) =>
+            {
+                if inputs.is_some() {
+                    // Multiple different fn trait impls. Is this even allowed?
+                    return None;
+                }
+                inputs = Some(
+                    pred.map_bound(|pred| pred.kind().rebind(p.trait_ref.substs.type_at(1)))
+                        .subst(cx.tcx, ty.substs),
+                );
             },
-            _ => None,
+            PredicateKind::Projection(p) if Some(p.projection_ty.item_def_id) == lang_items.fn_once_output() => {
+                if output.is_some() {
+                    // Multiple different fn trait impls. Is this even allowed?
+                    return None;
+                }
+                output = Some(
+                    pred.map_bound(|pred| pred.kind().rebind(p.term.ty().unwrap()))
+                        .subst(cx.tcx, ty.substs),
+                );
+            },
+            _ => (),
         }
     }
+
+    inputs.map(|ty| ExprFnSig::Trait(ty, output))
 }
 
 #[derive(Clone, Copy)]
@@ -667,3 +747,45 @@ pub fn is_c_void(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
         false
     }
 }
+
+pub fn for_each_top_level_late_bound_region<B>(
+    ty: Ty<'_>,
+    f: impl FnMut(BoundRegion) -> ControlFlow<B>,
+) -> ControlFlow<B> {
+    struct V<F> {
+        index: u32,
+        f: F,
+    }
+    impl<'tcx, B, F: FnMut(BoundRegion) -> ControlFlow<B>> TypeVisitor<'tcx> for V<F> {
+        type BreakTy = B;
+        fn visit_region(&mut self, r: Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+            if let RegionKind::ReLateBound(idx, bound) = r.kind() && idx.as_u32() == self.index {
+                (self.f)(bound)
+            } else {
+                ControlFlow::Continue(())
+            }
+        }
+        fn visit_binder<T: TypeFoldable<'tcx>>(&mut self, t: &Binder<'tcx, T>) -> ControlFlow<Self::BreakTy> {
+            self.index += 1;
+            let res = t.super_visit_with(self);
+            self.index -= 1;
+            res
+        }
+    }
+    ty.visit_with(&mut V { index: 0, f })
+}
+
+/// Gets the struct or enum variant from the given `Res`
+pub fn variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<&'tcx VariantDef> {
+    match res {
+        Res::Def(DefKind::Struct, id) => Some(cx.tcx.adt_def(id).non_enum_variant()),
+        Res::Def(DefKind::Variant, id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).variant_with_id(id)),
+        Res::Def(DefKind::Ctor(CtorOf::Struct, _), id) => Some(cx.tcx.adt_def(cx.tcx.parent(id)).non_enum_variant()),
+        Res::Def(DefKind::Ctor(CtorOf::Variant, _), id) => {
+            let var_id = cx.tcx.parent(id);
+            Some(cx.tcx.adt_def(cx.tcx.parent(var_id)).variant_with_id(var_id))
+        },
+        Res::SelfCtor(id) => Some(cx.tcx.type_of(id).ty_adt_def().unwrap().non_enum_variant()),
+        _ => None,
+    }
+}
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index b6c8f1d516e..68cfa8c1aa8 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -1,16 +1,18 @@
+use crate::ty::needs_ordered_drop;
 use crate::{get_enclosing_block, path_to_local_id};
 use core::ops::ControlFlow;
 use rustc_hir as hir;
-use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::intravisit::{self, walk_block, walk_expr, Visitor};
 use rustc_hir::{
-    Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Stmt, UnOp, UnsafeSource,
-    Unsafety,
+    Arm, Block, BlockCheckMode, Body, BodyId, Expr, ExprKind, HirId, ItemId, ItemKind, Let, QPath, Stmt, UnOp,
+    UnsafeSource, Unsafety,
 };
 use rustc_lint::LateContext;
 use rustc_middle::hir::map::Map;
 use rustc_middle::hir::nested_filter;
-use rustc_middle::ty;
+use rustc_middle::ty::adjustment::Adjust;
+use rustc_middle::ty::{self, Ty, TypeckResults};
 
 /// Convenience method for creating a `Visitor` with just `visit_expr` overridden and nested
 /// bodies (i.e. closures) are visited.
@@ -494,3 +496,124 @@ pub fn for_each_local_use_after_expr<'tcx, B>(
         ControlFlow::Continue(())
     }
 }
+
+// Calls the given function for every unconsumed temporary created by the expression. Note the
+// function is only guaranteed to be called for types which need to be dropped, but it may be called
+// for other types.
+pub fn for_each_unconsumed_temporary<'tcx, B>(
+    cx: &LateContext<'tcx>,
+    e: &'tcx Expr<'tcx>,
+    mut f: impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
+) -> ControlFlow<B> {
+    // Todo: Handle partially consumed values.
+    fn helper<'tcx, B>(
+        typeck: &'tcx TypeckResults<'tcx>,
+        consume: bool,
+        e: &'tcx Expr<'tcx>,
+        f: &mut impl FnMut(Ty<'tcx>) -> ControlFlow<B>,
+    ) -> ControlFlow<B> {
+        if !consume
+            || matches!(
+                typeck.expr_adjustments(e),
+                [adjust, ..] if matches!(adjust.kind, Adjust::Borrow(_) | Adjust::Deref(_))
+            )
+        {
+            match e.kind {
+                ExprKind::Path(QPath::Resolved(None, p))
+                    if matches!(p.res, Res::Def(DefKind::Ctor(_, CtorKind::Const), _)) =>
+                {
+                    f(typeck.expr_ty(e))?;
+                },
+                ExprKind::Path(_)
+                | ExprKind::Unary(UnOp::Deref, _)
+                | ExprKind::Index(..)
+                | ExprKind::Field(..)
+                | ExprKind::AddrOf(..) => (),
+                _ => f(typeck.expr_ty(e))?,
+            }
+        }
+        match e.kind {
+            ExprKind::AddrOf(_, _, e)
+            | ExprKind::Field(e, _)
+            | ExprKind::Unary(UnOp::Deref, e)
+            | ExprKind::Match(e, ..)
+            | ExprKind::Let(&Let { init: e, .. }) => {
+                helper(typeck, false, e, f)?;
+            },
+            ExprKind::Block(&Block { expr: Some(e), .. }, _)
+            | ExprKind::Box(e)
+            | ExprKind::Cast(e, _)
+            | ExprKind::Unary(_, e) => {
+                helper(typeck, true, e, f)?;
+            },
+            ExprKind::Call(callee, args) => {
+                helper(typeck, true, callee, f)?;
+                for arg in args {
+                    helper(typeck, true, arg, f)?;
+                }
+            },
+            ExprKind::MethodCall(_, args, _) | ExprKind::Tup(args) | ExprKind::Array(args) => {
+                for arg in args {
+                    helper(typeck, true, arg, f)?;
+                }
+            },
+            ExprKind::Index(borrowed, consumed)
+            | ExprKind::Assign(borrowed, consumed, _)
+            | ExprKind::AssignOp(_, borrowed, consumed) => {
+                helper(typeck, false, borrowed, f)?;
+                helper(typeck, true, consumed, f)?;
+            },
+            ExprKind::Binary(_, lhs, rhs) => {
+                helper(typeck, true, lhs, f)?;
+                helper(typeck, true, rhs, f)?;
+            },
+            ExprKind::Struct(_, fields, default) => {
+                for field in fields {
+                    helper(typeck, true, field.expr, f)?;
+                }
+                if let Some(default) = default {
+                    helper(typeck, false, default, f)?;
+                }
+            },
+            ExprKind::If(cond, then, else_expr) => {
+                helper(typeck, true, cond, f)?;
+                helper(typeck, true, then, f)?;
+                if let Some(else_expr) = else_expr {
+                    helper(typeck, true, else_expr, f)?;
+                }
+            },
+            ExprKind::Type(e, _) => {
+                helper(typeck, consume, e, f)?;
+            },
+
+            // Either drops temporaries, jumps out of the current expression, or has no sub expression.
+            ExprKind::DropTemps(_)
+            | ExprKind::Ret(_)
+            | ExprKind::Break(..)
+            | ExprKind::Yield(..)
+            | ExprKind::Block(..)
+            | ExprKind::Loop(..)
+            | ExprKind::Repeat(..)
+            | ExprKind::Lit(_)
+            | ExprKind::ConstBlock(_)
+            | ExprKind::Closure { .. }
+            | ExprKind::Path(_)
+            | ExprKind::Continue(_)
+            | ExprKind::InlineAsm(_)
+            | ExprKind::Err => (),
+        }
+        ControlFlow::Continue(())
+    }
+    helper(cx.typeck_results(), true, e, &mut f)
+}
+
+pub fn any_temporaries_need_ordered_drop<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
+    for_each_unconsumed_temporary(cx, e, |ty| {
+        if needs_ordered_drop(cx, ty) {
+            ControlFlow::Break(())
+        } else {
+            ControlFlow::Continue(())
+        }
+    })
+    .is_break()
+}
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index 6ad56aacf8c..6cc6d5036b3 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2022-06-16"
+channel = "nightly-2022-06-30"
 components = ["cargo", "llvm-tools-preview", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
diff --git a/src/tools/clippy/src/driver.rs b/src/tools/clippy/src/driver.rs
index 67467f89b47..96d542cfe10 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -60,6 +60,7 @@ fn test_arg_value() {
     assert_eq!(arg_value(args, "--bar", |p| p == "foo"), None);
     assert_eq!(arg_value(args, "--foobar", |p| p == "foo"), None);
     assert_eq!(arg_value(args, "--foobar", |p| p == "123"), Some("123"));
+    assert_eq!(arg_value(args, "--foobar", |p| p.contains("12")), Some("123"));
     assert_eq!(arg_value(args, "--foo", |_| true), None);
 }
 
@@ -152,7 +153,8 @@ You can use tool lints to allow or deny lints from your code, eg.:
 
 const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust-clippy/issues/new";
 
-static ICE_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> = LazyLock::new(|| {
+type PanicCallback = dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static;
+static ICE_HOOK: LazyLock<Box<PanicCallback>> = LazyLock::new(|| {
     let hook = panic::take_hook();
     panic::set_hook(Box::new(|info| report_clippy_ice(info, BUG_REPORT_URL)));
     hook
@@ -334,15 +336,13 @@ pub fn main() {
         // - IF Clippy is run on the main crate, not on deps (`!cap_lints_allow`) THEN
         //    - IF `--no-deps` is not set (`!no_deps`) OR
         //    - IF `--no-deps` is set and Clippy is run on the specified primary package
-        let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some();
+        let cap_lints_allow = arg_value(&orig_args, "--cap-lints", |val| val == "allow").is_some()
+            && arg_value(&orig_args, "--force-warn", |val| val.contains("clippy::")).is_none();
         let in_primary_package = env::var("CARGO_PRIMARY_PACKAGE").is_ok();
 
         let clippy_enabled = !cap_lints_allow && (!no_deps || in_primary_package);
         if clippy_enabled {
             args.extend(clippy_args);
-        }
-
-        if clippy_enabled {
             rustc_driver::RunCompiler::new(&args, &mut ClippyCallbacks { clippy_args_var }).run()
         } else {
             rustc_driver::RunCompiler::new(&args, &mut RustcCallbacks { clippy_args_var }).run()
diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs
index 061cda7e01e..bf7a39edf4c 100644
--- a/src/tools/clippy/tests/compile-test.rs
+++ b/src/tools/clippy/tests/compile-test.rs
@@ -23,6 +23,7 @@ const RUN_INTERNAL_TESTS: bool = cfg!(feature = "internal");
 
 /// All crates used in UI tests are listed here
 static TEST_DEPENDENCIES: &[&str] = &[
+    "clippy_lints",
     "clippy_utils",
     "derive_new",
     "futures",
@@ -41,6 +42,8 @@ static TEST_DEPENDENCIES: &[&str] = &[
 // Test dependencies may need an `extern crate` here to ensure that they show up
 // in the depinfo file (otherwise cargo thinks they are unused)
 #[allow(unused_extern_crates)]
+extern crate clippy_lints;
+#[allow(unused_extern_crates)]
 extern crate clippy_utils;
 #[allow(unused_extern_crates)]
 extern crate derive_new;
@@ -124,7 +127,7 @@ fn base_config(test_dir: &str) -> compiletest::Config {
     let mut config = compiletest::Config {
         edition: Some("2021".into()),
         mode: TestMode::Ui,
-        ..compiletest::Config::default()
+        ..Default::default()
     };
 
     if let Ok(filters) = env::var("TESTNAME") {
@@ -280,6 +283,24 @@ fn run_ui_cargo() {
                 }
 
                 env::set_current_dir(&src_path)?;
+
+                let cargo_toml_path = case.path().join("Cargo.toml");
+                let cargo_content = fs::read(&cargo_toml_path)?;
+                let cargo_parsed: toml::Value = toml::from_str(
+                    std::str::from_utf8(&cargo_content).expect("`Cargo.toml` is not a valid utf-8 file!"),
+                )
+                .expect("Can't parse `Cargo.toml`");
+
+                let _g = VarGuard::set("CARGO_MANIFEST_DIR", case.path());
+                let _h = VarGuard::set(
+                    "CARGO_PKG_RUST_VERSION",
+                    cargo_parsed
+                        .get("package")
+                        .and_then(|p| p.get("rust-version"))
+                        .and_then(toml::Value::as_str)
+                        .unwrap_or(""),
+                );
+
                 for file in fs::read_dir(&src_path)? {
                     let file = file?;
                     if file.file_type()?.is_dir() {
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/Cargo.toml
new file mode 100644
index 00000000000..73ec29c5803
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "fail-both-diff"
+version = "0.1.0"
+rust-version = "1.56"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/clippy.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/clippy.toml
new file mode 100644
index 00000000000..abe19b3a007
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/clippy.toml
@@ -0,0 +1 @@
+msrv = "1.59"
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.rs
new file mode 100644
index 00000000000..5b91d550867
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.rs
@@ -0,0 +1,11 @@
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+    pub fn bar() -> Foo {
+        Foo
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr
new file mode 100644
index 00000000000..9a7d802dc6d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_diff/src/main.stderr
@@ -0,0 +1,16 @@
+warning: the MSRV in `clippy.toml` and `Cargo.toml` differ; using `1.59.0` from `clippy.toml`
+
+error: unnecessary structure name repetition
+  --> $DIR/main.rs:6:21
+   |
+LL |     pub fn bar() -> Foo {
+   |                     ^^^ help: use the applicable keyword: `Self`
+   |
+note: the lint level is defined here
+  --> $DIR/main.rs:1:9
+   |
+LL | #![deny(clippy::use_self)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error; 1 warning emitted
+
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/Cargo.toml
new file mode 100644
index 00000000000..2d6d547e4fe
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "fail-both-same"
+version = "0.1.0"
+rust-version = "1.57.0"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/clippy.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/clippy.toml
new file mode 100644
index 00000000000..5cccb362c14
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/clippy.toml
@@ -0,0 +1 @@
+msrv = "1.57"
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.rs
new file mode 100644
index 00000000000..5b91d550867
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.rs
@@ -0,0 +1,11 @@
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+    pub fn bar() -> Foo {
+        Foo
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr
new file mode 100644
index 00000000000..a280e1bacdf
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_both_same/src/main.stderr
@@ -0,0 +1,14 @@
+error: unnecessary structure name repetition
+  --> $DIR/main.rs:6:21
+   |
+LL |     pub fn bar() -> Foo {
+   |                     ^^^ help: use the applicable keyword: `Self`
+   |
+note: the lint level is defined here
+  --> $DIR/main.rs:1:9
+   |
+LL | #![deny(clippy::use_self)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/Cargo.toml
new file mode 100644
index 00000000000..36a53bd829d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "fail-cargo"
+version = "0.1.0"
+rust-version = "1.56.1"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.rs
new file mode 100644
index 00000000000..5b91d550867
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.rs
@@ -0,0 +1,11 @@
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+    pub fn bar() -> Foo {
+        Foo
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr
new file mode 100644
index 00000000000..a280e1bacdf
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_cargo/src/main.stderr
@@ -0,0 +1,14 @@
+error: unnecessary structure name repetition
+  --> $DIR/main.rs:6:21
+   |
+LL |     pub fn bar() -> Foo {
+   |                     ^^^ help: use the applicable keyword: `Self`
+   |
+note: the lint level is defined here
+  --> $DIR/main.rs:1:9
+   |
+LL | #![deny(clippy::use_self)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/Cargo.toml
new file mode 100644
index 00000000000..9f644a1a39a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "fail-clippy"
+version = "0.1.0"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/clippy.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/clippy.toml
new file mode 100644
index 00000000000..ddbdbc1fa25
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/clippy.toml
@@ -0,0 +1 @@
+msrv = "1.58"
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.rs
new file mode 100644
index 00000000000..5b91d550867
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.rs
@@ -0,0 +1,11 @@
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+    pub fn bar() -> Foo {
+        Foo
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr
new file mode 100644
index 00000000000..a280e1bacdf
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_clippy/src/main.stderr
@@ -0,0 +1,14 @@
+error: unnecessary structure name repetition
+  --> $DIR/main.rs:6:21
+   |
+LL |     pub fn bar() -> Foo {
+   |                     ^^^ help: use the applicable keyword: `Self`
+   |
+note: the lint level is defined here
+  --> $DIR/main.rs:1:9
+   |
+LL | #![deny(clippy::use_self)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.toml
new file mode 100644
index 00000000000..5380e993b29
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "fail-file-attr"
+version = "0.1.0"
+rust-version = "1.13"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/clippy.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/clippy.toml
new file mode 100644
index 00000000000..ea5d806594b
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/clippy.toml
@@ -0,0 +1 @@
+msrv = "1.13.0"
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.rs
new file mode 100644
index 00000000000..bcbffa82a54
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.rs
@@ -0,0 +1,16 @@
+// FIXME: this should produce a warning, because the attribute says 1.58 and the cargo.toml file
+// says 1.13
+
+#![feature(custom_inner_attributes)]
+#![clippy::msrv = "1.58.0"]
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+    pub fn bar() -> Foo {
+        Foo
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr
new file mode 100644
index 00000000000..88f6e00922b
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/fail_file_attr/src/main.stderr
@@ -0,0 +1,14 @@
+error: unnecessary structure name repetition
+  --> $DIR/main.rs:11:21
+   |
+LL |     pub fn bar() -> Foo {
+   |                     ^^^ help: use the applicable keyword: `Self`
+   |
+note: the lint level is defined here
+  --> $DIR/main.rs:6:9
+   |
+LL | #![deny(clippy::use_self)]
+   |         ^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/Cargo.toml
new file mode 100644
index 00000000000..1f9bd8f9a84
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "fail-both-same"
+version = "0.1.0"
+rust-version = "1.13.0"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/clippy.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/clippy.toml
new file mode 100644
index 00000000000..5e8e48b636b
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/clippy.toml
@@ -0,0 +1 @@
+msrv = "1.13"
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/src/main.rs
new file mode 100644
index 00000000000..5b91d550867
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_both_same/src/main.rs
@@ -0,0 +1,11 @@
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+    pub fn bar() -> Foo {
+        Foo
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_cargo/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_cargo/Cargo.toml
new file mode 100644
index 00000000000..77538027c0f
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_cargo/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "fail-cargo"
+version = "0.1.0"
+rust-version = "1.13.0"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_cargo/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_cargo/src/main.rs
new file mode 100644
index 00000000000..5b91d550867
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_cargo/src/main.rs
@@ -0,0 +1,11 @@
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+    pub fn bar() -> Foo {
+        Foo
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/Cargo.toml
new file mode 100644
index 00000000000..9f644a1a39a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/Cargo.toml
@@ -0,0 +1,7 @@
+[package]
+name = "fail-clippy"
+version = "0.1.0"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/clippy.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/clippy.toml
new file mode 100644
index 00000000000..5e8e48b636b
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/clippy.toml
@@ -0,0 +1 @@
+msrv = "1.13"
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/src/main.rs
new file mode 100644
index 00000000000..5b91d550867
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_clippy/src/main.rs
@@ -0,0 +1,11 @@
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+    pub fn bar() -> Foo {
+        Foo
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_file_attr/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_file_attr/Cargo.toml
new file mode 100644
index 00000000000..f0387cd90b8
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_file_attr/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "fail-file-attr"
+version = "0.1.0"
+rust-version = "1.59"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_file_attr/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_file_attr/src/main.rs
new file mode 100644
index 00000000000..27fe4771d2d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/pass_file_attr/src/main.rs
@@ -0,0 +1,13 @@
+#![feature(custom_inner_attributes)]
+#![clippy::msrv = "1.13.0"]
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+    pub fn bar() -> Foo {
+        Foo
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/Cargo.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/Cargo.toml
new file mode 100644
index 00000000000..a19d5b33fe5
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/Cargo.toml
@@ -0,0 +1,8 @@
+[package]
+name = "warn-both-diff"
+version = "0.1.0"
+rust-version = "1.56.0"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/clippy.toml b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/clippy.toml
new file mode 100644
index 00000000000..5e8e48b636b
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/clippy.toml
@@ -0,0 +1 @@
+msrv = "1.13"
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/src/main.rs b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/src/main.rs
new file mode 100644
index 00000000000..5b91d550867
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/src/main.rs
@@ -0,0 +1,11 @@
+#![deny(clippy::use_self)]
+
+pub struct Foo;
+
+impl Foo {
+    pub fn bar() -> Foo {
+        Foo
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/src/main.stderr b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/src/main.stderr
new file mode 100644
index 00000000000..eeae5b7b275
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/cargo_rust_version/warn_both_diff/src/main.stderr
@@ -0,0 +1,4 @@
+warning: the MSRV in `clippy.toml` and `Cargo.toml` differ; using `1.13.0` from `clippy.toml`
+
+warning: 1 warning emitted
+
diff --git a/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr b/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr
index 2abb4e3e06e..98697e001f9 100644
--- a/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr
+++ b/src/tools/clippy/tests/ui-cargo/multiple_config_files/warn/src/main.stderr
@@ -1,2 +1,2 @@
-Using config file `$SRC_DIR/tests/ui-cargo/multiple_config_files/warn/.clippy.toml`
-Warning: `$SRC_DIR/tests/ui-cargo/multiple_config_files/warn/clippy.toml` will be ignored.
+Using config file `$SRC_DIR/.clippy.toml`
+Warning: `$SRC_DIR/clippy.toml` will be ignored.
diff --git a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed
index a5a6f20ddd5..9f299d7dec7 100644
--- a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed
+++ b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.fixed
@@ -45,7 +45,12 @@ impl EarlyLintPass for Pass {
             if predicate {
                 db.note(note_msg);
             }
-        })
+        });
+
+        // Issue #8798
+        span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
+            db.help(help_msg).help(help_msg);
+        });
     }
 }
 
diff --git a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs
index 6d783aa0786..2b113f555e4 100644
--- a/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs
+++ b/src/tools/clippy/tests/ui-internal/collapsible_span_lint_calls.rs
@@ -55,7 +55,12 @@ impl EarlyLintPass for Pass {
             if predicate {
                 db.note(note_msg);
             }
-        })
+        });
+
+        // Issue #8798
+        span_lint_and_then(cx, TEST_LINT, expr.span, lint_msg, |db| {
+            db.help(help_msg).help(help_msg);
+        });
     }
 }
 
diff --git a/src/tools/clippy/tests/ui-internal/default_deprecation_reason.rs b/src/tools/clippy/tests/ui-internal/default_deprecation_reason.rs
new file mode 100644
index 00000000000..c8961d5e1f0
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/default_deprecation_reason.rs
@@ -0,0 +1,30 @@
+#![deny(clippy::internal)]
+#![feature(rustc_private)]
+
+#[macro_use]
+extern crate clippy_lints;
+use clippy_lints::deprecated_lints::ClippyDeprecatedLint;
+
+declare_deprecated_lint! {
+    /// ### What it does
+    /// Nothing. This lint has been deprecated.
+    ///
+    /// ### Deprecation reason
+    /// TODO
+    #[clippy::version = "1.63.0"]
+    pub COOL_LINT_DEFAULT,
+    "default deprecation note"
+}
+
+declare_deprecated_lint! {
+    /// ### What it does
+    /// Nothing. This lint has been deprecated.
+    ///
+    /// ### Deprecation reason
+    /// This lint has been replaced by `cooler_lint`
+    #[clippy::version = "1.63.0"]
+    pub COOL_LINT,
+    "this lint has been replaced by `cooler_lint`"
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-internal/default_deprecation_reason.stderr b/src/tools/clippy/tests/ui-internal/default_deprecation_reason.stderr
new file mode 100644
index 00000000000..ca26b649f98
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/default_deprecation_reason.stderr
@@ -0,0 +1,22 @@
+error: the lint `COOL_LINT_DEFAULT` has the default deprecation reason
+  --> $DIR/default_deprecation_reason.rs:8:1
+   |
+LL | / declare_deprecated_lint! {
+LL | |     /// ### What it does
+LL | |     /// Nothing. This lint has been deprecated.
+LL | |     ///
+...  |
+LL | |     "default deprecation note"
+LL | | }
+   | |_^
+   |
+note: the lint level is defined here
+  --> $DIR/default_deprecation_reason.rs:1:9
+   |
+LL | #![deny(clippy::internal)]
+   |         ^^^^^^^^^^^^^^^^
+   = note: `#[deny(clippy::default_deprecation_reason)]` implied by `#[deny(clippy::internal)]`
+   = note: this error originates in the macro `declare_deprecated_lint` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to previous error
+
diff --git a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
index 9f283337c7e..5bd2c2799f0 100644
--- a/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/macro_rules.rs
@@ -127,3 +127,11 @@ macro_rules! ptr_as_ptr_cast {
         $ptr as *const i32
     };
 }
+
+#[macro_export]
+macro_rules! manual_rem_euclid {
+    () => {
+        let value: i32 = 5;
+        let _: i32 = ((value % 4) + 4) % 4;
+    };
+}
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
index ed7b17651e6..a89a06308d0 100644
--- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
@@ -72,3 +72,17 @@ pub fn mini_macro(_: TokenStream) -> TokenStream {
         }
     )
 }
+
+#[proc_macro_derive(ExtraLifetimeDerive)]
+#[allow(unused)]
+pub fn extra_lifetime(_input: TokenStream) -> TokenStream {
+    quote!(
+        pub struct ExtraLifetime;
+
+        impl<'b> ExtraLifetime {
+            pub fn something<'c>() -> Self {
+                Self
+            }
+        }
+    )
+}
diff --git a/src/tools/clippy/tests/ui/escape_analysis.rs b/src/tools/clippy/tests/ui/boxed_local.rs
index 13e2b6c7a2e..4639f00a8d8 100644
--- a/src/tools/clippy/tests/ui/escape_analysis.rs
+++ b/src/tools/clippy/tests/ui/boxed_local.rs
@@ -1,4 +1,5 @@
 #![feature(box_syntax)]
+#![feature(lint_reasons)]
 #![allow(
     clippy::borrowed_box,
     clippy::needless_pass_by_value,
@@ -202,3 +203,7 @@ mod issue4804 {
         fn foo(x: Box<u32>) {}
     }
 }
+
+fn check_expect(#[expect(clippy::boxed_local)] x: Box<A>) {
+    x.foo();
+}
diff --git a/src/tools/clippy/tests/ui/escape_analysis.stderr b/src/tools/clippy/tests/ui/boxed_local.stderr
index 4a82b4419f9..9036529f39c 100644
--- a/src/tools/clippy/tests/ui/escape_analysis.stderr
+++ b/src/tools/clippy/tests/ui/boxed_local.stderr
@@ -1,5 +1,5 @@
 error: local variable doesn't need to be boxed here
-  --> $DIR/escape_analysis.rs:40:13
+  --> $DIR/boxed_local.rs:41:13
    |
 LL | fn warn_arg(x: Box<A>) {
    |             ^
@@ -7,19 +7,19 @@ LL | fn warn_arg(x: Box<A>) {
    = note: `-D clippy::boxed-local` implied by `-D warnings`
 
 error: local variable doesn't need to be boxed here
-  --> $DIR/escape_analysis.rs:131:12
+  --> $DIR/boxed_local.rs:132:12
    |
 LL | pub fn new(_needs_name: Box<PeekableSeekable<&()>>) -> () {}
    |            ^^^^^^^^^^^
 
 error: local variable doesn't need to be boxed here
-  --> $DIR/escape_analysis.rs:195:44
+  --> $DIR/boxed_local.rs:196:44
    |
 LL |         fn default_impl_x(self: Box<Self>, x: Box<u32>) -> u32 {
    |                                            ^
 
 error: local variable doesn't need to be boxed here
-  --> $DIR/escape_analysis.rs:202:16
+  --> $DIR/boxed_local.rs:203:16
    |
 LL |         fn foo(x: Box<u32>) {}
    |                ^
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs
index ee3fdfabe9d..82dce81979f 100644
--- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs
+++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.rs
@@ -1,3 +1,4 @@
+#![feature(lint_reasons)]
 #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
 #![allow(clippy::if_same_then_else, clippy::branches_sharing_code)]
 
@@ -84,3 +85,18 @@ fn main() {
 
     assert!(x.is_ok(), "{:?}", x.unwrap_err()); // ok, it's a common test pattern
 }
+
+fn check_expect() {
+    let x = Some(());
+    if x.is_some() {
+        #[expect(clippy::unnecessary_unwrap)]
+        x.unwrap(); // unnecessary
+        #[expect(clippy::unnecessary_unwrap)]
+        x.expect("an error message"); // unnecessary
+    } else {
+        #[expect(clippy::panicking_unwrap)]
+        x.unwrap(); // will panic
+        #[expect(clippy::panicking_unwrap)]
+        x.expect("an error message"); // will panic
+    }
+}
diff --git a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
index 34131592802..ef688274222 100644
--- a/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
+++ b/src/tools/clippy/tests/ui/checked_unwrap/simple_conditionals.stderr
@@ -1,5 +1,5 @@
 error: called `unwrap` on `x` after checking its variant with `is_some`
-  --> $DIR/simple_conditionals.rs:39:9
+  --> $DIR/simple_conditionals.rs:40:9
    |
 LL |     if x.is_some() {
    |     -------------- help: try: `if let Some(..) = x`
@@ -7,13 +7,13 @@ LL |         x.unwrap(); // unnecessary
    |         ^^^^^^^^^^
    |
 note: the lint level is defined here
-  --> $DIR/simple_conditionals.rs:1:35
+  --> $DIR/simple_conditionals.rs:2:35
    |
 LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: called `expect` on `x` after checking its variant with `is_some`
-  --> $DIR/simple_conditionals.rs:40:9
+  --> $DIR/simple_conditionals.rs:41:9
    |
 LL |     if x.is_some() {
    |     -------------- help: try: `if let Some(..) = x`
@@ -22,7 +22,7 @@ LL |         x.expect("an error message"); // unnecessary
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this call to `unwrap()` will always panic
-  --> $DIR/simple_conditionals.rs:42:9
+  --> $DIR/simple_conditionals.rs:43:9
    |
 LL |     if x.is_some() {
    |        ----------- because of this check
@@ -31,13 +31,13 @@ LL |         x.unwrap(); // will panic
    |         ^^^^^^^^^^
    |
 note: the lint level is defined here
-  --> $DIR/simple_conditionals.rs:1:9
+  --> $DIR/simple_conditionals.rs:2:9
    |
 LL | #![deny(clippy::panicking_unwrap, clippy::unnecessary_unwrap)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this call to `expect()` will always panic
-  --> $DIR/simple_conditionals.rs:43:9
+  --> $DIR/simple_conditionals.rs:44:9
    |
 LL |     if x.is_some() {
    |        ----------- because of this check
@@ -46,7 +46,7 @@ LL |         x.expect("an error message"); // will panic
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this call to `unwrap()` will always panic
-  --> $DIR/simple_conditionals.rs:46:9
+  --> $DIR/simple_conditionals.rs:47:9
    |
 LL |     if x.is_none() {
    |        ----------- because of this check
@@ -54,7 +54,7 @@ LL |         x.unwrap(); // will panic
    |         ^^^^^^^^^^
 
 error: called `unwrap` on `x` after checking its variant with `is_none`
-  --> $DIR/simple_conditionals.rs:48:9
+  --> $DIR/simple_conditionals.rs:49:9
    |
 LL |     if x.is_none() {
    |     -------------- help: try: `if let Some(..) = x`
@@ -63,7 +63,7 @@ LL |         x.unwrap(); // unnecessary
    |         ^^^^^^^^^^
 
 error: called `unwrap` on `x` after checking its variant with `is_some`
-  --> $DIR/simple_conditionals.rs:7:13
+  --> $DIR/simple_conditionals.rs:8:13
    |
 LL |         if $a.is_some() {
    |         --------------- help: try: `if let Some(..) = x`
@@ -76,7 +76,7 @@ LL |     m!(x);
    = note: this error originates in the macro `m` (in Nightly builds, run with -Z macro-backtrace for more info)
 
 error: called `unwrap` on `x` after checking its variant with `is_ok`
-  --> $DIR/simple_conditionals.rs:56:9
+  --> $DIR/simple_conditionals.rs:57:9
    |
 LL |     if x.is_ok() {
    |     ------------ help: try: `if let Ok(..) = x`
@@ -84,7 +84,7 @@ LL |         x.unwrap(); // unnecessary
    |         ^^^^^^^^^^
 
 error: called `expect` on `x` after checking its variant with `is_ok`
-  --> $DIR/simple_conditionals.rs:57:9
+  --> $DIR/simple_conditionals.rs:58:9
    |
 LL |     if x.is_ok() {
    |     ------------ help: try: `if let Ok(..) = x`
@@ -93,7 +93,7 @@ LL |         x.expect("an error message"); // unnecessary
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: this call to `unwrap_err()` will always panic
-  --> $DIR/simple_conditionals.rs:58:9
+  --> $DIR/simple_conditionals.rs:59:9
    |
 LL |     if x.is_ok() {
    |        --------- because of this check
@@ -102,7 +102,7 @@ LL |         x.unwrap_err(); // will panic
    |         ^^^^^^^^^^^^^^
 
 error: this call to `unwrap()` will always panic
-  --> $DIR/simple_conditionals.rs:60:9
+  --> $DIR/simple_conditionals.rs:61:9
    |
 LL |     if x.is_ok() {
    |        --------- because of this check
@@ -111,7 +111,7 @@ LL |         x.unwrap(); // will panic
    |         ^^^^^^^^^^
 
 error: this call to `expect()` will always panic
-  --> $DIR/simple_conditionals.rs:61:9
+  --> $DIR/simple_conditionals.rs:62:9
    |
 LL |     if x.is_ok() {
    |        --------- because of this check
@@ -120,7 +120,7 @@ LL |         x.expect("an error message"); // will panic
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: called `unwrap_err` on `x` after checking its variant with `is_ok`
-  --> $DIR/simple_conditionals.rs:62:9
+  --> $DIR/simple_conditionals.rs:63:9
    |
 LL |     if x.is_ok() {
    |     ------------ help: try: `if let Err(..) = x`
@@ -129,7 +129,7 @@ LL |         x.unwrap_err(); // unnecessary
    |         ^^^^^^^^^^^^^^
 
 error: this call to `unwrap()` will always panic
-  --> $DIR/simple_conditionals.rs:65:9
+  --> $DIR/simple_conditionals.rs:66:9
    |
 LL |     if x.is_err() {
    |        ---------- because of this check
@@ -137,7 +137,7 @@ LL |         x.unwrap(); // will panic
    |         ^^^^^^^^^^
 
 error: called `unwrap_err` on `x` after checking its variant with `is_err`
-  --> $DIR/simple_conditionals.rs:66:9
+  --> $DIR/simple_conditionals.rs:67:9
    |
 LL |     if x.is_err() {
    |     ------------- help: try: `if let Err(..) = x`
@@ -146,7 +146,7 @@ LL |         x.unwrap_err(); // unnecessary
    |         ^^^^^^^^^^^^^^
 
 error: called `unwrap` on `x` after checking its variant with `is_err`
-  --> $DIR/simple_conditionals.rs:68:9
+  --> $DIR/simple_conditionals.rs:69:9
    |
 LL |     if x.is_err() {
    |     ------------- help: try: `if let Ok(..) = x`
@@ -155,7 +155,7 @@ LL |         x.unwrap(); // unnecessary
    |         ^^^^^^^^^^
 
 error: this call to `unwrap_err()` will always panic
-  --> $DIR/simple_conditionals.rs:69:9
+  --> $DIR/simple_conditionals.rs:70:9
    |
 LL |     if x.is_err() {
    |        ---------- because of this check
diff --git a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs
index 48c5e9537d6..62af545db50 100644
--- a/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs
+++ b/src/tools/clippy/tests/ui/declare_interior_mutable_const/others.rs
@@ -31,4 +31,9 @@ const NO_ANN: &dyn Display = &70;
 static STATIC_TUPLE: (AtomicUsize, String) = (ATOMIC, STRING);
 //^ there should be no lints on this line
 
+// issue #8493
+thread_local! {
+    static THREAD_LOCAL: Cell<i32> = const { Cell::new(0) };
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/default_instead_of_iter_empty.fixed b/src/tools/clippy/tests/ui/default_instead_of_iter_empty.fixed
new file mode 100644
index 00000000000..f1abfdcd6ce
--- /dev/null
+++ b/src/tools/clippy/tests/ui/default_instead_of_iter_empty.fixed
@@ -0,0 +1,21 @@
+// run-rustfix
+#![warn(clippy::default_instead_of_iter_empty)]
+#![allow(dead_code)]
+use std::collections::HashMap;
+
+#[derive(Default)]
+struct Iter {
+    iter: std::iter::Empty<usize>,
+}
+
+fn main() {
+    // Do lint.
+    let _ = std::iter::empty::<usize>();
+    let _ = std::iter::empty::<HashMap<usize, usize>>();
+    let _foo: std::iter::Empty<usize> = std::iter::empty();
+
+    // Do not lint.
+    let _ = Vec::<usize>::default();
+    let _ = String::default();
+    let _ = Iter::default();
+}
diff --git a/src/tools/clippy/tests/ui/default_instead_of_iter_empty.rs b/src/tools/clippy/tests/ui/default_instead_of_iter_empty.rs
new file mode 100644
index 00000000000..2630519c46d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/default_instead_of_iter_empty.rs
@@ -0,0 +1,21 @@
+// run-rustfix
+#![warn(clippy::default_instead_of_iter_empty)]
+#![allow(dead_code)]
+use std::collections::HashMap;
+
+#[derive(Default)]
+struct Iter {
+    iter: std::iter::Empty<usize>,
+}
+
+fn main() {
+    // Do lint.
+    let _ = std::iter::Empty::<usize>::default();
+    let _ = std::iter::Empty::<HashMap<usize, usize>>::default();
+    let _foo: std::iter::Empty<usize> = std::iter::Empty::default();
+
+    // Do not lint.
+    let _ = Vec::<usize>::default();
+    let _ = String::default();
+    let _ = Iter::default();
+}
diff --git a/src/tools/clippy/tests/ui/default_instead_of_iter_empty.stderr b/src/tools/clippy/tests/ui/default_instead_of_iter_empty.stderr
new file mode 100644
index 00000000000..460fc84def8
--- /dev/null
+++ b/src/tools/clippy/tests/ui/default_instead_of_iter_empty.stderr
@@ -0,0 +1,22 @@
+error: `std::iter::empty()` is the more idiomatic way
+  --> $DIR/default_instead_of_iter_empty.rs:13:13
+   |
+LL |     let _ = std::iter::Empty::<usize>::default();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::iter::empty::<usize>()`
+   |
+   = note: `-D clippy::default-instead-of-iter-empty` implied by `-D warnings`
+
+error: `std::iter::empty()` is the more idiomatic way
+  --> $DIR/default_instead_of_iter_empty.rs:14:13
+   |
+LL |     let _ = std::iter::Empty::<HashMap<usize, usize>>::default();
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::iter::empty::<HashMap<usize, usize>>()`
+
+error: `std::iter::empty()` is the more idiomatic way
+  --> $DIR/default_instead_of_iter_empty.rs:15:41
+   |
+LL |     let _foo: std::iter::Empty<usize> = std::iter::Empty::default();
+   |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::iter::empty()`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/enum_variants.rs b/src/tools/clippy/tests/ui/enum_variants.rs
index b2bf7c4e360..efed12ee2ef 100644
--- a/src/tools/clippy/tests/ui/enum_variants.rs
+++ b/src/tools/clippy/tests/ui/enum_variants.rs
@@ -158,4 +158,25 @@ enum Phase {
     PostLookup,
 }
 
+mod issue9018 {
+    enum DoLint {
+        _TypeCreate,
+        _TypeRead,
+        _TypeUpdate,
+        _TypeDestroy,
+    }
+
+    enum DoLintToo {
+        _CreateType,
+        _UpdateType,
+        _DeleteType,
+    }
+
+    enum DoNotLint {
+        _Foo,
+        _Bar,
+        _Baz,
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/enum_variants.stderr b/src/tools/clippy/tests/ui/enum_variants.stderr
index 8a3265086e8..7342aff80f0 100644
--- a/src/tools/clippy/tests/ui/enum_variants.stderr
+++ b/src/tools/clippy/tests/ui/enum_variants.stderr
@@ -120,5 +120,30 @@ LL | | }
    |
    = help: remove the postfixes and use full paths to the variants instead of glob imports
 
-error: aborting due to 12 previous errors
+error: all variants have the same prefix: `_Type`
+  --> $DIR/enum_variants.rs:162:5
+   |
+LL | /     enum DoLint {
+LL | |         _TypeCreate,
+LL | |         _TypeRead,
+LL | |         _TypeUpdate,
+LL | |         _TypeDestroy,
+LL | |     }
+   | |_____^
+   |
+   = help: remove the prefixes and use full paths to the variants instead of glob imports
+
+error: all variants have the same postfix: `Type`
+  --> $DIR/enum_variants.rs:169:5
+   |
+LL | /     enum DoLintToo {
+LL | |         _CreateType,
+LL | |         _UpdateType,
+LL | |         _DeleteType,
+LL | |     }
+   | |_____^
+   |
+   = help: remove the postfixes and use full paths to the variants instead of glob imports
+
+error: aborting due to 14 previous errors
 
diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.fixed b/src/tools/clippy/tests/ui/explicit_auto_deref.fixed
new file mode 100644
index 00000000000..d4ff1b1566d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_auto_deref.fixed
@@ -0,0 +1,214 @@
+// run-rustfix
+
+#![warn(clippy::explicit_auto_deref)]
+#![allow(
+    dead_code,
+    unused_braces,
+    clippy::borrowed_box,
+    clippy::needless_borrow,
+    clippy::needless_return,
+    clippy::ptr_arg,
+    clippy::redundant_field_names,
+    clippy::too_many_arguments,
+    clippy::borrow_deref_ref,
+    clippy::let_unit_value
+)]
+
+trait CallableStr {
+    type T: Fn(&str);
+    fn callable_str(&self) -> Self::T;
+}
+impl CallableStr for () {
+    type T = fn(&str);
+    fn callable_str(&self) -> Self::T {
+        fn f(_: &str) {}
+        f
+    }
+}
+impl CallableStr for i32 {
+    type T = <() as CallableStr>::T;
+    fn callable_str(&self) -> Self::T {
+        ().callable_str()
+    }
+}
+
+trait CallableT<U: ?Sized> {
+    type T: Fn(&U);
+    fn callable_t(&self) -> Self::T;
+}
+impl<U: ?Sized> CallableT<U> for () {
+    type T = fn(&U);
+    fn callable_t(&self) -> Self::T {
+        fn f<U: ?Sized>(_: &U) {}
+        f::<U>
+    }
+}
+impl<U: ?Sized> CallableT<U> for i32 {
+    type T = <() as CallableT<U>>::T;
+    fn callable_t(&self) -> Self::T {
+        ().callable_t()
+    }
+}
+
+fn f_str(_: &str) {}
+fn f_string(_: &String) {}
+fn f_t<T>(_: T) {}
+fn f_ref_t<T: ?Sized>(_: &T) {}
+
+fn f_str_t<T>(_: &str, _: T) {}
+
+fn f_box_t<T>(_: &Box<T>) {}
+
+extern "C" {
+    fn var(_: u32, ...);
+}
+
+fn main() {
+    let s = String::new();
+
+    let _: &str = &s;
+    let _ = &*s; // Don't lint. Inferred type would change.
+    let _: &_ = &*s; // Don't lint. Inferred type would change.
+
+    f_str(&s);
+    f_t(&*s); // Don't lint. Inferred type would change.
+    f_ref_t(&*s); // Don't lint. Inferred type would change.
+
+    f_str_t(&s, &*s); // Don't lint second param.
+
+    let b = Box::new(Box::new(Box::new(5)));
+    let _: &Box<i32> = &b;
+    let _: &Box<_> = &**b; // Don't lint. Inferred type would change.
+
+    f_box_t(&**b); // Don't lint. Inferred type would change.
+
+    let c = |_x: &str| ();
+    c(&s);
+
+    let c = |_x| ();
+    c(&*s); // Don't lint. Inferred type would change.
+
+    fn _f(x: &String) -> &str {
+        x
+    }
+
+    fn _f1(x: &String) -> &str {
+        { x }
+    }
+
+    fn _f2(x: &String) -> &str {
+        { x }
+    }
+
+    fn _f3(x: &Box<Box<Box<i32>>>) -> &Box<i32> {
+        x
+    }
+
+    fn _f4(
+        x: String,
+        f1: impl Fn(&str),
+        f2: &dyn Fn(&str),
+        f3: fn(&str),
+        f4: impl CallableStr,
+        f5: <() as CallableStr>::T,
+        f6: <i32 as CallableStr>::T,
+        f7: &dyn CallableStr<T = fn(&str)>,
+        f8: impl CallableT<str>,
+        f9: <() as CallableT<str>>::T,
+        f10: <i32 as CallableT<str>>::T,
+        f11: &dyn CallableT<str, T = fn(&str)>,
+    ) {
+        f1(&x);
+        f2(&x);
+        f3(&x);
+        f4.callable_str()(&x);
+        f5(&x);
+        f6(&x);
+        f7.callable_str()(&x);
+        f8.callable_t()(&x);
+        f9(&x);
+        f10(&x);
+        f11.callable_t()(&x);
+    }
+
+    struct S1<'a>(&'a str);
+    let _ = S1(&s);
+
+    struct S2<'a> {
+        s: &'a str,
+    }
+    let _ = S2 { s: &s };
+
+    struct S3<'a, T: ?Sized>(&'a T);
+    let _ = S3(&*s); // Don't lint. Inferred type would change.
+
+    struct S4<'a, T: ?Sized> {
+        s: &'a T,
+    }
+    let _ = S4 { s: &*s }; // Don't lint. Inferred type would change.
+
+    enum E1<'a> {
+        S1(&'a str),
+        S2 { s: &'a str },
+    }
+    impl<'a> E1<'a> {
+        fn m1(s: &'a String) {
+            let _ = Self::S1(s);
+            let _ = Self::S2 { s: s };
+        }
+    }
+    let _ = E1::S1(&s);
+    let _ = E1::S2 { s: &s };
+
+    enum E2<'a, T: ?Sized> {
+        S1(&'a T),
+        S2 { s: &'a T },
+    }
+    let _ = E2::S1(&*s); // Don't lint. Inferred type would change.
+    let _ = E2::S2 { s: &*s }; // Don't lint. Inferred type would change.
+
+    let ref_s = &s;
+    let _: &String = &*ref_s; // Don't lint reborrow.
+    f_string(&*ref_s); // Don't lint reborrow.
+
+    struct S5 {
+        foo: u32,
+    }
+    let b = Box::new(Box::new(S5 { foo: 5 }));
+    let _ = b.foo;
+    let _ = b.foo;
+    let _ = b.foo;
+
+    struct S6 {
+        foo: S5,
+    }
+    impl core::ops::Deref for S6 {
+        type Target = S5;
+        fn deref(&self) -> &Self::Target {
+            &self.foo
+        }
+    }
+    let s6 = S6 { foo: S5 { foo: 5 } };
+    let _ = (*s6).foo; // Don't lint. `S6` also has a field named `foo`
+
+    let ref_str = &"foo";
+    let _ = f_str(ref_str);
+    let ref_ref_str = &ref_str;
+    let _ = f_str(ref_ref_str);
+
+    fn _f5(x: &u32) -> u32 {
+        if true {
+            *x
+        } else {
+            return *x;
+        }
+    }
+
+    f_str(&&ref_str); // `needless_borrow` will suggest removing both references
+    f_str(&ref_str); // `needless_borrow` will suggest removing only one reference
+
+    let x = &&40;
+    unsafe {
+        var(0, &**x);
+    }
+}
diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.rs b/src/tools/clippy/tests/ui/explicit_auto_deref.rs
new file mode 100644
index 00000000000..99294a7947b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_auto_deref.rs
@@ -0,0 +1,214 @@
+// run-rustfix
+
+#![warn(clippy::explicit_auto_deref)]
+#![allow(
+    dead_code,
+    unused_braces,
+    clippy::borrowed_box,
+    clippy::needless_borrow,
+    clippy::needless_return,
+    clippy::ptr_arg,
+    clippy::redundant_field_names,
+    clippy::too_many_arguments,
+    clippy::borrow_deref_ref,
+    clippy::let_unit_value
+)]
+
+trait CallableStr {
+    type T: Fn(&str);
+    fn callable_str(&self) -> Self::T;
+}
+impl CallableStr for () {
+    type T = fn(&str);
+    fn callable_str(&self) -> Self::T {
+        fn f(_: &str) {}
+        f
+    }
+}
+impl CallableStr for i32 {
+    type T = <() as CallableStr>::T;
+    fn callable_str(&self) -> Self::T {
+        ().callable_str()
+    }
+}
+
+trait CallableT<U: ?Sized> {
+    type T: Fn(&U);
+    fn callable_t(&self) -> Self::T;
+}
+impl<U: ?Sized> CallableT<U> for () {
+    type T = fn(&U);
+    fn callable_t(&self) -> Self::T {
+        fn f<U: ?Sized>(_: &U) {}
+        f::<U>
+    }
+}
+impl<U: ?Sized> CallableT<U> for i32 {
+    type T = <() as CallableT<U>>::T;
+    fn callable_t(&self) -> Self::T {
+        ().callable_t()
+    }
+}
+
+fn f_str(_: &str) {}
+fn f_string(_: &String) {}
+fn f_t<T>(_: T) {}
+fn f_ref_t<T: ?Sized>(_: &T) {}
+
+fn f_str_t<T>(_: &str, _: T) {}
+
+fn f_box_t<T>(_: &Box<T>) {}
+
+extern "C" {
+    fn var(_: u32, ...);
+}
+
+fn main() {
+    let s = String::new();
+
+    let _: &str = &*s;
+    let _ = &*s; // Don't lint. Inferred type would change.
+    let _: &_ = &*s; // Don't lint. Inferred type would change.
+
+    f_str(&*s);
+    f_t(&*s); // Don't lint. Inferred type would change.
+    f_ref_t(&*s); // Don't lint. Inferred type would change.
+
+    f_str_t(&*s, &*s); // Don't lint second param.
+
+    let b = Box::new(Box::new(Box::new(5)));
+    let _: &Box<i32> = &**b;
+    let _: &Box<_> = &**b; // Don't lint. Inferred type would change.
+
+    f_box_t(&**b); // Don't lint. Inferred type would change.
+
+    let c = |_x: &str| ();
+    c(&*s);
+
+    let c = |_x| ();
+    c(&*s); // Don't lint. Inferred type would change.
+
+    fn _f(x: &String) -> &str {
+        &**x
+    }
+
+    fn _f1(x: &String) -> &str {
+        { &**x }
+    }
+
+    fn _f2(x: &String) -> &str {
+        &**{ x }
+    }
+
+    fn _f3(x: &Box<Box<Box<i32>>>) -> &Box<i32> {
+        &***x
+    }
+
+    fn _f4(
+        x: String,
+        f1: impl Fn(&str),
+        f2: &dyn Fn(&str),
+        f3: fn(&str),
+        f4: impl CallableStr,
+        f5: <() as CallableStr>::T,
+        f6: <i32 as CallableStr>::T,
+        f7: &dyn CallableStr<T = fn(&str)>,
+        f8: impl CallableT<str>,
+        f9: <() as CallableT<str>>::T,
+        f10: <i32 as CallableT<str>>::T,
+        f11: &dyn CallableT<str, T = fn(&str)>,
+    ) {
+        f1(&*x);
+        f2(&*x);
+        f3(&*x);
+        f4.callable_str()(&*x);
+        f5(&*x);
+        f6(&*x);
+        f7.callable_str()(&*x);
+        f8.callable_t()(&*x);
+        f9(&*x);
+        f10(&*x);
+        f11.callable_t()(&*x);
+    }
+
+    struct S1<'a>(&'a str);
+    let _ = S1(&*s);
+
+    struct S2<'a> {
+        s: &'a str,
+    }
+    let _ = S2 { s: &*s };
+
+    struct S3<'a, T: ?Sized>(&'a T);
+    let _ = S3(&*s); // Don't lint. Inferred type would change.
+
+    struct S4<'a, T: ?Sized> {
+        s: &'a T,
+    }
+    let _ = S4 { s: &*s }; // Don't lint. Inferred type would change.
+
+    enum E1<'a> {
+        S1(&'a str),
+        S2 { s: &'a str },
+    }
+    impl<'a> E1<'a> {
+        fn m1(s: &'a String) {
+            let _ = Self::S1(&**s);
+            let _ = Self::S2 { s: &**s };
+        }
+    }
+    let _ = E1::S1(&*s);
+    let _ = E1::S2 { s: &*s };
+
+    enum E2<'a, T: ?Sized> {
+        S1(&'a T),
+        S2 { s: &'a T },
+    }
+    let _ = E2::S1(&*s); // Don't lint. Inferred type would change.
+    let _ = E2::S2 { s: &*s }; // Don't lint. Inferred type would change.
+
+    let ref_s = &s;
+    let _: &String = &*ref_s; // Don't lint reborrow.
+    f_string(&*ref_s); // Don't lint reborrow.
+
+    struct S5 {
+        foo: u32,
+    }
+    let b = Box::new(Box::new(S5 { foo: 5 }));
+    let _ = b.foo;
+    let _ = (*b).foo;
+    let _ = (**b).foo;
+
+    struct S6 {
+        foo: S5,
+    }
+    impl core::ops::Deref for S6 {
+        type Target = S5;
+        fn deref(&self) -> &Self::Target {
+            &self.foo
+        }
+    }
+    let s6 = S6 { foo: S5 { foo: 5 } };
+    let _ = (*s6).foo; // Don't lint. `S6` also has a field named `foo`
+
+    let ref_str = &"foo";
+    let _ = f_str(*ref_str);
+    let ref_ref_str = &ref_str;
+    let _ = f_str(**ref_ref_str);
+
+    fn _f5(x: &u32) -> u32 {
+        if true {
+            *x
+        } else {
+            return *x;
+        }
+    }
+
+    f_str(&&*ref_str); // `needless_borrow` will suggest removing both references
+    f_str(&&**ref_str); // `needless_borrow` will suggest removing only one reference
+
+    let x = &&40;
+    unsafe {
+        var(0, &**x);
+    }
+}
diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.stderr b/src/tools/clippy/tests/ui/explicit_auto_deref.stderr
new file mode 100644
index 00000000000..55f956e37ae
--- /dev/null
+++ b/src/tools/clippy/tests/ui/explicit_auto_deref.stderr
@@ -0,0 +1,196 @@
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:69:20
+   |
+LL |     let _: &str = &*s;
+   |                    ^^ help: try this: `s`
+   |
+   = note: `-D clippy::explicit-auto-deref` implied by `-D warnings`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:73:12
+   |
+LL |     f_str(&*s);
+   |            ^^ help: try this: `s`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:77:14
+   |
+LL |     f_str_t(&*s, &*s); // Don't lint second param.
+   |              ^^ help: try this: `s`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:80:25
+   |
+LL |     let _: &Box<i32> = &**b;
+   |                         ^^^ help: try this: `b`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:86:8
+   |
+LL |     c(&*s);
+   |        ^^ help: try this: `s`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:92:9
+   |
+LL |         &**x
+   |         ^^^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:96:11
+   |
+LL |         { &**x }
+   |           ^^^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:100:9
+   |
+LL |         &**{ x }
+   |         ^^^^^^^^ help: try this: `{ x }`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:104:9
+   |
+LL |         &***x
+   |         ^^^^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:121:13
+   |
+LL |         f1(&*x);
+   |             ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:122:13
+   |
+LL |         f2(&*x);
+   |             ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:123:13
+   |
+LL |         f3(&*x);
+   |             ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:124:28
+   |
+LL |         f4.callable_str()(&*x);
+   |                            ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:125:13
+   |
+LL |         f5(&*x);
+   |             ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:126:13
+   |
+LL |         f6(&*x);
+   |             ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:127:28
+   |
+LL |         f7.callable_str()(&*x);
+   |                            ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:128:26
+   |
+LL |         f8.callable_t()(&*x);
+   |                          ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:129:13
+   |
+LL |         f9(&*x);
+   |             ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:130:14
+   |
+LL |         f10(&*x);
+   |              ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:131:27
+   |
+LL |         f11.callable_t()(&*x);
+   |                           ^^ help: try this: `x`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:135:17
+   |
+LL |     let _ = S1(&*s);
+   |                 ^^ help: try this: `s`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:140:22
+   |
+LL |     let _ = S2 { s: &*s };
+   |                      ^^ help: try this: `s`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:156:30
+   |
+LL |             let _ = Self::S1(&**s);
+   |                              ^^^^ help: try this: `s`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:157:35
+   |
+LL |             let _ = Self::S2 { s: &**s };
+   |                                   ^^^^ help: try this: `s`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:160:21
+   |
+LL |     let _ = E1::S1(&*s);
+   |                     ^^ help: try this: `s`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:161:26
+   |
+LL |     let _ = E1::S2 { s: &*s };
+   |                          ^^ help: try this: `s`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:179:13
+   |
+LL |     let _ = (*b).foo;
+   |             ^^^^ help: try this: `b`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:180:13
+   |
+LL |     let _ = (**b).foo;
+   |             ^^^^^ help: try this: `b`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:195:19
+   |
+LL |     let _ = f_str(*ref_str);
+   |                   ^^^^^^^^ help: try this: `ref_str`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:197:19
+   |
+LL |     let _ = f_str(**ref_ref_str);
+   |                   ^^^^^^^^^^^^^ help: try this: `ref_ref_str`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:207:13
+   |
+LL |     f_str(&&*ref_str); // `needless_borrow` will suggest removing both references
+   |             ^^^^^^^^ help: try this: `ref_str`
+
+error: deref which would be done by auto-deref
+  --> $DIR/explicit_auto_deref.rs:208:12
+   |
+LL |     f_str(&&**ref_str); // `needless_borrow` will suggest removing only one reference
+   |            ^^^^^^^^^^ help: try this: `ref_str`
+
+error: aborting due to 32 previous errors
+
diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.fixed b/src/tools/clippy/tests/ui/explicit_deref_methods.fixed
index 92f27e68549..523cae183ee 100644
--- a/src/tools/clippy/tests/ui/explicit_deref_methods.fixed
+++ b/src/tools/clippy/tests/ui/explicit_deref_methods.fixed
@@ -4,7 +4,8 @@
     unused_variables,
     clippy::clone_double_ref,
     clippy::needless_borrow,
-    clippy::borrow_deref_ref
+    clippy::borrow_deref_ref,
+    clippy::explicit_auto_deref
 )]
 #![warn(clippy::explicit_deref_methods)]
 
diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.rs b/src/tools/clippy/tests/ui/explicit_deref_methods.rs
index d118607f992..0bbc1ae57cd 100644
--- a/src/tools/clippy/tests/ui/explicit_deref_methods.rs
+++ b/src/tools/clippy/tests/ui/explicit_deref_methods.rs
@@ -4,7 +4,8 @@
     unused_variables,
     clippy::clone_double_ref,
     clippy::needless_borrow,
-    clippy::borrow_deref_ref
+    clippy::borrow_deref_ref,
+    clippy::explicit_auto_deref
 )]
 #![warn(clippy::explicit_deref_methods)]
 
diff --git a/src/tools/clippy/tests/ui/explicit_deref_methods.stderr b/src/tools/clippy/tests/ui/explicit_deref_methods.stderr
index 8e8b358972b..4b10ed1377b 100644
--- a/src/tools/clippy/tests/ui/explicit_deref_methods.stderr
+++ b/src/tools/clippy/tests/ui/explicit_deref_methods.stderr
@@ -1,5 +1,5 @@
 error: explicit `deref` method call
-  --> $DIR/explicit_deref_methods.rs:35:19
+  --> $DIR/explicit_deref_methods.rs:36:19
    |
 LL |     let b: &str = a.deref();
    |                   ^^^^^^^^^ help: try this: `&*a`
@@ -7,67 +7,67 @@ LL |     let b: &str = a.deref();
    = note: `-D clippy::explicit-deref-methods` implied by `-D warnings`
 
 error: explicit `deref_mut` method call
-  --> $DIR/explicit_deref_methods.rs:37:23
+  --> $DIR/explicit_deref_methods.rs:38:23
    |
 LL |     let b: &mut str = a.deref_mut();
    |                       ^^^^^^^^^^^^^ help: try this: `&mut **a`
 
 error: explicit `deref` method call
-  --> $DIR/explicit_deref_methods.rs:40:39
+  --> $DIR/explicit_deref_methods.rs:41:39
    |
 LL |     let b: String = format!("{}, {}", a.deref(), a.deref());
    |                                       ^^^^^^^^^ help: try this: `&*a`
 
 error: explicit `deref` method call
-  --> $DIR/explicit_deref_methods.rs:40:50
+  --> $DIR/explicit_deref_methods.rs:41:50
    |
 LL |     let b: String = format!("{}, {}", a.deref(), a.deref());
    |                                                  ^^^^^^^^^ help: try this: `&*a`
 
 error: explicit `deref` method call
-  --> $DIR/explicit_deref_methods.rs:42:20
+  --> $DIR/explicit_deref_methods.rs:43:20
    |
 LL |     println!("{}", a.deref());
    |                    ^^^^^^^^^ help: try this: `&*a`
 
 error: explicit `deref` method call
-  --> $DIR/explicit_deref_methods.rs:45:11
+  --> $DIR/explicit_deref_methods.rs:46:11
    |
 LL |     match a.deref() {
    |           ^^^^^^^^^ help: try this: `&*a`
 
 error: explicit `deref` method call
-  --> $DIR/explicit_deref_methods.rs:49:28
+  --> $DIR/explicit_deref_methods.rs:50:28
    |
 LL |     let b: String = concat(a.deref());
    |                            ^^^^^^^^^ help: try this: `&*a`
 
 error: explicit `deref` method call
-  --> $DIR/explicit_deref_methods.rs:51:13
+  --> $DIR/explicit_deref_methods.rs:52:13
    |
 LL |     let b = just_return(a).deref();
    |             ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)`
 
 error: explicit `deref` method call
-  --> $DIR/explicit_deref_methods.rs:53:28
+  --> $DIR/explicit_deref_methods.rs:54:28
    |
 LL |     let b: String = concat(just_return(a).deref());
    |                            ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `just_return(a)`
 
 error: explicit `deref` method call
-  --> $DIR/explicit_deref_methods.rs:55:19
+  --> $DIR/explicit_deref_methods.rs:56:19
    |
 LL |     let b: &str = a.deref().deref();
    |                   ^^^^^^^^^^^^^^^^^ help: try this: `&**a`
 
 error: explicit `deref` method call
-  --> $DIR/explicit_deref_methods.rs:58:13
+  --> $DIR/explicit_deref_methods.rs:59:13
    |
 LL |     let b = opt_a.unwrap().deref();
    |             ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `&*opt_a.unwrap()`
 
 error: explicit `deref` method call
-  --> $DIR/explicit_deref_methods.rs:84:31
+  --> $DIR/explicit_deref_methods.rs:85:31
    |
 LL |     let b: &str = expr_deref!(a.deref());
    |                               ^^^^^^^^^ help: try this: `&*a`
diff --git a/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs
index f76127a7105..d6631e01290 100644
--- a/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs
+++ b/src/tools/clippy/tests/ui/extra_unused_lifetimes.rs
@@ -1,3 +1,5 @@
+// aux-build:proc_macro_derive.rs
+
 #![allow(
     unused,
     dead_code,
@@ -7,6 +9,9 @@
 )]
 #![warn(clippy::extra_unused_lifetimes)]
 
+#[macro_use]
+extern crate proc_macro_derive;
+
 fn empty() {}
 
 fn used_lt<'a>(x: &'a u8) {}
@@ -114,4 +119,11 @@ mod second_case {
     }
 }
 
+// Should not lint
+#[derive(ExtraLifetimeDerive)]
+struct Human<'a> {
+    pub bones: i32,
+    pub name: &'a str,
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr b/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr
index fcc12d4ce14..26ebc3976df 100644
--- a/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr
+++ b/src/tools/clippy/tests/ui/extra_unused_lifetimes.stderr
@@ -1,5 +1,5 @@
 error: this lifetime isn't used in the function definition
-  --> $DIR/extra_unused_lifetimes.rs:14:14
+  --> $DIR/extra_unused_lifetimes.rs:19:14
    |
 LL | fn unused_lt<'a>(x: u8) {}
    |              ^^
@@ -7,31 +7,31 @@ LL | fn unused_lt<'a>(x: u8) {}
    = note: `-D clippy::extra-unused-lifetimes` implied by `-D warnings`
 
 error: this lifetime isn't used in the function definition
-  --> $DIR/extra_unused_lifetimes.rs:41:10
+  --> $DIR/extra_unused_lifetimes.rs:46:10
    |
 LL |     fn x<'a>(&self) {}
    |          ^^
 
 error: this lifetime isn't used in the function definition
-  --> $DIR/extra_unused_lifetimes.rs:67:22
+  --> $DIR/extra_unused_lifetimes.rs:72:22
    |
 LL |         fn unused_lt<'a>(x: u8) {}
    |                      ^^
 
 error: this lifetime isn't used in the impl
-  --> $DIR/extra_unused_lifetimes.rs:78:10
+  --> $DIR/extra_unused_lifetimes.rs:83:10
    |
 LL |     impl<'a> std::ops::AddAssign<&Scalar> for &mut Scalar {
    |          ^^
 
 error: this lifetime isn't used in the impl
-  --> $DIR/extra_unused_lifetimes.rs:84:10
+  --> $DIR/extra_unused_lifetimes.rs:89:10
    |
 LL |     impl<'b> Scalar {
    |          ^^
 
 error: this lifetime isn't used in the function definition
-  --> $DIR/extra_unused_lifetimes.rs:85:26
+  --> $DIR/extra_unused_lifetimes.rs:90:26
    |
 LL |         pub fn something<'c>() -> Self {
    |                          ^^
diff --git a/src/tools/clippy/tests/ui/if_same_then_else.rs b/src/tools/clippy/tests/ui/if_same_then_else.rs
index ef956745500..2598c2ab426 100644
--- a/src/tools/clippy/tests/ui/if_same_then_else.rs
+++ b/src/tools/clippy/tests/ui/if_same_then_else.rs
@@ -6,7 +6,9 @@
     clippy::no_effect,
     clippy::unused_unit,
     clippy::zero_divided_by_zero,
-    clippy::branches_sharing_code
+    clippy::branches_sharing_code,
+    dead_code,
+    unreachable_code
 )]
 
 struct Foo {
@@ -155,4 +157,61 @@ mod issue_5698 {
     }
 }
 
+mod issue_8836 {
+    fn do_not_lint() {
+        if true {
+            todo!()
+        } else {
+            todo!()
+        }
+        if true {
+            todo!();
+        } else {
+            todo!();
+        }
+        if true {
+            unimplemented!()
+        } else {
+            unimplemented!()
+        }
+        if true {
+            unimplemented!();
+        } else {
+            unimplemented!();
+        }
+
+        if true {
+            println!("FOO");
+            todo!();
+        } else {
+            println!("FOO");
+            todo!();
+        }
+
+        if true {
+            println!("FOO");
+            unimplemented!();
+        } else {
+            println!("FOO");
+            unimplemented!();
+        }
+
+        if true {
+            println!("FOO");
+            todo!()
+        } else {
+            println!("FOO");
+            todo!()
+        }
+
+        if true {
+            println!("FOO");
+            unimplemented!()
+        } else {
+            println!("FOO");
+            unimplemented!()
+        }
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/if_same_then_else.stderr b/src/tools/clippy/tests/ui/if_same_then_else.stderr
index 2f38052fc20..2cdf442486a 100644
--- a/src/tools/clippy/tests/ui/if_same_then_else.stderr
+++ b/src/tools/clippy/tests/ui/if_same_then_else.stderr
@@ -1,5 +1,5 @@
 error: this `if` has identical blocks
-  --> $DIR/if_same_then_else.rs:21:13
+  --> $DIR/if_same_then_else.rs:23:13
    |
 LL |       if true {
    |  _____________^
@@ -13,7 +13,7 @@ LL | |     } else {
    |
    = note: `-D clippy::if-same-then-else` implied by `-D warnings`
 note: same as this
-  --> $DIR/if_same_then_else.rs:29:12
+  --> $DIR/if_same_then_else.rs:31:12
    |
 LL |       } else {
    |  ____________^
@@ -26,7 +26,7 @@ LL | |     }
    | |_____^
 
 error: this `if` has identical blocks
-  --> $DIR/if_same_then_else.rs:65:21
+  --> $DIR/if_same_then_else.rs:67:21
    |
 LL |       let _ = if true {
    |  _____________________^
@@ -35,7 +35,7 @@ LL | |     } else {
    | |_____^
    |
 note: same as this
-  --> $DIR/if_same_then_else.rs:67:12
+  --> $DIR/if_same_then_else.rs:69:12
    |
 LL |       } else {
    |  ____________^
@@ -45,7 +45,7 @@ LL | |     };
    | |_____^
 
 error: this `if` has identical blocks
-  --> $DIR/if_same_then_else.rs:72:21
+  --> $DIR/if_same_then_else.rs:74:21
    |
 LL |       let _ = if true {
    |  _____________________^
@@ -54,7 +54,7 @@ LL | |     } else {
    | |_____^
    |
 note: same as this
-  --> $DIR/if_same_then_else.rs:74:12
+  --> $DIR/if_same_then_else.rs:76:12
    |
 LL |       } else {
    |  ____________^
@@ -64,7 +64,7 @@ LL | |     };
    | |_____^
 
 error: this `if` has identical blocks
-  --> $DIR/if_same_then_else.rs:88:21
+  --> $DIR/if_same_then_else.rs:90:21
    |
 LL |       let _ = if true {
    |  _____________________^
@@ -73,7 +73,7 @@ LL | |     } else {
    | |_____^
    |
 note: same as this
-  --> $DIR/if_same_then_else.rs:90:12
+  --> $DIR/if_same_then_else.rs:92:12
    |
 LL |       } else {
    |  ____________^
@@ -83,7 +83,7 @@ LL | |     };
    | |_____^
 
 error: this `if` has identical blocks
-  --> $DIR/if_same_then_else.rs:95:13
+  --> $DIR/if_same_then_else.rs:97:13
    |
 LL |       if true {
    |  _____________^
@@ -96,7 +96,7 @@ LL | |     } else {
    | |_____^
    |
 note: same as this
-  --> $DIR/if_same_then_else.rs:102:12
+  --> $DIR/if_same_then_else.rs:104:12
    |
 LL |       } else {
    |  ____________^
diff --git a/src/tools/clippy/tests/ui/implicit_return.fixed b/src/tools/clippy/tests/ui/implicit_return.fixed
index a51f7bc6a29..5e55b8b6739 100644
--- a/src/tools/clippy/tests/ui/implicit_return.fixed
+++ b/src/tools/clippy/tests/ui/implicit_return.fixed
@@ -1,5 +1,5 @@
 // run-rustfix
-
+#![feature(lint_reasons)]
 #![warn(clippy::implicit_return)]
 #![allow(clippy::needless_return, clippy::needless_bool, unused, clippy::never_loop)]
 
@@ -128,3 +128,13 @@ async fn foo() -> bool {
 }
 
 fn main() {}
+
+fn check_expect() -> bool {
+    if true {
+        // no error!
+        return true;
+    }
+
+    #[expect(clippy::implicit_return)]
+    true
+}
diff --git a/src/tools/clippy/tests/ui/implicit_return.rs b/src/tools/clippy/tests/ui/implicit_return.rs
index 03f8ec49d51..76f0a980352 100644
--- a/src/tools/clippy/tests/ui/implicit_return.rs
+++ b/src/tools/clippy/tests/ui/implicit_return.rs
@@ -1,5 +1,5 @@
 // run-rustfix
-
+#![feature(lint_reasons)]
 #![warn(clippy::implicit_return)]
 #![allow(clippy::needless_return, clippy::needless_bool, unused, clippy::never_loop)]
 
@@ -128,3 +128,13 @@ async fn foo() -> bool {
 }
 
 fn main() {}
+
+fn check_expect() -> bool {
+    if true {
+        // no error!
+        return true;
+    }
+
+    #[expect(clippy::implicit_return)]
+    true
+}
diff --git a/src/tools/clippy/tests/ui/let_underscore_lock.rs b/src/tools/clippy/tests/ui/let_underscore_lock.rs
index 539d74d1d4c..7a7c4e92499 100644
--- a/src/tools/clippy/tests/ui/let_underscore_lock.rs
+++ b/src/tools/clippy/tests/ui/let_underscore_lock.rs
@@ -13,6 +13,10 @@ fn main() {
     let _ = rw.try_read();
     let _ = rw.try_write();
 
+    // These shouldn't throw an error.
+    let _ = m;
+    let _ = rw;
+
     use parking_lot::{lock_api::RawMutex, Mutex, RwLock};
 
     let p_m: Mutex<()> = Mutex::const_new(RawMutex::INIT, ());
@@ -24,4 +28,9 @@ fn main() {
     let p_rw = RwLock::new(0);
     let _ = p_rw.read();
     let _ = p_rw.write();
+
+    // These shouldn't throw an error.
+    let _ = p_m;
+    let _ = p_m1;
+    let _ = p_rw;
 }
diff --git a/src/tools/clippy/tests/ui/let_underscore_lock.stderr b/src/tools/clippy/tests/ui/let_underscore_lock.stderr
index 3a2bc17bf7b..4365b48fabb 100644
--- a/src/tools/clippy/tests/ui/let_underscore_lock.stderr
+++ b/src/tools/clippy/tests/ui/let_underscore_lock.stderr
@@ -48,7 +48,7 @@ LL |     let _ = rw.try_write();
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
 error: non-binding let on a synchronization lock
-  --> $DIR/let_underscore_lock.rs:19:5
+  --> $DIR/let_underscore_lock.rs:23:5
    |
 LL |     let _ = p_m.lock();
    |     ^^^^^^^^^^^^^^^^^^^
@@ -56,7 +56,7 @@ LL |     let _ = p_m.lock();
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
 error: non-binding let on a synchronization lock
-  --> $DIR/let_underscore_lock.rs:22:5
+  --> $DIR/let_underscore_lock.rs:26:5
    |
 LL |     let _ = p_m1.lock();
    |     ^^^^^^^^^^^^^^^^^^^^
@@ -64,7 +64,7 @@ LL |     let _ = p_m1.lock();
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
 error: non-binding let on a synchronization lock
-  --> $DIR/let_underscore_lock.rs:25:5
+  --> $DIR/let_underscore_lock.rs:29:5
    |
 LL |     let _ = p_rw.read();
    |     ^^^^^^^^^^^^^^^^^^^^
@@ -72,7 +72,7 @@ LL |     let _ = p_rw.read();
    = help: consider using an underscore-prefixed named binding or dropping explicitly with `std::mem::drop`
 
 error: non-binding let on a synchronization lock
-  --> $DIR/let_underscore_lock.rs:26:5
+  --> $DIR/let_underscore_lock.rs:30:5
    |
 LL |     let _ = p_rw.write();
    |     ^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/logic_bug.rs b/src/tools/clippy/tests/ui/logic_bug.rs
index 4eaa2dd98eb..dd6b1db5f70 100644
--- a/src/tools/clippy/tests/ui/logic_bug.rs
+++ b/src/tools/clippy/tests/ui/logic_bug.rs
@@ -1,3 +1,4 @@
+#![feature(lint_reasons)]
 #![allow(unused, clippy::diverging_sub_expression)]
 #![warn(clippy::logic_bug)]
 
@@ -24,3 +25,10 @@ fn equality_stuff() {
     let _ = a > b && a <= b;
     let _ = a > b && a == b;
 }
+
+fn check_expect() {
+    let a: i32 = unimplemented!();
+    let b: i32 = unimplemented!();
+    #[expect(clippy::logic_bug)]
+    let _ = a < b && a >= b;
+}
diff --git a/src/tools/clippy/tests/ui/logic_bug.stderr b/src/tools/clippy/tests/ui/logic_bug.stderr
index 8f55e1c8ad8..4021fbf4570 100644
--- a/src/tools/clippy/tests/ui/logic_bug.stderr
+++ b/src/tools/clippy/tests/ui/logic_bug.stderr
@@ -1,60 +1,60 @@
 error: this boolean expression contains a logic bug
-  --> $DIR/logic_bug.rs:10:13
+  --> $DIR/logic_bug.rs:11:13
    |
 LL |     let _ = a && b || a;
    |             ^^^^^^^^^^^ help: it would look like the following: `a`
    |
    = note: `-D clippy::logic-bug` implied by `-D warnings`
 help: this expression can be optimized out by applying boolean operations to the outer expression
-  --> $DIR/logic_bug.rs:10:18
+  --> $DIR/logic_bug.rs:11:18
    |
 LL |     let _ = a && b || a;
    |                  ^
 
 error: this boolean expression contains a logic bug
-  --> $DIR/logic_bug.rs:12:13
+  --> $DIR/logic_bug.rs:13:13
    |
 LL |     let _ = false && a;
    |             ^^^^^^^^^^ help: it would look like the following: `false`
    |
 help: this expression can be optimized out by applying boolean operations to the outer expression
-  --> $DIR/logic_bug.rs:12:22
+  --> $DIR/logic_bug.rs:13:22
    |
 LL |     let _ = false && a;
    |                      ^
 
 error: this boolean expression contains a logic bug
-  --> $DIR/logic_bug.rs:22:13
+  --> $DIR/logic_bug.rs:23:13
    |
 LL |     let _ = a == b && a != b;
    |             ^^^^^^^^^^^^^^^^ help: it would look like the following: `false`
    |
 help: this expression can be optimized out by applying boolean operations to the outer expression
-  --> $DIR/logic_bug.rs:22:13
+  --> $DIR/logic_bug.rs:23:13
    |
 LL |     let _ = a == b && a != b;
    |             ^^^^^^
 
 error: this boolean expression contains a logic bug
-  --> $DIR/logic_bug.rs:23:13
+  --> $DIR/logic_bug.rs:24:13
    |
 LL |     let _ = a < b && a >= b;
    |             ^^^^^^^^^^^^^^^ help: it would look like the following: `false`
    |
 help: this expression can be optimized out by applying boolean operations to the outer expression
-  --> $DIR/logic_bug.rs:23:13
+  --> $DIR/logic_bug.rs:24:13
    |
 LL |     let _ = a < b && a >= b;
    |             ^^^^^
 
 error: this boolean expression contains a logic bug
-  --> $DIR/logic_bug.rs:24:13
+  --> $DIR/logic_bug.rs:25:13
    |
 LL |     let _ = a > b && a <= b;
    |             ^^^^^^^^^^^^^^^ help: it would look like the following: `false`
    |
 help: this expression can be optimized out by applying boolean operations to the outer expression
-  --> $DIR/logic_bug.rs:24:13
+  --> $DIR/logic_bug.rs:25:13
    |
 LL |     let _ = a > b && a <= b;
    |             ^^^^^
diff --git a/src/tools/clippy/tests/ui/macro_use_imports.fixed b/src/tools/clippy/tests/ui/macro_use_imports.fixed
index a83c8ba0b64..e612480d264 100644
--- a/src/tools/clippy/tests/ui/macro_use_imports.fixed
+++ b/src/tools/clippy/tests/ui/macro_use_imports.fixed
@@ -4,6 +4,7 @@
 // run-rustfix
 // ignore-32bit
 
+#![feature(lint_reasons)]
 #![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
 #![allow(clippy::single_component_path_imports)]
 #![warn(clippy::macro_use_imports)]
diff --git a/src/tools/clippy/tests/ui/macro_use_imports.rs b/src/tools/clippy/tests/ui/macro_use_imports.rs
index e26a7545ea6..b34817cc3b2 100644
--- a/src/tools/clippy/tests/ui/macro_use_imports.rs
+++ b/src/tools/clippy/tests/ui/macro_use_imports.rs
@@ -4,6 +4,7 @@
 // run-rustfix
 // ignore-32bit
 
+#![feature(lint_reasons)]
 #![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
 #![allow(clippy::single_component_path_imports)]
 #![warn(clippy::macro_use_imports)]
diff --git a/src/tools/clippy/tests/ui/macro_use_imports.stderr b/src/tools/clippy/tests/ui/macro_use_imports.stderr
index 9028a636e7f..bf7b6edd0e3 100644
--- a/src/tools/clippy/tests/ui/macro_use_imports.stderr
+++ b/src/tools/clippy/tests/ui/macro_use_imports.stderr
@@ -1,28 +1,28 @@
 error: `macro_use` attributes are no longer needed in the Rust 2018 edition
-  --> $DIR/macro_use_imports.rs:18:5
+  --> $DIR/macro_use_imports.rs:23:5
    |
 LL |     #[macro_use]
-   |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, function_macro, ty_macro, inner_mod_macro, pub_in_private_macro};`
+   |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};`
    |
    = note: `-D clippy::macro-use-imports` implied by `-D warnings`
 
 error: `macro_use` attributes are no longer needed in the Rust 2018 edition
-  --> $DIR/macro_use_imports.rs:20:5
+  --> $DIR/macro_use_imports.rs:21:5
    |
 LL |     #[macro_use]
    |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mini_mac::ClippyMiniMacroTest;`
 
 error: `macro_use` attributes are no longer needed in the Rust 2018 edition
-  --> $DIR/macro_use_imports.rs:22:5
+  --> $DIR/macro_use_imports.rs:25:5
    |
 LL |     #[macro_use]
-   |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{inner::foofoo, inner::try_err};`
+   |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;`
 
 error: `macro_use` attributes are no longer needed in the Rust 2018 edition
-  --> $DIR/macro_use_imports.rs:24:5
+  --> $DIR/macro_use_imports.rs:19:5
    |
 LL |     #[macro_use]
-   |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::inner::nested::string_add;`
+   |     ^^^^^^^^^^^^ help: remove the attribute and import the macro directly, try: `use mac::{pub_macro, function_macro, ty_macro, inner_mod_macro, pub_in_private_macro};`
 
 error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/macro_use_imports_expect.rs b/src/tools/clippy/tests/ui/macro_use_imports_expect.rs
new file mode 100644
index 00000000000..8a1b05da9ef
--- /dev/null
+++ b/src/tools/clippy/tests/ui/macro_use_imports_expect.rs
@@ -0,0 +1,51 @@
+// aux-build:macro_rules.rs
+// aux-build:macro_use_helper.rs
+// aux-build:proc_macro_derive.rs
+// ignore-32bit
+
+#![feature(lint_reasons)]
+#![allow(unused_imports, unreachable_code, unused_variables, dead_code, unused_attributes)]
+#![allow(clippy::single_component_path_imports)]
+#![warn(clippy::macro_use_imports)]
+
+#[macro_use]
+extern crate macro_use_helper as mac;
+
+#[macro_use]
+extern crate proc_macro_derive as mini_mac;
+
+mod a {
+    #[expect(clippy::macro_use_imports)]
+    #[macro_use]
+    use mac;
+    #[expect(clippy::macro_use_imports)]
+    #[macro_use]
+    use mini_mac;
+    #[expect(clippy::macro_use_imports)]
+    #[macro_use]
+    use mac::inner;
+    #[expect(clippy::macro_use_imports)]
+    #[macro_use]
+    use mac::inner::nested;
+
+    #[derive(ClippyMiniMacroTest)]
+    struct Test;
+
+    fn test() {
+        pub_macro!();
+        inner_mod_macro!();
+        pub_in_private_macro!(_var);
+        function_macro!();
+        let v: ty_macro!() = Vec::default();
+
+        inner::try_err!();
+        inner::foofoo!();
+        nested::string_add!();
+    }
+}
+
+// issue #7015, ICE due to calling `module_children` with local `DefId`
+#[macro_use]
+use a as b;
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_find.rs b/src/tools/clippy/tests/ui/manual_find.rs
new file mode 100644
index 00000000000..257fe045f78
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_find.rs
@@ -0,0 +1,22 @@
+#![allow(unused)]
+#![warn(clippy::manual_find)]
+
+fn vec_string(strings: Vec<String>) -> Option<String> {
+    for s in strings {
+        if s == String::new() {
+            return Some(s);
+        }
+    }
+    None
+}
+
+fn tuple(arr: Vec<(String, i32)>) -> Option<String> {
+    for (s, _) in arr {
+        if s == String::new() {
+            return Some(s);
+        }
+    }
+    None
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_find.stderr b/src/tools/clippy/tests/ui/manual_find.stderr
new file mode 100644
index 00000000000..da0fd4aaef7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_find.stderr
@@ -0,0 +1,29 @@
+error: manual implementation of `Iterator::find`
+  --> $DIR/manual_find.rs:5:5
+   |
+LL | /     for s in strings {
+LL | |         if s == String::new() {
+LL | |             return Some(s);
+LL | |         }
+LL | |     }
+LL | |     None
+   | |________^ help: replace with an iterator: `strings.into_iter().find(|s| s == String::new())`
+   |
+   = note: `-D clippy::manual-find` implied by `-D warnings`
+   = note: you may need to dereference some variables
+
+error: manual implementation of `Iterator::find`
+  --> $DIR/manual_find.rs:14:5
+   |
+LL | /     for (s, _) in arr {
+LL | |         if s == String::new() {
+LL | |             return Some(s);
+LL | |         }
+LL | |     }
+LL | |     None
+   | |________^ help: replace with an iterator: `arr.into_iter().map(|(s, _)| s).find(|s| s == String::new())`
+   |
+   = note: you may need to dereference some variables
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_find_fixable.fixed b/src/tools/clippy/tests/ui/manual_find_fixable.fixed
new file mode 100644
index 00000000000..36d1644c22b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_find_fixable.fixed
@@ -0,0 +1,182 @@
+// run-rustfix
+
+#![allow(unused, clippy::needless_return)]
+#![warn(clippy::manual_find)]
+
+use std::collections::HashMap;
+
+const ARRAY: &[u32; 5] = &[2, 7, 1, 9, 3];
+
+fn lookup(n: u32) -> Option<u32> {
+    ARRAY.iter().find(|&&v| v == n).copied()
+}
+
+fn with_pat(arr: Vec<(u32, u32)>) -> Option<u32> {
+    arr.into_iter().map(|(a, _)| a).find(|&a| a % 2 == 0)
+}
+
+struct Data {
+    name: String,
+    is_true: bool,
+}
+fn with_struct(arr: Vec<Data>) -> Option<Data> {
+    arr.into_iter().find(|el| el.name.len() == 10)
+}
+
+struct Tuple(usize, usize);
+fn with_tuple_struct(arr: Vec<Tuple>) -> Option<usize> {
+    arr.into_iter().map(|Tuple(a, _)| a).find(|&a| a >= 3)
+}
+
+struct A;
+impl A {
+    fn should_keep(&self) -> bool {
+        true
+    }
+}
+fn with_method_call(arr: Vec<A>) -> Option<A> {
+    arr.into_iter().find(|el| el.should_keep())
+}
+
+fn with_closure(arr: Vec<u32>) -> Option<u32> {
+    let f = |el: u32| -> u32 { el + 10 };
+    arr.into_iter().find(|&el| f(el) == 20)
+}
+
+fn with_closure2(arr: HashMap<String, i32>) -> Option<i32> {
+    let f = |el: i32| -> bool { el == 10 };
+    arr.values().find(|&&el| f(el)).copied()
+}
+
+fn with_bool(arr: Vec<Data>) -> Option<Data> {
+    arr.into_iter().find(|el| el.is_true)
+}
+
+fn with_side_effects(arr: Vec<u32>) -> Option<u32> {
+    for v in arr {
+        if v == 1 {
+            println!("side effect");
+            return Some(v);
+        }
+    }
+    None
+}
+
+fn with_else(arr: Vec<u32>) -> Option<u32> {
+    for el in arr {
+        if el % 2 == 0 {
+            return Some(el);
+        } else {
+            println!("{}", el);
+        }
+    }
+    None
+}
+
+fn tuple_with_ref(v: [(u8, &u8); 3]) -> Option<u8> {
+    v.into_iter().map(|(_, &x)| x).find(|&x| x > 10)
+}
+
+fn ref_to_tuple_with_ref(v: &[(u8, &u8)]) -> Option<u8> {
+    v.iter().map(|&(_, &x)| x).find(|&x| x > 10)
+}
+
+fn explicit_ret(arr: Vec<i32>) -> Option<i32> {
+    arr.into_iter().find(|&x| x >= 5)
+}
+
+fn plus_one(a: i32) -> Option<i32> {
+    Some(a + 1)
+}
+fn fn_instead_of_some(a: &[i32]) -> Option<i32> {
+    for &x in a {
+        if x == 1 {
+            return plus_one(x);
+        }
+    }
+    None
+}
+
+fn for_in_condition(a: &[i32], b: bool) -> Option<i32> {
+    if b {
+        for &x in a {
+            if x == 1 {
+                return Some(x);
+            }
+        }
+    }
+    None
+}
+
+fn intermediate_statements(a: &[i32]) -> Option<i32> {
+    for &x in a {
+        if x == 1 {
+            return Some(x);
+        }
+    }
+
+    println!("side effect");
+
+    None
+}
+
+fn mixed_binding_modes(arr: Vec<(i32, String)>) -> Option<i32> {
+    for (x, mut s) in arr {
+        if x == 1 && s.as_mut_str().len() == 2 {
+            return Some(x);
+        }
+    }
+    None
+}
+
+fn as_closure() {
+    #[rustfmt::skip]
+    let f = |arr: Vec<i32>| -> Option<i32> {
+        arr.into_iter().find(|&x| x < 1)
+    };
+}
+
+fn in_block(a: &[i32]) -> Option<i32> {
+    let should_be_none = {
+        for &x in a {
+            if x == 1 {
+                return Some(x);
+            }
+        }
+        None
+    };
+
+    assert!(should_be_none.is_none());
+
+    should_be_none
+}
+
+// Not handled yet
+fn mut_binding(v: Vec<String>) -> Option<String> {
+    for mut s in v {
+        if s.as_mut_str().len() > 1 {
+            return Some(s);
+        }
+    }
+    None
+}
+
+fn subpattern(v: Vec<[u32; 32]>) -> Option<[u32; 32]> {
+    for a @ [first, ..] in v {
+        if a[12] == first {
+            return Some(a);
+        }
+    }
+    None
+}
+
+fn two_bindings(v: Vec<(u8, u8)>) -> Option<u8> {
+    for (a, n) in v {
+        if a == n {
+            return Some(a);
+        }
+    }
+    None
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_find_fixable.rs b/src/tools/clippy/tests/ui/manual_find_fixable.rs
new file mode 100644
index 00000000000..ed277ddaa72
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_find_fixable.rs
@@ -0,0 +1,242 @@
+// run-rustfix
+
+#![allow(unused, clippy::needless_return)]
+#![warn(clippy::manual_find)]
+
+use std::collections::HashMap;
+
+const ARRAY: &[u32; 5] = &[2, 7, 1, 9, 3];
+
+fn lookup(n: u32) -> Option<u32> {
+    for &v in ARRAY {
+        if v == n {
+            return Some(v);
+        }
+    }
+    None
+}
+
+fn with_pat(arr: Vec<(u32, u32)>) -> Option<u32> {
+    for (a, _) in arr {
+        if a % 2 == 0 {
+            return Some(a);
+        }
+    }
+    None
+}
+
+struct Data {
+    name: String,
+    is_true: bool,
+}
+fn with_struct(arr: Vec<Data>) -> Option<Data> {
+    for el in arr {
+        if el.name.len() == 10 {
+            return Some(el);
+        }
+    }
+    None
+}
+
+struct Tuple(usize, usize);
+fn with_tuple_struct(arr: Vec<Tuple>) -> Option<usize> {
+    for Tuple(a, _) in arr {
+        if a >= 3 {
+            return Some(a);
+        }
+    }
+    None
+}
+
+struct A;
+impl A {
+    fn should_keep(&self) -> bool {
+        true
+    }
+}
+fn with_method_call(arr: Vec<A>) -> Option<A> {
+    for el in arr {
+        if el.should_keep() {
+            return Some(el);
+        }
+    }
+    None
+}
+
+fn with_closure(arr: Vec<u32>) -> Option<u32> {
+    let f = |el: u32| -> u32 { el + 10 };
+    for el in arr {
+        if f(el) == 20 {
+            return Some(el);
+        }
+    }
+    None
+}
+
+fn with_closure2(arr: HashMap<String, i32>) -> Option<i32> {
+    let f = |el: i32| -> bool { el == 10 };
+    for &el in arr.values() {
+        if f(el) {
+            return Some(el);
+        }
+    }
+    None
+}
+
+fn with_bool(arr: Vec<Data>) -> Option<Data> {
+    for el in arr {
+        if el.is_true {
+            return Some(el);
+        }
+    }
+    None
+}
+
+fn with_side_effects(arr: Vec<u32>) -> Option<u32> {
+    for v in arr {
+        if v == 1 {
+            println!("side effect");
+            return Some(v);
+        }
+    }
+    None
+}
+
+fn with_else(arr: Vec<u32>) -> Option<u32> {
+    for el in arr {
+        if el % 2 == 0 {
+            return Some(el);
+        } else {
+            println!("{}", el);
+        }
+    }
+    None
+}
+
+fn tuple_with_ref(v: [(u8, &u8); 3]) -> Option<u8> {
+    for (_, &x) in v {
+        if x > 10 {
+            return Some(x);
+        }
+    }
+    None
+}
+
+fn ref_to_tuple_with_ref(v: &[(u8, &u8)]) -> Option<u8> {
+    for &(_, &x) in v {
+        if x > 10 {
+            return Some(x);
+        }
+    }
+    None
+}
+
+fn explicit_ret(arr: Vec<i32>) -> Option<i32> {
+    for x in arr {
+        if x >= 5 {
+            return Some(x);
+        }
+    }
+    return None;
+}
+
+fn plus_one(a: i32) -> Option<i32> {
+    Some(a + 1)
+}
+fn fn_instead_of_some(a: &[i32]) -> Option<i32> {
+    for &x in a {
+        if x == 1 {
+            return plus_one(x);
+        }
+    }
+    None
+}
+
+fn for_in_condition(a: &[i32], b: bool) -> Option<i32> {
+    if b {
+        for &x in a {
+            if x == 1 {
+                return Some(x);
+            }
+        }
+    }
+    None
+}
+
+fn intermediate_statements(a: &[i32]) -> Option<i32> {
+    for &x in a {
+        if x == 1 {
+            return Some(x);
+        }
+    }
+
+    println!("side effect");
+
+    None
+}
+
+fn mixed_binding_modes(arr: Vec<(i32, String)>) -> Option<i32> {
+    for (x, mut s) in arr {
+        if x == 1 && s.as_mut_str().len() == 2 {
+            return Some(x);
+        }
+    }
+    None
+}
+
+fn as_closure() {
+    #[rustfmt::skip]
+    let f = |arr: Vec<i32>| -> Option<i32> {
+        for x in arr {
+            if x < 1 {
+                return Some(x);
+            }
+        }
+        None
+    };
+}
+
+fn in_block(a: &[i32]) -> Option<i32> {
+    let should_be_none = {
+        for &x in a {
+            if x == 1 {
+                return Some(x);
+            }
+        }
+        None
+    };
+
+    assert!(should_be_none.is_none());
+
+    should_be_none
+}
+
+// Not handled yet
+fn mut_binding(v: Vec<String>) -> Option<String> {
+    for mut s in v {
+        if s.as_mut_str().len() > 1 {
+            return Some(s);
+        }
+    }
+    None
+}
+
+fn subpattern(v: Vec<[u32; 32]>) -> Option<[u32; 32]> {
+    for a @ [first, ..] in v {
+        if a[12] == first {
+            return Some(a);
+        }
+    }
+    None
+}
+
+fn two_bindings(v: Vec<(u8, u8)>) -> Option<u8> {
+    for (a, n) in v {
+        if a == n {
+            return Some(a);
+        }
+    }
+    None
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_find_fixable.stderr b/src/tools/clippy/tests/ui/manual_find_fixable.stderr
new file mode 100644
index 00000000000..dbc4ff69a74
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_find_fixable.stderr
@@ -0,0 +1,142 @@
+error: manual implementation of `Iterator::find`
+  --> $DIR/manual_find_fixable.rs:11:5
+   |
+LL | /     for &v in ARRAY {
+LL | |         if v == n {
+LL | |             return Some(v);
+LL | |         }
+LL | |     }
+LL | |     None
+   | |________^ help: replace with an iterator: `ARRAY.iter().find(|&&v| v == n).copied()`
+   |
+   = note: `-D clippy::manual-find` implied by `-D warnings`
+
+error: manual implementation of `Iterator::find`
+  --> $DIR/manual_find_fixable.rs:20:5
+   |
+LL | /     for (a, _) in arr {
+LL | |         if a % 2 == 0 {
+LL | |             return Some(a);
+LL | |         }
+LL | |     }
+LL | |     None
+   | |________^ help: replace with an iterator: `arr.into_iter().map(|(a, _)| a).find(|&a| a % 2 == 0)`
+
+error: manual implementation of `Iterator::find`
+  --> $DIR/manual_find_fixable.rs:33:5
+   |
+LL | /     for el in arr {
+LL | |         if el.name.len() == 10 {
+LL | |             return Some(el);
+LL | |         }
+LL | |     }
+LL | |     None
+   | |________^ help: replace with an iterator: `arr.into_iter().find(|el| el.name.len() == 10)`
+   |
+   = note: you may need to dereference some variables
+
+error: manual implementation of `Iterator::find`
+  --> $DIR/manual_find_fixable.rs:43:5
+   |
+LL | /     for Tuple(a, _) in arr {
+LL | |         if a >= 3 {
+LL | |             return Some(a);
+LL | |         }
+LL | |     }
+LL | |     None
+   | |________^ help: replace with an iterator: `arr.into_iter().map(|Tuple(a, _)| a).find(|&a| a >= 3)`
+
+error: manual implementation of `Iterator::find`
+  --> $DIR/manual_find_fixable.rs:58:5
+   |
+LL | /     for el in arr {
+LL | |         if el.should_keep() {
+LL | |             return Some(el);
+LL | |         }
+LL | |     }
+LL | |     None
+   | |________^ help: replace with an iterator: `arr.into_iter().find(|el| el.should_keep())`
+   |
+   = note: you may need to dereference some variables
+
+error: manual implementation of `Iterator::find`
+  --> $DIR/manual_find_fixable.rs:68:5
+   |
+LL | /     for el in arr {
+LL | |         if f(el) == 20 {
+LL | |             return Some(el);
+LL | |         }
+LL | |     }
+LL | |     None
+   | |________^ help: replace with an iterator: `arr.into_iter().find(|&el| f(el) == 20)`
+
+error: manual implementation of `Iterator::find`
+  --> $DIR/manual_find_fixable.rs:78:5
+   |
+LL | /     for &el in arr.values() {
+LL | |         if f(el) {
+LL | |             return Some(el);
+LL | |         }
+LL | |     }
+LL | |     None
+   | |________^ help: replace with an iterator: `arr.values().find(|&&el| f(el)).copied()`
+
+error: manual implementation of `Iterator::find`
+  --> $DIR/manual_find_fixable.rs:87:5
+   |
+LL | /     for el in arr {
+LL | |         if el.is_true {
+LL | |             return Some(el);
+LL | |         }
+LL | |     }
+LL | |     None
+   | |________^ help: replace with an iterator: `arr.into_iter().find(|el| el.is_true)`
+   |
+   = note: you may need to dereference some variables
+
+error: manual implementation of `Iterator::find`
+  --> $DIR/manual_find_fixable.rs:117:5
+   |
+LL | /     for (_, &x) in v {
+LL | |         if x > 10 {
+LL | |             return Some(x);
+LL | |         }
+LL | |     }
+LL | |     None
+   | |________^ help: replace with an iterator: `v.into_iter().map(|(_, &x)| x).find(|&x| x > 10)`
+
+error: manual implementation of `Iterator::find`
+  --> $DIR/manual_find_fixable.rs:126:5
+   |
+LL | /     for &(_, &x) in v {
+LL | |         if x > 10 {
+LL | |             return Some(x);
+LL | |         }
+LL | |     }
+LL | |     None
+   | |________^ help: replace with an iterator: `v.iter().map(|&(_, &x)| x).find(|&x| x > 10)`
+
+error: manual implementation of `Iterator::find`
+  --> $DIR/manual_find_fixable.rs:135:5
+   |
+LL | /     for x in arr {
+LL | |         if x >= 5 {
+LL | |             return Some(x);
+LL | |         }
+LL | |     }
+LL | |     return None;
+   | |________________^ help: replace with an iterator: `arr.into_iter().find(|&x| x >= 5)`
+
+error: manual implementation of `Iterator::find`
+  --> $DIR/manual_find_fixable.rs:190:9
+   |
+LL | /         for x in arr {
+LL | |             if x < 1 {
+LL | |                 return Some(x);
+LL | |             }
+LL | |         }
+LL | |         None
+   | |____________^ help: replace with an iterator: `arr.into_iter().find(|&x| x < 1)`
+
+error: aborting due to 12 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs
index f23c6d69b4c..03b2433f666 100644
--- a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs
+++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.rs
@@ -1,3 +1,4 @@
+#![feature(lint_reasons)]
 #![warn(clippy::manual_non_exhaustive)]
 #![allow(unused)]
 
@@ -75,4 +76,12 @@ fn foo(x: &mut UsedHidden) {
     }
 }
 
+#[expect(clippy::manual_non_exhaustive)]
+enum ExpectLint {
+    A,
+    B,
+    #[doc(hidden)]
+    _C,
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr
index 317a45d2cbd..144fe86df55 100644
--- a/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr
+++ b/src/tools/clippy/tests/ui/manual_non_exhaustive_enum.stderr
@@ -1,5 +1,5 @@
 error: this seems like a manual implementation of the non-exhaustive pattern
-  --> $DIR/manual_non_exhaustive_enum.rs:4:1
+  --> $DIR/manual_non_exhaustive_enum.rs:5:1
    |
 LL |   enum E {
    |   ^-----
@@ -15,13 +15,13 @@ LL | | }
    |
    = note: `-D clippy::manual-non-exhaustive` implied by `-D warnings`
 help: remove this variant
-  --> $DIR/manual_non_exhaustive_enum.rs:8:5
+  --> $DIR/manual_non_exhaustive_enum.rs:9:5
    |
 LL |     _C,
    |     ^^
 
 error: this seems like a manual implementation of the non-exhaustive pattern
-  --> $DIR/manual_non_exhaustive_enum.rs:13:1
+  --> $DIR/manual_non_exhaustive_enum.rs:14:1
    |
 LL | / enum Ep {
 LL | |     A,
@@ -32,7 +32,7 @@ LL | | }
    | |_^
    |
 help: remove this variant
-  --> $DIR/manual_non_exhaustive_enum.rs:17:5
+  --> $DIR/manual_non_exhaustive_enum.rs:18:5
    |
 LL |     _C,
    |     ^^
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.fixed b/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
new file mode 100644
index 00000000000..5601c96c10b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.fixed
@@ -0,0 +1,55 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::manual_rem_euclid)]
+
+#[macro_use]
+extern crate macro_rules;
+
+macro_rules! internal_rem_euclid {
+    () => {
+        let value: i32 = 5;
+        let _: i32 = value.rem_euclid(4);
+    };
+}
+
+fn main() {
+    let value: i32 = 5;
+
+    let _: i32 = value.rem_euclid(4);
+    let _: i32 = value.rem_euclid(4);
+    let _: i32 = value.rem_euclid(4);
+    let _: i32 = value.rem_euclid(4);
+    let _: i32 = 1 + value.rem_euclid(4);
+
+    let _: i32 = (3 + value % 4) % 4;
+    let _: i32 = (-4 + value % -4) % -4;
+    let _: i32 = ((5 % 4) + 4) % 4;
+
+    // Make sure the lint does not trigger if it would cause an error, like with an ambiguous
+    // integer type
+    let not_annotated = 24;
+    let _ = ((not_annotated % 4) + 4) % 4;
+    let inferred: _ = 24;
+    let _ = ((inferred % 4) + 4) % 4;
+
+    // For lint to apply the constant must always be on the RHS of the previous value for %
+    let _: i32 = 4 % ((value % 4) + 4);
+    let _: i32 = ((4 % value) + 4) % 4;
+
+    // Lint in internal macros
+    internal_rem_euclid!();
+
+    // Do not lint in external macros
+    manual_rem_euclid!();
+}
+
+// Should lint for params too
+pub fn rem_euclid_4(num: i32) -> i32 {
+    num.rem_euclid(4)
+}
+
+// Constant version came later, should still lint
+pub const fn const_rem_euclid_4(num: i32) -> i32 {
+    num.rem_euclid(4)
+}
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.rs b/src/tools/clippy/tests/ui/manual_rem_euclid.rs
new file mode 100644
index 00000000000..52135be26b7
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.rs
@@ -0,0 +1,55 @@
+// run-rustfix
+// aux-build:macro_rules.rs
+
+#![warn(clippy::manual_rem_euclid)]
+
+#[macro_use]
+extern crate macro_rules;
+
+macro_rules! internal_rem_euclid {
+    () => {
+        let value: i32 = 5;
+        let _: i32 = ((value % 4) + 4) % 4;
+    };
+}
+
+fn main() {
+    let value: i32 = 5;
+
+    let _: i32 = ((value % 4) + 4) % 4;
+    let _: i32 = (4 + (value % 4)) % 4;
+    let _: i32 = (value % 4 + 4) % 4;
+    let _: i32 = (4 + value % 4) % 4;
+    let _: i32 = 1 + (4 + value % 4) % 4;
+
+    let _: i32 = (3 + value % 4) % 4;
+    let _: i32 = (-4 + value % -4) % -4;
+    let _: i32 = ((5 % 4) + 4) % 4;
+
+    // Make sure the lint does not trigger if it would cause an error, like with an ambiguous
+    // integer type
+    let not_annotated = 24;
+    let _ = ((not_annotated % 4) + 4) % 4;
+    let inferred: _ = 24;
+    let _ = ((inferred % 4) + 4) % 4;
+
+    // For lint to apply the constant must always be on the RHS of the previous value for %
+    let _: i32 = 4 % ((value % 4) + 4);
+    let _: i32 = ((4 % value) + 4) % 4;
+
+    // Lint in internal macros
+    internal_rem_euclid!();
+
+    // Do not lint in external macros
+    manual_rem_euclid!();
+}
+
+// Should lint for params too
+pub fn rem_euclid_4(num: i32) -> i32 {
+    ((num % 4) + 4) % 4
+}
+
+// Constant version came later, should still lint
+pub const fn const_rem_euclid_4(num: i32) -> i32 {
+    ((num % 4) + 4) % 4
+}
diff --git a/src/tools/clippy/tests/ui/manual_rem_euclid.stderr b/src/tools/clippy/tests/ui/manual_rem_euclid.stderr
new file mode 100644
index 00000000000..a237fd0213c
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_rem_euclid.stderr
@@ -0,0 +1,57 @@
+error: manual `rem_euclid` implementation
+  --> $DIR/manual_rem_euclid.rs:19:18
+   |
+LL |     let _: i32 = ((value % 4) + 4) % 4;
+   |                  ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
+   |
+   = note: `-D clippy::manual-rem-euclid` implied by `-D warnings`
+
+error: manual `rem_euclid` implementation
+  --> $DIR/manual_rem_euclid.rs:20:18
+   |
+LL |     let _: i32 = (4 + (value % 4)) % 4;
+   |                  ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
+
+error: manual `rem_euclid` implementation
+  --> $DIR/manual_rem_euclid.rs:21:18
+   |
+LL |     let _: i32 = (value % 4 + 4) % 4;
+   |                  ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
+
+error: manual `rem_euclid` implementation
+  --> $DIR/manual_rem_euclid.rs:22:18
+   |
+LL |     let _: i32 = (4 + value % 4) % 4;
+   |                  ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
+
+error: manual `rem_euclid` implementation
+  --> $DIR/manual_rem_euclid.rs:23:22
+   |
+LL |     let _: i32 = 1 + (4 + value % 4) % 4;
+   |                      ^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
+
+error: manual `rem_euclid` implementation
+  --> $DIR/manual_rem_euclid.rs:12:22
+   |
+LL |         let _: i32 = ((value % 4) + 4) % 4;
+   |                      ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `value.rem_euclid(4)`
+...
+LL |     internal_rem_euclid!();
+   |     ---------------------- in this macro invocation
+   |
+   = note: this error originates in the macro `internal_rem_euclid` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: manual `rem_euclid` implementation
+  --> $DIR/manual_rem_euclid.rs:49:5
+   |
+LL |     ((num % 4) + 4) % 4
+   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
+
+error: manual `rem_euclid` implementation
+  --> $DIR/manual_rem_euclid.rs:54:5
+   |
+LL |     ((num % 4) + 4) % 4
+   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `num.rem_euclid(4)`
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_retain.fixed b/src/tools/clippy/tests/ui/manual_retain.fixed
new file mode 100644
index 00000000000..fba503a2066
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_retain.fixed
@@ -0,0 +1,240 @@
+// run-rustfix
+#![feature(custom_inner_attributes)]
+#![warn(clippy::manual_retain)]
+#![allow(unused)]
+use std::collections::BTreeMap;
+use std::collections::BTreeSet;
+use std::collections::BinaryHeap;
+use std::collections::HashMap;
+use std::collections::HashSet;
+use std::collections::VecDeque;
+
+fn main() {
+    binary_heap_retain();
+    btree_set_retain();
+    btree_map_retain();
+    hash_set_retain();
+    hash_map_retain();
+    string_retain();
+    vec_deque_retain();
+    vec_retain();
+    _msrv_153();
+    _msrv_126();
+    _msrv_118();
+}
+
+fn binary_heap_retain() {
+    // NOTE: Do not lint now, because binary_heap_retain is nighyly API.
+    // And we need to add a test case for msrv if we update this implmention.
+    // https://github.com/rust-lang/rust/issues/71503
+    let mut heap = BinaryHeap::from([1, 2, 3]);
+    heap = heap.into_iter().filter(|x| x % 2 == 0).collect();
+    heap = heap.iter().filter(|&x| x % 2 == 0).copied().collect();
+    heap = heap.iter().filter(|&x| x % 2 == 0).cloned().collect();
+
+    // Do not lint, because type conversion is performed
+    heap = heap.into_iter().filter(|x| x % 2 == 0).collect::<BinaryHeap<i8>>();
+    heap = heap.iter().filter(|&x| x % 2 == 0).copied().collect::<BinaryHeap<i8>>();
+    heap = heap.iter().filter(|&x| x % 2 == 0).cloned().collect::<BinaryHeap<i8>>();
+
+    // Do not lint, because this expression is not assign.
+    let mut bar: BinaryHeap<i8> = heap.iter().filter(|&x| x % 2 == 0).copied().collect();
+    let mut foobar: BinaryHeap<i8> = heap.into_iter().filter(|x| x % 2 == 0).collect();
+
+    // Do not lint, because it is an assignment to a different variable.
+    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+    bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
+}
+
+fn btree_map_retain() {
+    let mut btree_map: BTreeMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
+    // Do lint.
+    btree_map.retain(|k, _| k % 2 == 0);
+    btree_map.retain(|_, &mut v| v % 2 == 0);
+    btree_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0));
+
+    // Do not lint.
+    btree_map = btree_map
+        .into_iter()
+        .filter(|(x, _)| x % 2 == 0)
+        .collect::<BTreeMap<i8, i8>>();
+
+    // Do not lint, because this expression is not assign.
+    let mut foobar: BTreeMap<i8, i8> = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+
+    // Do not lint, because it is an assignment to a different variable.
+    btree_map = foobar.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+}
+
+fn btree_set_retain() {
+    let mut btree_set = BTreeSet::from([1, 2, 3, 4, 5, 6]);
+
+    // Do lint.
+    btree_set.retain(|x| x % 2 == 0);
+    btree_set.retain(|x| x % 2 == 0);
+    btree_set.retain(|x| x % 2 == 0);
+
+    // Do not lint, because type conversion is performed
+    btree_set = btree_set
+        .iter()
+        .filter(|&x| x % 2 == 0)
+        .copied()
+        .collect::<BTreeSet<i8>>();
+
+    btree_set = btree_set
+        .iter()
+        .filter(|&x| x % 2 == 0)
+        .cloned()
+        .collect::<BTreeSet<i8>>();
+
+    btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect::<BTreeSet<i8>>();
+
+    // Do not lint, because this expression is not assign.
+    let mut foobar: BTreeSet<i8> = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+    let mut bar: BTreeSet<i8> = btree_set.into_iter().filter(|x| x % 2 == 0).collect();
+
+    // Do not lint, because it is an assignment to a different variable.
+    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+    bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
+    bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
+}
+
+fn hash_map_retain() {
+    let mut hash_map: HashMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
+    // Do lint.
+    hash_map.retain(|k, _| k % 2 == 0);
+    hash_map.retain(|_, &mut v| v % 2 == 0);
+    hash_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0));
+
+    // Do not lint.
+    hash_map = hash_map
+        .into_iter()
+        .filter(|(x, _)| x % 2 == 0)
+        .collect::<HashMap<i8, i8>>();
+
+    // Do not lint, because this expression is not assign.
+    let mut foobar: HashMap<i8, i8> = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+
+    // Do not lint, because it is an assignment to a different variable.
+    hash_map = foobar.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+}
+
+fn hash_set_retain() {
+    let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]);
+    // Do lint.
+    hash_set.retain(|x| x % 2 == 0);
+    hash_set.retain(|x| x % 2 == 0);
+    hash_set.retain(|x| x % 2 == 0);
+
+    // Do not lint, because type conversion is performed
+    hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect::<HashSet<i8>>();
+    hash_set = hash_set
+        .iter()
+        .filter(|&x| x % 2 == 0)
+        .copied()
+        .collect::<HashSet<i8>>();
+
+    hash_set = hash_set
+        .iter()
+        .filter(|&x| x % 2 == 0)
+        .cloned()
+        .collect::<HashSet<i8>>();
+
+    // Do not lint, because this expression is not assign.
+    let mut bar: HashSet<i8> = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+    let mut foobar: HashSet<i8> = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
+
+    // Do not lint, because it is an assignment to a different variable.
+    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+    bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
+    bar = foobar.into_iter().filter(|&x| x % 2 == 0).collect();
+}
+
+fn string_retain() {
+    let mut s = String::from("foobar");
+    // Do lint.
+    s.retain(|c| c != 'o');
+
+    // Do not lint, because this expression is not assign.
+    let mut bar: String = s.chars().filter(|&c| c != 'o').to_owned().collect();
+
+    // Do not lint, because it is an assignment to a different variable.
+    s = bar.chars().filter(|&c| c != 'o').to_owned().collect();
+}
+
+fn vec_retain() {
+    let mut vec = vec![0, 1, 2];
+    // Do lint.
+    vec.retain(|x| x % 2 == 0);
+    vec.retain(|x| x % 2 == 0);
+    vec.retain(|x| x % 2 == 0);
+
+    // Do not lint, because type conversion is performed
+    vec = vec.into_iter().filter(|x| x % 2 == 0).collect::<Vec<i8>>();
+    vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect::<Vec<i8>>();
+    vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect::<Vec<i8>>();
+
+    // Do not lint, because this expression is not assign.
+    let mut bar: Vec<i8> = vec.iter().filter(|&x| x % 2 == 0).copied().collect();
+    let mut foobar: Vec<i8> = vec.into_iter().filter(|x| x % 2 == 0).collect();
+
+    // Do not lint, because it is an assignment to a different variable.
+    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+    bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
+    bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
+}
+
+fn vec_deque_retain() {
+    let mut vec_deque = VecDeque::new();
+    vec_deque.extend(1..5);
+
+    // Do lint.
+    vec_deque.retain(|x| x % 2 == 0);
+    vec_deque.retain(|x| x % 2 == 0);
+    vec_deque.retain(|x| x % 2 == 0);
+
+    // Do not lint, because type conversion is performed
+    vec_deque = vec_deque
+        .iter()
+        .filter(|&x| x % 2 == 0)
+        .copied()
+        .collect::<VecDeque<i8>>();
+    vec_deque = vec_deque
+        .iter()
+        .filter(|&x| x % 2 == 0)
+        .cloned()
+        .collect::<VecDeque<i8>>();
+    vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect::<VecDeque<i8>>();
+
+    // Do not lint, because this expression is not assign.
+    let mut bar: VecDeque<i8> = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect();
+    let mut foobar: VecDeque<i8> = vec_deque.into_iter().filter(|x| x % 2 == 0).collect();
+
+    // Do not lint, because it is an assignment to a different variable.
+    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+    bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
+    bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
+}
+
+fn _msrv_153() {
+    #![clippy::msrv = "1.52"]
+    let mut btree_map: BTreeMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
+    btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+
+    let mut btree_set = BTreeSet::from([1, 2, 3, 4, 5, 6]);
+    btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+}
+
+fn _msrv_126() {
+    #![clippy::msrv = "1.25"]
+    let mut s = String::from("foobar");
+    s = s.chars().filter(|&c| c != 'o').to_owned().collect();
+}
+
+fn _msrv_118() {
+    #![clippy::msrv = "1.17"]
+    let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]);
+    hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
+    let mut hash_map: HashMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
+    hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+}
diff --git a/src/tools/clippy/tests/ui/manual_retain.rs b/src/tools/clippy/tests/ui/manual_retain.rs
new file mode 100644
index 00000000000..81a849fe768
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_retain.rs
@@ -0,0 +1,246 @@
+// run-rustfix
+#![feature(custom_inner_attributes)]
+#![warn(clippy::manual_retain)]
+#![allow(unused)]
+use std::collections::BTreeMap;
+use std::collections::BTreeSet;
+use std::collections::BinaryHeap;
+use std::collections::HashMap;
+use std::collections::HashSet;
+use std::collections::VecDeque;
+
+fn main() {
+    binary_heap_retain();
+    btree_set_retain();
+    btree_map_retain();
+    hash_set_retain();
+    hash_map_retain();
+    string_retain();
+    vec_deque_retain();
+    vec_retain();
+    _msrv_153();
+    _msrv_126();
+    _msrv_118();
+}
+
+fn binary_heap_retain() {
+    // NOTE: Do not lint now, because binary_heap_retain is nighyly API.
+    // And we need to add a test case for msrv if we update this implmention.
+    // https://github.com/rust-lang/rust/issues/71503
+    let mut heap = BinaryHeap::from([1, 2, 3]);
+    heap = heap.into_iter().filter(|x| x % 2 == 0).collect();
+    heap = heap.iter().filter(|&x| x % 2 == 0).copied().collect();
+    heap = heap.iter().filter(|&x| x % 2 == 0).cloned().collect();
+
+    // Do not lint, because type conversion is performed
+    heap = heap.into_iter().filter(|x| x % 2 == 0).collect::<BinaryHeap<i8>>();
+    heap = heap.iter().filter(|&x| x % 2 == 0).copied().collect::<BinaryHeap<i8>>();
+    heap = heap.iter().filter(|&x| x % 2 == 0).cloned().collect::<BinaryHeap<i8>>();
+
+    // Do not lint, because this expression is not assign.
+    let mut bar: BinaryHeap<i8> = heap.iter().filter(|&x| x % 2 == 0).copied().collect();
+    let mut foobar: BinaryHeap<i8> = heap.into_iter().filter(|x| x % 2 == 0).collect();
+
+    // Do not lint, because it is an assignment to a different variable.
+    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+    bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
+}
+
+fn btree_map_retain() {
+    let mut btree_map: BTreeMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
+    // Do lint.
+    btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+    btree_map = btree_map.into_iter().filter(|(_, v)| v % 2 == 0).collect();
+    btree_map = btree_map
+        .into_iter()
+        .filter(|(k, v)| (k % 2 == 0) && (v % 2 == 0))
+        .collect();
+
+    // Do not lint.
+    btree_map = btree_map
+        .into_iter()
+        .filter(|(x, _)| x % 2 == 0)
+        .collect::<BTreeMap<i8, i8>>();
+
+    // Do not lint, because this expression is not assign.
+    let mut foobar: BTreeMap<i8, i8> = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+
+    // Do not lint, because it is an assignment to a different variable.
+    btree_map = foobar.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+}
+
+fn btree_set_retain() {
+    let mut btree_set = BTreeSet::from([1, 2, 3, 4, 5, 6]);
+
+    // Do lint.
+    btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+    btree_set = btree_set.iter().filter(|&x| x % 2 == 0).cloned().collect();
+    btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect();
+
+    // Do not lint, because type conversion is performed
+    btree_set = btree_set
+        .iter()
+        .filter(|&x| x % 2 == 0)
+        .copied()
+        .collect::<BTreeSet<i8>>();
+
+    btree_set = btree_set
+        .iter()
+        .filter(|&x| x % 2 == 0)
+        .cloned()
+        .collect::<BTreeSet<i8>>();
+
+    btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect::<BTreeSet<i8>>();
+
+    // Do not lint, because this expression is not assign.
+    let mut foobar: BTreeSet<i8> = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+    let mut bar: BTreeSet<i8> = btree_set.into_iter().filter(|x| x % 2 == 0).collect();
+
+    // Do not lint, because it is an assignment to a different variable.
+    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+    bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
+    bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
+}
+
+fn hash_map_retain() {
+    let mut hash_map: HashMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
+    // Do lint.
+    hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+    hash_map = hash_map.into_iter().filter(|(_, v)| v % 2 == 0).collect();
+    hash_map = hash_map
+        .into_iter()
+        .filter(|(k, v)| (k % 2 == 0) && (v % 2 == 0))
+        .collect();
+
+    // Do not lint.
+    hash_map = hash_map
+        .into_iter()
+        .filter(|(x, _)| x % 2 == 0)
+        .collect::<HashMap<i8, i8>>();
+
+    // Do not lint, because this expression is not assign.
+    let mut foobar: HashMap<i8, i8> = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+
+    // Do not lint, because it is an assignment to a different variable.
+    hash_map = foobar.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+}
+
+fn hash_set_retain() {
+    let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]);
+    // Do lint.
+    hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
+    hash_set = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+    hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect();
+
+    // Do not lint, because type conversion is performed
+    hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect::<HashSet<i8>>();
+    hash_set = hash_set
+        .iter()
+        .filter(|&x| x % 2 == 0)
+        .copied()
+        .collect::<HashSet<i8>>();
+
+    hash_set = hash_set
+        .iter()
+        .filter(|&x| x % 2 == 0)
+        .cloned()
+        .collect::<HashSet<i8>>();
+
+    // Do not lint, because this expression is not assign.
+    let mut bar: HashSet<i8> = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+    let mut foobar: HashSet<i8> = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
+
+    // Do not lint, because it is an assignment to a different variable.
+    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+    bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
+    bar = foobar.into_iter().filter(|&x| x % 2 == 0).collect();
+}
+
+fn string_retain() {
+    let mut s = String::from("foobar");
+    // Do lint.
+    s = s.chars().filter(|&c| c != 'o').to_owned().collect();
+
+    // Do not lint, because this expression is not assign.
+    let mut bar: String = s.chars().filter(|&c| c != 'o').to_owned().collect();
+
+    // Do not lint, because it is an assignment to a different variable.
+    s = bar.chars().filter(|&c| c != 'o').to_owned().collect();
+}
+
+fn vec_retain() {
+    let mut vec = vec![0, 1, 2];
+    // Do lint.
+    vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect();
+    vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect();
+    vec = vec.into_iter().filter(|x| x % 2 == 0).collect();
+
+    // Do not lint, because type conversion is performed
+    vec = vec.into_iter().filter(|x| x % 2 == 0).collect::<Vec<i8>>();
+    vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect::<Vec<i8>>();
+    vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect::<Vec<i8>>();
+
+    // Do not lint, because this expression is not assign.
+    let mut bar: Vec<i8> = vec.iter().filter(|&x| x % 2 == 0).copied().collect();
+    let mut foobar: Vec<i8> = vec.into_iter().filter(|x| x % 2 == 0).collect();
+
+    // Do not lint, because it is an assignment to a different variable.
+    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+    bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
+    bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
+}
+
+fn vec_deque_retain() {
+    let mut vec_deque = VecDeque::new();
+    vec_deque.extend(1..5);
+
+    // Do lint.
+    vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect();
+    vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).cloned().collect();
+    vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect();
+
+    // Do not lint, because type conversion is performed
+    vec_deque = vec_deque
+        .iter()
+        .filter(|&x| x % 2 == 0)
+        .copied()
+        .collect::<VecDeque<i8>>();
+    vec_deque = vec_deque
+        .iter()
+        .filter(|&x| x % 2 == 0)
+        .cloned()
+        .collect::<VecDeque<i8>>();
+    vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect::<VecDeque<i8>>();
+
+    // Do not lint, because this expression is not assign.
+    let mut bar: VecDeque<i8> = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect();
+    let mut foobar: VecDeque<i8> = vec_deque.into_iter().filter(|x| x % 2 == 0).collect();
+
+    // Do not lint, because it is an assignment to a different variable.
+    bar = foobar.iter().filter(|&x| x % 2 == 0).copied().collect();
+    bar = foobar.iter().filter(|&x| x % 2 == 0).cloned().collect();
+    bar = foobar.into_iter().filter(|x| x % 2 == 0).collect();
+}
+
+fn _msrv_153() {
+    #![clippy::msrv = "1.52"]
+    let mut btree_map: BTreeMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
+    btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+
+    let mut btree_set = BTreeSet::from([1, 2, 3, 4, 5, 6]);
+    btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+}
+
+fn _msrv_126() {
+    #![clippy::msrv = "1.25"]
+    let mut s = String::from("foobar");
+    s = s.chars().filter(|&c| c != 'o').to_owned().collect();
+}
+
+fn _msrv_118() {
+    #![clippy::msrv = "1.17"]
+    let mut hash_set = HashSet::from([1, 2, 3, 4, 5, 6]);
+    hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
+    let mut hash_map: HashMap<i8, i8> = (0..8).map(|x| (x, x * 10)).collect();
+    hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+}
diff --git a/src/tools/clippy/tests/ui/manual_retain.stderr b/src/tools/clippy/tests/ui/manual_retain.stderr
new file mode 100644
index 00000000000..ec635919b48
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_retain.stderr
@@ -0,0 +1,124 @@
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:52:5
+   |
+LL |     btree_map = btree_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|k, _| k % 2 == 0)`
+   |
+   = note: `-D clippy::manual-retain` implied by `-D warnings`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:53:5
+   |
+LL |     btree_map = btree_map.into_iter().filter(|(_, v)| v % 2 == 0).collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_map.retain(|_, &mut v| v % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:54:5
+   |
+LL | /     btree_map = btree_map
+LL | |         .into_iter()
+LL | |         .filter(|(k, v)| (k % 2 == 0) && (v % 2 == 0))
+LL | |         .collect();
+   | |__________________^ help: consider calling `.retain()` instead: `btree_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:76:5
+   |
+LL |     btree_set = btree_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:77:5
+   |
+LL |     btree_set = btree_set.iter().filter(|&x| x % 2 == 0).cloned().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:78:5
+   |
+LL |     btree_set = btree_set.into_iter().filter(|x| x % 2 == 0).collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `btree_set.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:108:5
+   |
+LL |     hash_map = hash_map.into_iter().filter(|(k, _)| k % 2 == 0).collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|k, _| k % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:109:5
+   |
+LL |     hash_map = hash_map.into_iter().filter(|(_, v)| v % 2 == 0).collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_map.retain(|_, &mut v| v % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:110:5
+   |
+LL | /     hash_map = hash_map
+LL | |         .into_iter()
+LL | |         .filter(|(k, v)| (k % 2 == 0) && (v % 2 == 0))
+LL | |         .collect();
+   | |__________________^ help: consider calling `.retain()` instead: `hash_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0))`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:131:5
+   |
+LL |     hash_set = hash_set.into_iter().filter(|x| x % 2 == 0).collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:132:5
+   |
+LL |     hash_set = hash_set.iter().filter(|&x| x % 2 == 0).copied().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:133:5
+   |
+LL |     hash_set = hash_set.iter().filter(|&x| x % 2 == 0).cloned().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `hash_set.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:162:5
+   |
+LL |     s = s.chars().filter(|&c| c != 'o').to_owned().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `s.retain(|c| c != 'o')`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:174:5
+   |
+LL |     vec = vec.iter().filter(|&x| x % 2 == 0).copied().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:175:5
+   |
+LL |     vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:176:5
+   |
+LL |     vec = vec.into_iter().filter(|x| x % 2 == 0).collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:198:5
+   |
+LL |     vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).copied().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:199:5
+   |
+LL |     vec_deque = vec_deque.iter().filter(|&x| x % 2 == 0).cloned().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:200:5
+   |
+LL |     vec_deque = vec_deque.into_iter().filter(|x| x % 2 == 0).collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec_deque.retain(|x| x % 2 == 0)`
+
+error: aborting due to 19 previous errors
+
diff --git a/src/tools/clippy/tests/ui/methods.rs b/src/tools/clippy/tests/ui/methods.rs
index 9805097084d..1970c2eae53 100644
--- a/src/tools/clippy/tests/ui/methods.rs
+++ b/src/tools/clippy/tests/ui/methods.rs
@@ -15,6 +15,7 @@
     clippy::use_self,
     clippy::useless_format,
     clippy::wrong_self_convention,
+    clippy::unused_async,
     clippy::unused_self,
     unused
 )]
diff --git a/src/tools/clippy/tests/ui/methods.stderr b/src/tools/clippy/tests/ui/methods.stderr
index 6be38b24fbd..b63672dd6fd 100644
--- a/src/tools/clippy/tests/ui/methods.stderr
+++ b/src/tools/clippy/tests/ui/methods.stderr
@@ -1,5 +1,5 @@
 error: methods called `new` usually return `Self`
-  --> $DIR/methods.rs:103:5
+  --> $DIR/methods.rs:104:5
    |
 LL | /     fn new() -> i32 {
 LL | |         0
@@ -9,7 +9,7 @@ LL | |     }
    = note: `-D clippy::new-ret-no-self` implied by `-D warnings`
 
 error: called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling `.find(..)` instead
-  --> $DIR/methods.rs:124:13
+  --> $DIR/methods.rs:125:13
    |
 LL |       let _ = v.iter().filter(|&x| {
    |  _____________^
diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.rs b/src/tools/clippy/tests/ui/min_rust_version_attr.rs
index f83c3e0e281..44e407bd1ab 100644
--- a/src/tools/clippy/tests/ui/min_rust_version_attr.rs
+++ b/src/tools/clippy/tests/ui/min_rust_version_attr.rs
@@ -155,6 +155,11 @@ fn cast_abs_to_unsigned() {
     assert_eq!(10u32, x.abs() as u32);
 }
 
+fn manual_rem_euclid() {
+    let x: i32 = 10;
+    let _: i32 = ((x % 4) + 4) % 4;
+}
+
 fn main() {
     filter_map_next();
     checked_conversion();
@@ -174,6 +179,7 @@ fn main() {
     int_from_bool();
     err_expect();
     cast_abs_to_unsigned();
+    manual_rem_euclid();
 }
 
 mod just_under_msrv {
@@ -211,3 +217,12 @@ mod just_above_msrv {
         }
     }
 }
+
+mod const_rem_euclid {
+    #![feature(custom_inner_attributes)]
+    #![clippy::msrv = "1.50.0"]
+
+    pub const fn const_rem_euclid_4(num: i32) -> i32 {
+        ((num % 4) + 4) % 4
+    }
+}
diff --git a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr
index de225eb740d..b1c23b539ff 100644
--- a/src/tools/clippy/tests/ui/min_rust_version_attr.stderr
+++ b/src/tools/clippy/tests/ui/min_rust_version_attr.stderr
@@ -1,12 +1,12 @@
 error: stripping a prefix manually
-  --> $DIR/min_rust_version_attr.rs:198:24
+  --> $DIR/min_rust_version_attr.rs:204:24
    |
 LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
    |                        ^^^^^^^^^^^^^^^^^^^^
    |
    = note: `-D clippy::manual-strip` implied by `-D warnings`
 note: the prefix was tested here
-  --> $DIR/min_rust_version_attr.rs:197:9
+  --> $DIR/min_rust_version_attr.rs:203:9
    |
 LL |         if s.starts_with("hello, ") {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -17,13 +17,13 @@ LL ~             assert_eq!(<stripped>.to_uppercase(), "WORLD!");
    |
 
 error: stripping a prefix manually
-  --> $DIR/min_rust_version_attr.rs:210:24
+  --> $DIR/min_rust_version_attr.rs:216:24
    |
 LL |             assert_eq!(s["hello, ".len()..].to_uppercase(), "WORLD!");
    |                        ^^^^^^^^^^^^^^^^^^^^
    |
 note: the prefix was tested here
-  --> $DIR/min_rust_version_attr.rs:209:9
+  --> $DIR/min_rust_version_attr.rs:215:9
    |
 LL |         if s.starts_with("hello, ") {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/needless_borrow.fixed b/src/tools/clippy/tests/ui/needless_borrow.fixed
index e7a483c0582..cb005122436 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.fixed
+++ b/src/tools/clippy/tests/ui/needless_borrow.fixed
@@ -62,7 +62,18 @@ fn main() {
         0 => &mut x,
         _ => &mut *x,
     };
-
+    let y: &mut i32 = match 0 {
+        // Lint here. The type given above triggers auto-borrow.
+        0 => x,
+        _ => &mut *x,
+    };
+    fn ref_mut_i32(_: &mut i32) {}
+    ref_mut_i32(match 0 {
+        // Lint here. The type given above triggers auto-borrow.
+        0 => x,
+        _ => &mut *x,
+    });
+    // use 'x' after to make sure it's still usable in the fixed code.
     *x = 5;
 
     let s = String::new();
@@ -74,6 +85,36 @@ fn main() {
     let _ = x.0;
     let x = &x as *const (i32, i32);
     let _ = unsafe { (*x).0 };
+
+    // Issue #8367
+    trait Foo {
+        fn foo(self);
+    }
+    impl Foo for &'_ () {
+        fn foo(self) {}
+    }
+    (&()).foo(); // Don't lint. `()` doesn't implement `Foo`
+    (&()).foo();
+
+    impl Foo for i32 {
+        fn foo(self) {}
+    }
+    impl Foo for &'_ i32 {
+        fn foo(self) {}
+    }
+    (&5).foo(); // Don't lint. `5` will call `<i32 as Foo>::foo`
+    (&5).foo();
+
+    trait FooRef {
+        fn foo_ref(&self);
+    }
+    impl FooRef for () {
+        fn foo_ref(&self) {}
+    }
+    impl FooRef for &'_ () {
+        fn foo_ref(&self) {}
+    }
+    (&&()).foo_ref(); // Don't lint. `&()` will call `<() as FooRef>::foo_ref`
 }
 
 #[allow(clippy::needless_borrowed_reference)]
diff --git a/src/tools/clippy/tests/ui/needless_borrow.rs b/src/tools/clippy/tests/ui/needless_borrow.rs
index 1d6bf46405a..d636a401003 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.rs
+++ b/src/tools/clippy/tests/ui/needless_borrow.rs
@@ -62,7 +62,18 @@ fn main() {
         0 => &mut x,
         _ => &mut *x,
     };
-
+    let y: &mut i32 = match 0 {
+        // Lint here. The type given above triggers auto-borrow.
+        0 => &mut x,
+        _ => &mut *x,
+    };
+    fn ref_mut_i32(_: &mut i32) {}
+    ref_mut_i32(match 0 {
+        // Lint here. The type given above triggers auto-borrow.
+        0 => &mut x,
+        _ => &mut *x,
+    });
+    // use 'x' after to make sure it's still usable in the fixed code.
     *x = 5;
 
     let s = String::new();
@@ -74,6 +85,36 @@ fn main() {
     let _ = (&x).0;
     let x = &x as *const (i32, i32);
     let _ = unsafe { (&*x).0 };
+
+    // Issue #8367
+    trait Foo {
+        fn foo(self);
+    }
+    impl Foo for &'_ () {
+        fn foo(self) {}
+    }
+    (&()).foo(); // Don't lint. `()` doesn't implement `Foo`
+    (&&()).foo();
+
+    impl Foo for i32 {
+        fn foo(self) {}
+    }
+    impl Foo for &'_ i32 {
+        fn foo(self) {}
+    }
+    (&5).foo(); // Don't lint. `5` will call `<i32 as Foo>::foo`
+    (&&5).foo();
+
+    trait FooRef {
+        fn foo_ref(&self);
+    }
+    impl FooRef for () {
+        fn foo_ref(&self) {}
+    }
+    impl FooRef for &'_ () {
+        fn foo_ref(&self) {}
+    }
+    (&&()).foo_ref(); // Don't lint. `&()` will call `<() as FooRef>::foo_ref`
 }
 
 #[allow(clippy::needless_borrowed_reference)]
diff --git a/src/tools/clippy/tests/ui/needless_borrow.stderr b/src/tools/clippy/tests/ui/needless_borrow.stderr
index be59d8f546d..8a2e2b98959 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.stderr
+++ b/src/tools/clippy/tests/ui/needless_borrow.stderr
@@ -84,17 +84,41 @@ error: this expression creates a reference which is immediately dereferenced by
 LL |     let y: &mut i32 = &mut &mut x;
    |                       ^^^^^^^^^^^ help: change this to: `x`
 
+error: this expression creates a reference which is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:67:14
+   |
+LL |         0 => &mut x,
+   |              ^^^^^^ help: change this to: `x`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:73:14
+   |
+LL |         0 => &mut x,
+   |              ^^^^^^ help: change this to: `x`
+
 error: this expression borrows a value the compiler would automatically borrow
-  --> $DIR/needless_borrow.rs:74:13
+  --> $DIR/needless_borrow.rs:85:13
    |
 LL |     let _ = (&x).0;
    |             ^^^^ help: change this to: `x`
 
 error: this expression borrows a value the compiler would automatically borrow
-  --> $DIR/needless_borrow.rs:76:22
+  --> $DIR/needless_borrow.rs:87:22
    |
 LL |     let _ = unsafe { (&*x).0 };
    |                      ^^^^^ help: change this to: `(*x)`
 
-error: aborting due to 16 previous errors
+error: this expression creates a reference which is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:97:5
+   |
+LL |     (&&()).foo();
+   |     ^^^^^^ help: change this to: `(&())`
+
+error: this expression creates a reference which is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:106:5
+   |
+LL |     (&&5).foo();
+   |     ^^^^^ help: change this to: `(&5)`
+
+error: aborting due to 20 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_borrow_pat.rs b/src/tools/clippy/tests/ui/needless_borrow_pat.rs
index 04b6283da3c..222e8e61799 100644
--- a/src/tools/clippy/tests/ui/needless_borrow_pat.rs
+++ b/src/tools/clippy/tests/ui/needless_borrow_pat.rs
@@ -1,7 +1,7 @@
 // FIXME: run-rustfix waiting on multi-span suggestions
 
 #![warn(clippy::needless_borrow)]
-#![allow(clippy::needless_borrowed_reference)]
+#![allow(clippy::needless_borrowed_reference, clippy::explicit_auto_deref)]
 
 fn f1(_: &str) {}
 macro_rules! m1 {
diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed
index 7c828430b78..0bc0d0011ef 100644
--- a/src/tools/clippy/tests/ui/needless_return.fixed
+++ b/src/tools/clippy/tests/ui/needless_return.fixed
@@ -1,5 +1,6 @@
 // run-rustfix
 
+#![feature(lint_reasons)]
 #![feature(let_else)]
 #![allow(unused)]
 #![allow(
@@ -10,6 +11,8 @@
 )]
 #![warn(clippy::needless_return)]
 
+use std::cell::RefCell;
+
 macro_rules! the_answer {
     () => {
         42
@@ -86,17 +89,15 @@ fn test_nested_match(x: u32) {
     }
 }
 
-fn read_line() -> String {
-    use std::io::BufRead;
-    let stdin = ::std::io::stdin();
-    return stdin.lock().lines().next().unwrap().unwrap();
+fn temporary_outlives_local() -> String {
+    let x = RefCell::<String>::default();
+    return x.borrow().clone();
 }
 
 fn borrows_but_not_last(value: bool) -> String {
     if value {
-        use std::io::BufRead;
-        let stdin = ::std::io::stdin();
-        let _a = stdin.lock().lines().next().unwrap().unwrap();
+        let x = RefCell::<String>::default();
+        let _a = x.borrow().clone();
         String::from("test")
     } else {
         String::new()
@@ -197,17 +198,15 @@ async fn async_test_void_match(x: u32) {
     }
 }
 
-async fn async_read_line() -> String {
-    use std::io::BufRead;
-    let stdin = ::std::io::stdin();
-    return stdin.lock().lines().next().unwrap().unwrap();
+async fn async_temporary_outlives_local() -> String {
+    let x = RefCell::<String>::default();
+    return x.borrow().clone();
 }
 
 async fn async_borrows_but_not_last(value: bool) -> String {
     if value {
-        use std::io::BufRead;
-        let stdin = ::std::io::stdin();
-        let _a = stdin.lock().lines().next().unwrap().unwrap();
+        let x = RefCell::<String>::default();
+        let _a = x.borrow().clone();
         String::from("test")
     } else {
         String::new()
@@ -229,4 +228,13 @@ fn needless_return_macro() -> String {
     format!("Hello {}", "world!")
 }
 
+fn check_expect() -> bool {
+    if true {
+        // no error!
+        return true;
+    }
+    #[expect(clippy::needless_return)]
+    return true;
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs
index fe82af00e67..eb9f72e8e78 100644
--- a/src/tools/clippy/tests/ui/needless_return.rs
+++ b/src/tools/clippy/tests/ui/needless_return.rs
@@ -1,5 +1,6 @@
 // run-rustfix
 
+#![feature(lint_reasons)]
 #![feature(let_else)]
 #![allow(unused)]
 #![allow(
@@ -10,6 +11,8 @@
 )]
 #![warn(clippy::needless_return)]
 
+use std::cell::RefCell;
+
 macro_rules! the_answer {
     () => {
         42
@@ -86,17 +89,15 @@ fn test_nested_match(x: u32) {
     }
 }
 
-fn read_line() -> String {
-    use std::io::BufRead;
-    let stdin = ::std::io::stdin();
-    return stdin.lock().lines().next().unwrap().unwrap();
+fn temporary_outlives_local() -> String {
+    let x = RefCell::<String>::default();
+    return x.borrow().clone();
 }
 
 fn borrows_but_not_last(value: bool) -> String {
     if value {
-        use std::io::BufRead;
-        let stdin = ::std::io::stdin();
-        let _a = stdin.lock().lines().next().unwrap().unwrap();
+        let x = RefCell::<String>::default();
+        let _a = x.borrow().clone();
         return String::from("test");
     } else {
         return String::new();
@@ -197,17 +198,15 @@ async fn async_test_void_match(x: u32) {
     }
 }
 
-async fn async_read_line() -> String {
-    use std::io::BufRead;
-    let stdin = ::std::io::stdin();
-    return stdin.lock().lines().next().unwrap().unwrap();
+async fn async_temporary_outlives_local() -> String {
+    let x = RefCell::<String>::default();
+    return x.borrow().clone();
 }
 
 async fn async_borrows_but_not_last(value: bool) -> String {
     if value {
-        use std::io::BufRead;
-        let stdin = ::std::io::stdin();
-        let _a = stdin.lock().lines().next().unwrap().unwrap();
+        let x = RefCell::<String>::default();
+        let _a = x.borrow().clone();
         return String::from("test");
     } else {
         return String::new();
@@ -229,4 +228,13 @@ fn needless_return_macro() -> String {
     return format!("Hello {}", "world!");
 }
 
+fn check_expect() -> bool {
+    if true {
+        // no error!
+        return true;
+    }
+    #[expect(clippy::needless_return)]
+    return true;
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr
index 4c8be47b025..83ff0763869 100644
--- a/src/tools/clippy/tests/ui/needless_return.stderr
+++ b/src/tools/clippy/tests/ui/needless_return.stderr
@@ -1,5 +1,5 @@
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:24:5
+  --> $DIR/needless_return.rs:27:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
@@ -7,217 +7,217 @@ LL |     return true;
    = note: `-D clippy::needless-return` implied by `-D warnings`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:28:5
+  --> $DIR/needless_return.rs:31:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:33:9
+  --> $DIR/needless_return.rs:36:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:35:9
+  --> $DIR/needless_return.rs:38:9
    |
 LL |         return false;
    |         ^^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:41:17
+  --> $DIR/needless_return.rs:44:17
    |
 LL |         true => return false,
    |                 ^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:43:13
+  --> $DIR/needless_return.rs:46:13
    |
 LL |             return true;
    |             ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:50:9
+  --> $DIR/needless_return.rs:53:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:52:16
+  --> $DIR/needless_return.rs:55:16
    |
 LL |     let _ = || return true;
    |                ^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:56:5
+  --> $DIR/needless_return.rs:59:5
    |
 LL |     return the_answer!();
    |     ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:60:5
+  --> $DIR/needless_return.rs:63:5
    |
 LL |     return;
    |     ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:65:9
+  --> $DIR/needless_return.rs:68:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:67:9
+  --> $DIR/needless_return.rs:70:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:74:14
+  --> $DIR/needless_return.rs:77:14
    |
 LL |         _ => return,
    |              ^^^^^^ help: replace `return` with a unit value: `()`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:83:13
+  --> $DIR/needless_return.rs:86:13
    |
 LL |             return;
    |             ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:85:14
+  --> $DIR/needless_return.rs:88:14
    |
 LL |         _ => return,
    |              ^^^^^^ help: replace `return` with a unit value: `()`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:100:9
+  --> $DIR/needless_return.rs:101:9
    |
 LL |         return String::from("test");
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:102:9
+  --> $DIR/needless_return.rs:103:9
    |
 LL |         return String::new();
    |         ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:124:32
+  --> $DIR/needless_return.rs:125:32
    |
 LL |         bar.unwrap_or_else(|_| return)
    |                                ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:129:13
+  --> $DIR/needless_return.rs:130:13
    |
 LL |             return;
    |             ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:131:20
+  --> $DIR/needless_return.rs:132:20
    |
 LL |         let _ = || return;
    |                    ^^^^^^ help: replace `return` with an empty block: `{}`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:137:32
+  --> $DIR/needless_return.rs:138:32
    |
 LL |         res.unwrap_or_else(|_| return Foo)
    |                                ^^^^^^^^^^ help: remove `return`: `Foo`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:146:5
+  --> $DIR/needless_return.rs:147:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:150:5
+  --> $DIR/needless_return.rs:151:5
    |
 LL |     return true;
    |     ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:155:9
+  --> $DIR/needless_return.rs:156:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:157:9
+  --> $DIR/needless_return.rs:158:9
    |
 LL |         return false;
    |         ^^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:163:17
+  --> $DIR/needless_return.rs:164:17
    |
 LL |         true => return false,
    |                 ^^^^^^^^^^^^ help: remove `return`: `false`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:165:13
+  --> $DIR/needless_return.rs:166:13
    |
 LL |             return true;
    |             ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:172:9
+  --> $DIR/needless_return.rs:173:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:174:16
+  --> $DIR/needless_return.rs:175:16
    |
 LL |     let _ = || return true;
    |                ^^^^^^^^^^^ help: remove `return`: `true`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:178:5
+  --> $DIR/needless_return.rs:179:5
    |
 LL |     return the_answer!();
    |     ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `the_answer!()`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:182:5
+  --> $DIR/needless_return.rs:183:5
    |
 LL |     return;
    |     ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:187:9
+  --> $DIR/needless_return.rs:188:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:189:9
+  --> $DIR/needless_return.rs:190:9
    |
 LL |         return;
    |         ^^^^^^^ help: remove `return`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:196:14
+  --> $DIR/needless_return.rs:197:14
    |
 LL |         _ => return,
    |              ^^^^^^ help: replace `return` with a unit value: `()`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:211:9
+  --> $DIR/needless_return.rs:210:9
    |
 LL |         return String::from("test");
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::from("test")`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:213:9
+  --> $DIR/needless_return.rs:212:9
    |
 LL |         return String::new();
    |         ^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `String::new()`
 
 error: unneeded `return` statement
-  --> $DIR/needless_return.rs:229:5
+  --> $DIR/needless_return.rs:228:5
    |
 LL |     return format!("Hello {}", "world!");
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: remove `return`: `format!("Hello {}", "world!")`
diff --git a/src/tools/clippy/tests/ui/neg_multiply.fixed b/src/tools/clippy/tests/ui/neg_multiply.fixed
index 35af9d6ae31..58ab9e85678 100644
--- a/src/tools/clippy/tests/ui/neg_multiply.fixed
+++ b/src/tools/clippy/tests/ui/neg_multiply.fixed
@@ -38,6 +38,9 @@ fn main() {
 
     0xcafe | -0xff00;
 
+    -(3_usize as i32);
+    -(3_usize as i32);
+
     -1 * -1; // should be ok
 
     X * -1; // should be ok
diff --git a/src/tools/clippy/tests/ui/neg_multiply.rs b/src/tools/clippy/tests/ui/neg_multiply.rs
index 7dbdb0906ce..581290dc72e 100644
--- a/src/tools/clippy/tests/ui/neg_multiply.rs
+++ b/src/tools/clippy/tests/ui/neg_multiply.rs
@@ -38,6 +38,9 @@ fn main() {
 
     0xcafe | 0xff00 * -1;
 
+    3_usize as i32 * -1;
+    (3_usize as i32) * -1;
+
     -1 * -1; // should be ok
 
     X * -1; // should be ok
diff --git a/src/tools/clippy/tests/ui/neg_multiply.stderr b/src/tools/clippy/tests/ui/neg_multiply.stderr
index dbf8fb36938..388ef29eb85 100644
--- a/src/tools/clippy/tests/ui/neg_multiply.stderr
+++ b/src/tools/clippy/tests/ui/neg_multiply.stderr
@@ -36,5 +36,17 @@ error: this multiplication by -1 can be written more succinctly
 LL |     0xcafe | 0xff00 * -1;
    |              ^^^^^^^^^^^ help: consider using: `-0xff00`
 
-error: aborting due to 6 previous errors
+error: this multiplication by -1 can be written more succinctly
+  --> $DIR/neg_multiply.rs:41:5
+   |
+LL |     3_usize as i32 * -1;
+   |     ^^^^^^^^^^^^^^^^^^^ help: consider using: `-(3_usize as i32)`
+
+error: this multiplication by -1 can be written more succinctly
+  --> $DIR/neg_multiply.rs:42:5
+   |
+LL |     (3_usize as i32) * -1;
+   |     ^^^^^^^^^^^^^^^^^^^^^ help: consider using: `-(3_usize as i32)`
+
+error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.rs b/src/tools/clippy/tests/ui/nonminimal_bool.rs
index fa5743c1155..24ae62bb058 100644
--- a/src/tools/clippy/tests/ui/nonminimal_bool.rs
+++ b/src/tools/clippy/tests/ui/nonminimal_bool.rs
@@ -1,3 +1,4 @@
+#![feature(lint_reasons)]
 #![allow(unused, clippy::diverging_sub_expression)]
 #![warn(clippy::nonminimal_bool)]
 
@@ -50,3 +51,9 @@ fn issue4548() {
 
     if i != j && f(i, j) != 0 || i == j && f(i, j) != 1 {}
 }
+
+fn check_expect() {
+    let a: bool = unimplemented!();
+    #[expect(clippy::nonminimal_bool)]
+    let _ = !!a;
+}
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.stderr b/src/tools/clippy/tests/ui/nonminimal_bool.stderr
index bb93cbbd5e1..fc6a5ce1dc2 100644
--- a/src/tools/clippy/tests/ui/nonminimal_bool.stderr
+++ b/src/tools/clippy/tests/ui/nonminimal_bool.stderr
@@ -1,5 +1,5 @@
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:10:13
+  --> $DIR/nonminimal_bool.rs:11:13
    |
 LL |     let _ = !true;
    |             ^^^^^ help: try: `false`
@@ -7,43 +7,43 @@ LL |     let _ = !true;
    = note: `-D clippy::nonminimal-bool` implied by `-D warnings`
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:11:13
+  --> $DIR/nonminimal_bool.rs:12:13
    |
 LL |     let _ = !false;
    |             ^^^^^^ help: try: `true`
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:12:13
+  --> $DIR/nonminimal_bool.rs:13:13
    |
 LL |     let _ = !!a;
    |             ^^^ help: try: `a`
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:13:13
+  --> $DIR/nonminimal_bool.rs:14:13
    |
 LL |     let _ = false || a;
    |             ^^^^^^^^^^ help: try: `a`
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:17:13
+  --> $DIR/nonminimal_bool.rs:18:13
    |
 LL |     let _ = !(!a && b);
    |             ^^^^^^^^^^ help: try: `a || !b`
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:18:13
+  --> $DIR/nonminimal_bool.rs:19:13
    |
 LL |     let _ = !(!a || b);
    |             ^^^^^^^^^^ help: try: `a && !b`
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:19:13
+  --> $DIR/nonminimal_bool.rs:20:13
    |
 LL |     let _ = !a && !(b && c);
    |             ^^^^^^^^^^^^^^^ help: try: `!(a || b && c)`
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:27:13
+  --> $DIR/nonminimal_bool.rs:28:13
    |
 LL |     let _ = a == b && c == 5 && a == b;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -56,7 +56,7 @@ LL |     let _ = a == b && c == 5;
    |             ~~~~~~~~~~~~~~~~
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:28:13
+  --> $DIR/nonminimal_bool.rs:29:13
    |
 LL |     let _ = a == b || c == 5 || a == b;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -69,7 +69,7 @@ LL |     let _ = a == b || c == 5;
    |             ~~~~~~~~~~~~~~~~
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:29:13
+  --> $DIR/nonminimal_bool.rs:30:13
    |
 LL |     let _ = a == b && c == 5 && b == a;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -82,7 +82,7 @@ LL |     let _ = a == b && c == 5;
    |             ~~~~~~~~~~~~~~~~
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:30:13
+  --> $DIR/nonminimal_bool.rs:31:13
    |
 LL |     let _ = a != b || !(a != b || c == d);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -95,7 +95,7 @@ LL |     let _ = a != b || c != d;
    |             ~~~~~~~~~~~~~~~~
 
 error: this boolean expression can be simplified
-  --> $DIR/nonminimal_bool.rs:31:13
+  --> $DIR/nonminimal_bool.rs:32:13
    |
 LL |     let _ = a != b && !(a != b && c == d);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/ptr_arg.rs b/src/tools/clippy/tests/ui/ptr_arg.rs
index 814bbc7af71..fd15001e540 100644
--- a/src/tools/clippy/tests/ui/ptr_arg.rs
+++ b/src/tools/clippy/tests/ui/ptr_arg.rs
@@ -1,3 +1,4 @@
+#![feature(lint_reasons)]
 #![allow(unused, clippy::many_single_char_names, clippy::redundant_clone)]
 #![warn(clippy::ptr_arg)]
 
@@ -109,9 +110,12 @@ mod issue_5644 {
         #[allow(clippy::ptr_arg)] _s: &String,
         #[allow(clippy::ptr_arg)] _p: &PathBuf,
         #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
+        #[expect(clippy::ptr_arg)] _expect: &Cow<[i32]>,
     ) {
     }
 
+    fn some_allowed(#[allow(clippy::ptr_arg)] _v: &Vec<u32>, _s: &String) {}
+
     struct S;
     impl S {
         fn allowed(
@@ -119,6 +123,7 @@ mod issue_5644 {
             #[allow(clippy::ptr_arg)] _s: &String,
             #[allow(clippy::ptr_arg)] _p: &PathBuf,
             #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
+            #[expect(clippy::ptr_arg)] _expect: &Cow<[i32]>,
         ) {
         }
     }
@@ -129,6 +134,7 @@ mod issue_5644 {
             #[allow(clippy::ptr_arg)] _s: &String,
             #[allow(clippy::ptr_arg)] _p: &PathBuf,
             #[allow(clippy::ptr_arg)] _c: &Cow<[i32]>,
+            #[expect(clippy::ptr_arg)] _expect: &Cow<[i32]>,
         ) {
         }
     }
diff --git a/src/tools/clippy/tests/ui/ptr_arg.stderr b/src/tools/clippy/tests/ui/ptr_arg.stderr
index 7ec4a566ff3..d64b5f454a5 100644
--- a/src/tools/clippy/tests/ui/ptr_arg.stderr
+++ b/src/tools/clippy/tests/ui/ptr_arg.stderr
@@ -1,5 +1,5 @@
 error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:7:14
+  --> $DIR/ptr_arg.rs:8:14
    |
 LL | fn do_vec(x: &Vec<i64>) {
    |              ^^^^^^^^^ help: change this to: `&[i64]`
@@ -7,43 +7,43 @@ LL | fn do_vec(x: &Vec<i64>) {
    = note: `-D clippy::ptr-arg` implied by `-D warnings`
 
 error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:11:18
+  --> $DIR/ptr_arg.rs:12:18
    |
 LL | fn do_vec_mut(x: &mut Vec<i64>) {
    |                  ^^^^^^^^^^^^^ help: change this to: `&mut [i64]`
 
 error: writing `&String` instead of `&str` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:15:14
+  --> $DIR/ptr_arg.rs:16:14
    |
 LL | fn do_str(x: &String) {
    |              ^^^^^^^ help: change this to: `&str`
 
 error: writing `&mut String` instead of `&mut str` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:19:18
+  --> $DIR/ptr_arg.rs:20:18
    |
 LL | fn do_str_mut(x: &mut String) {
    |                  ^^^^^^^^^^^ help: change this to: `&mut str`
 
 error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:23:15
+  --> $DIR/ptr_arg.rs:24:15
    |
 LL | fn do_path(x: &PathBuf) {
    |               ^^^^^^^^ help: change this to: `&Path`
 
 error: writing `&mut PathBuf` instead of `&mut Path` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:27:19
+  --> $DIR/ptr_arg.rs:28:19
    |
 LL | fn do_path_mut(x: &mut PathBuf) {
    |                   ^^^^^^^^^^^^ help: change this to: `&mut Path`
 
 error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:35:18
+  --> $DIR/ptr_arg.rs:36:18
    |
 LL |     fn do_vec(x: &Vec<i64>);
    |                  ^^^^^^^^^ help: change this to: `&[i64]`
 
 error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:48:14
+  --> $DIR/ptr_arg.rs:49:14
    |
 LL | fn cloned(x: &Vec<u8>) -> Vec<u8> {
    |              ^^^^^^^^
@@ -60,7 +60,7 @@ LL ~     x.to_owned()
    |
 
 error: writing `&String` instead of `&str` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:57:18
+  --> $DIR/ptr_arg.rs:58:18
    |
 LL | fn str_cloned(x: &String) -> String {
    |                  ^^^^^^^
@@ -76,7 +76,7 @@ LL ~     x.to_owned()
    |
 
 error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:65:19
+  --> $DIR/ptr_arg.rs:66:19
    |
 LL | fn path_cloned(x: &PathBuf) -> PathBuf {
    |                   ^^^^^^^^
@@ -92,7 +92,7 @@ LL ~     x.to_path_buf()
    |
 
 error: writing `&String` instead of `&str` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:73:44
+  --> $DIR/ptr_arg.rs:74:44
    |
 LL | fn false_positive_capacity(x: &Vec<u8>, y: &String) {
    |                                            ^^^^^^^
@@ -106,13 +106,19 @@ LL ~     let c = y;
    |
 
 error: using a reference to `Cow` is not recommended
-  --> $DIR/ptr_arg.rs:87:25
+  --> $DIR/ptr_arg.rs:88:25
    |
 LL | fn test_cow_with_ref(c: &Cow<[i32]>) {}
    |                         ^^^^^^^^^^^ help: change this to: `&[i32]`
 
+error: writing `&String` instead of `&str` involves a new object where a slice will do
+  --> $DIR/ptr_arg.rs:117:66
+   |
+LL |     fn some_allowed(#[allow(clippy::ptr_arg)] _v: &Vec<u32>, _s: &String) {}
+   |                                                                  ^^^^^^^ help: change this to: `&str`
+
 error: writing `&Vec` instead of `&[_]` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:140:21
+  --> $DIR/ptr_arg.rs:146:21
    |
 LL |     fn foo_vec(vec: &Vec<u8>) {
    |                     ^^^^^^^^
@@ -125,7 +131,7 @@ LL ~         let _ = vec.to_owned().clone();
    |
 
 error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:145:23
+  --> $DIR/ptr_arg.rs:151:23
    |
 LL |     fn foo_path(path: &PathBuf) {
    |                       ^^^^^^^^
@@ -138,7 +144,7 @@ LL ~         let _ = path.to_path_buf().clone();
    |
 
 error: writing `&PathBuf` instead of `&Path` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:150:21
+  --> $DIR/ptr_arg.rs:156:21
    |
 LL |     fn foo_str(str: &PathBuf) {
    |                     ^^^^^^^^
@@ -151,10 +157,10 @@ LL ~         let _ = str.to_path_buf().clone();
    |
 
 error: writing `&mut Vec` instead of `&mut [_]` involves a new object where a slice will do
-  --> $DIR/ptr_arg.rs:156:29
+  --> $DIR/ptr_arg.rs:162:29
    |
 LL | fn mut_vec_slice_methods(v: &mut Vec<u32>) {
    |                             ^^^^^^^^^^^^^ help: change this to: `&mut [u32]`
 
-error: aborting due to 16 previous errors
+error: aborting due to 17 previous errors
 
diff --git a/src/tools/clippy/tests/ui/redundant_clone.fixed b/src/tools/clippy/tests/ui/redundant_clone.fixed
index 1525f6a93df..da52c0acf93 100644
--- a/src/tools/clippy/tests/ui/redundant_clone.fixed
+++ b/src/tools/clippy/tests/ui/redundant_clone.fixed
@@ -1,6 +1,7 @@
 // run-rustfix
 // rustfix-only-machine-applicable
 
+#![feature(lint_reasons)]
 #![allow(clippy::implicit_clone, clippy::drop_non_drop)]
 use std::ffi::OsString;
 use std::path::Path;
@@ -29,6 +30,10 @@ fn main() {
     #[allow(clippy::redundant_clone)]
     let _s = String::new().to_string();
 
+    // Check that lint level works
+    #[expect(clippy::redundant_clone)]
+    let _s = String::new().to_string();
+
     let tup = (String::from("foo"),);
     let _t = tup.0;
 
diff --git a/src/tools/clippy/tests/ui/redundant_clone.rs b/src/tools/clippy/tests/ui/redundant_clone.rs
index 2f82aefd928..5867d019dbb 100644
--- a/src/tools/clippy/tests/ui/redundant_clone.rs
+++ b/src/tools/clippy/tests/ui/redundant_clone.rs
@@ -1,6 +1,7 @@
 // run-rustfix
 // rustfix-only-machine-applicable
 
+#![feature(lint_reasons)]
 #![allow(clippy::implicit_clone, clippy::drop_non_drop)]
 use std::ffi::OsString;
 use std::path::Path;
@@ -29,6 +30,10 @@ fn main() {
     #[allow(clippy::redundant_clone)]
     let _s = String::new().to_string();
 
+    // Check that lint level works
+    #[expect(clippy::redundant_clone)]
+    let _s = String::new().to_string();
+
     let tup = (String::from("foo"),);
     let _t = tup.0.clone();
 
diff --git a/src/tools/clippy/tests/ui/redundant_clone.stderr b/src/tools/clippy/tests/ui/redundant_clone.stderr
index 9f59017b261..aa1dd7cbb45 100644
--- a/src/tools/clippy/tests/ui/redundant_clone.stderr
+++ b/src/tools/clippy/tests/ui/redundant_clone.stderr
@@ -1,180 +1,180 @@
 error: redundant clone
-  --> $DIR/redundant_clone.rs:9:42
+  --> $DIR/redundant_clone.rs:10:42
    |
 LL |     let _s = ["lorem", "ipsum"].join(" ").to_string();
    |                                          ^^^^^^^^^^^^ help: remove this
    |
    = note: `-D clippy::redundant-clone` implied by `-D warnings`
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:9:14
+  --> $DIR/redundant_clone.rs:10:14
    |
 LL |     let _s = ["lorem", "ipsum"].join(" ").to_string();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:12:15
+  --> $DIR/redundant_clone.rs:13:15
    |
 LL |     let _s = s.clone();
    |               ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:12:14
+  --> $DIR/redundant_clone.rs:13:14
    |
 LL |     let _s = s.clone();
    |              ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:15:15
+  --> $DIR/redundant_clone.rs:16:15
    |
 LL |     let _s = s.to_string();
    |               ^^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:15:14
+  --> $DIR/redundant_clone.rs:16:14
    |
 LL |     let _s = s.to_string();
    |              ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:18:15
+  --> $DIR/redundant_clone.rs:19:15
    |
 LL |     let _s = s.to_owned();
    |               ^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:18:14
+  --> $DIR/redundant_clone.rs:19:14
    |
 LL |     let _s = s.to_owned();
    |              ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:20:42
+  --> $DIR/redundant_clone.rs:21:42
    |
 LL |     let _s = Path::new("/a/b/").join("c").to_owned();
    |                                          ^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:20:14
+  --> $DIR/redundant_clone.rs:21:14
    |
 LL |     let _s = Path::new("/a/b/").join("c").to_owned();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:22:42
+  --> $DIR/redundant_clone.rs:23:42
    |
 LL |     let _s = Path::new("/a/b/").join("c").to_path_buf();
    |                                          ^^^^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:22:14
+  --> $DIR/redundant_clone.rs:23:14
    |
 LL |     let _s = Path::new("/a/b/").join("c").to_path_buf();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:24:29
+  --> $DIR/redundant_clone.rs:25:29
    |
 LL |     let _s = OsString::new().to_owned();
    |                             ^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:24:14
+  --> $DIR/redundant_clone.rs:25:14
    |
 LL |     let _s = OsString::new().to_owned();
    |              ^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:26:29
+  --> $DIR/redundant_clone.rs:27:29
    |
 LL |     let _s = OsString::new().to_os_string();
    |                             ^^^^^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:26:14
+  --> $DIR/redundant_clone.rs:27:14
    |
 LL |     let _s = OsString::new().to_os_string();
    |              ^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:33:19
+  --> $DIR/redundant_clone.rs:38:19
    |
 LL |     let _t = tup.0.clone();
    |                   ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:33:14
+  --> $DIR/redundant_clone.rs:38:14
    |
 LL |     let _t = tup.0.clone();
    |              ^^^^^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:65:25
+  --> $DIR/redundant_clone.rs:70:25
    |
 LL |     if b { (a.clone(), a.clone()) } else { (Alpha, a) }
    |                         ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:65:24
+  --> $DIR/redundant_clone.rs:70:24
    |
 LL |     if b { (a.clone(), a.clone()) } else { (Alpha, a) }
    |                        ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:122:15
+  --> $DIR/redundant_clone.rs:127:15
    |
 LL |     let _s = s.clone();
    |               ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:122:14
+  --> $DIR/redundant_clone.rs:127:14
    |
 LL |     let _s = s.clone();
    |              ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:123:15
+  --> $DIR/redundant_clone.rs:128:15
    |
 LL |     let _t = t.clone();
    |               ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:123:14
+  --> $DIR/redundant_clone.rs:128:14
    |
 LL |     let _t = t.clone();
    |              ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:133:19
+  --> $DIR/redundant_clone.rs:138:19
    |
 LL |         let _f = f.clone();
    |                   ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:133:18
+  --> $DIR/redundant_clone.rs:138:18
    |
 LL |         let _f = f.clone();
    |                  ^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:145:14
+  --> $DIR/redundant_clone.rs:150:14
    |
 LL |     let y = x.clone().join("matthias");
    |              ^^^^^^^^ help: remove this
    |
 note: cloned value is neither consumed nor mutated
-  --> $DIR/redundant_clone.rs:145:13
+  --> $DIR/redundant_clone.rs:150:13
    |
 LL |     let y = x.clone().join("matthias");
    |             ^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/redundant_clone.rs:199:11
+  --> $DIR/redundant_clone.rs:204:11
    |
 LL |     foo(&x.clone(), move || {
    |           ^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/redundant_clone.rs:199:10
+  --> $DIR/redundant_clone.rs:204:10
    |
 LL |     foo(&x.clone(), move || {
    |          ^
diff --git a/src/tools/clippy/tests/ui/ref_binding_to_reference.rs b/src/tools/clippy/tests/ui/ref_binding_to_reference.rs
index 570ef406e4a..c8d0e56b197 100644
--- a/src/tools/clippy/tests/ui/ref_binding_to_reference.rs
+++ b/src/tools/clippy/tests/ui/ref_binding_to_reference.rs
@@ -2,7 +2,7 @@
 
 #![feature(lint_reasons)]
 #![warn(clippy::ref_binding_to_reference)]
-#![allow(clippy::needless_borrowed_reference)]
+#![allow(clippy::needless_borrowed_reference, clippy::explicit_auto_deref)]
 
 fn f1(_: &str) {}
 macro_rules! m2 {
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed
index 6831fb2cf59..5190c5304c7 100644
--- a/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.fixed
@@ -1,5 +1,5 @@
 // run-rustfix
-#![allow(dead_code)]
+#![allow(dead_code, clippy::explicit_auto_deref)]
 #![warn(clippy::search_is_some)]
 
 fn main() {
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs
index 767518ab0c0..310d87333a9 100644
--- a/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_none.rs
@@ -1,5 +1,5 @@
 // run-rustfix
-#![allow(dead_code)]
+#![allow(dead_code, clippy::explicit_auto_deref)]
 #![warn(clippy::search_is_some)]
 
 fn main() {
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed b/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed
index 7c940a2b069..5a2aee465d1 100644
--- a/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.fixed
@@ -1,5 +1,5 @@
 // run-rustfix
-#![allow(dead_code)]
+#![allow(dead_code, clippy::explicit_auto_deref)]
 #![warn(clippy::search_is_some)]
 
 fn main() {
diff --git a/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs b/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs
index 77fd52e4ce7..0e98ae18a21 100644
--- a/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs
+++ b/src/tools/clippy/tests/ui/search_is_some_fixable_some.rs
@@ -1,5 +1,5 @@
 // run-rustfix
-#![allow(dead_code)]
+#![allow(dead_code, clippy::explicit_auto_deref)]
 #![warn(clippy::search_is_some)]
 
 fn main() {
diff --git a/src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs b/src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs
index 1ccb0a1d167..50999c6f219 100644
--- a/src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs
+++ b/src/tools/clippy/tests/ui/should_impl_trait/corner_cases.rs
@@ -8,7 +8,8 @@
     clippy::missing_safety_doc,
     clippy::wrong_self_convention,
     clippy::missing_panics_doc,
-    clippy::return_self_not_must_use
+    clippy::return_self_not_must_use,
+    clippy::unused_async
 )]
 
 use std::ops::Mul;
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 4347610f393..185e5009b60 100644
--- a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs
+++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs
@@ -64,6 +64,19 @@ fn should_trigger_lint_with_mutex_guard_in_match_scrutinee() {
     };
 }
 
+fn should_not_trigger_lint_with_mutex_guard_in_match_scrutinee_when_lint_allowed() {
+    let mutex = Mutex::new(State {});
+
+    // Lint should not be triggered because it is "allowed" below.
+    #[allow(clippy::significant_drop_in_scrutinee)]
+    match mutex.lock().unwrap().foo() {
+        true => {
+            mutex.lock().unwrap().bar();
+        },
+        false => {},
+    };
+}
+
 fn should_not_trigger_lint_for_insignificant_drop() {
     // Should not trigger lint because there are no temporaries whose drops have a significant
     // side effect.
@@ -591,4 +604,20 @@ fn should_trigger_lint_for_read_write_lock_for_loop() {
     }
 }
 
+fn do_bar(mutex: &Mutex<State>) {
+    mutex.lock().unwrap().bar();
+}
+
+fn should_trigger_lint_without_significant_drop_in_arm() {
+    let mutex = Mutex::new(State {});
+
+    // Should trigger lint because the lifetime of the temporary MutexGuard is surprising because it
+    // is preserved until the end of the match, but there is no clear indication that this is the
+    // case.
+    match mutex.lock().unwrap().foo() {
+        true => do_bar(&mutex),
+        false => {},
+    };
+}
+
 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 5ce95204416..88ea6bce25b 100644
--- a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr
+++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr
@@ -1,174 +1,290 @@
-error: temporary with significant drop in match scrutinee
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
   --> $DIR/significant_drop_in_scrutinee.rs:59:11
    |
 LL |     match mutex.lock().unwrap().foo() {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         true => {
+LL |             mutex.lock().unwrap().bar();
+   |             --------------------- another value with significant `Drop` created here
+...
+LL |     };
+   |      - temporary lives until here
    |
    = note: `-D clippy::significant-drop-in-scrutinee` implied by `-D warnings`
+   = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
 LL ~     let value = mutex.lock().unwrap().foo();
 LL ~     match value {
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:132:11
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:145:11
    |
 LL |     match s.lock_m().get_the_value() {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
+...
+LL |             println!("{}", s.lock_m().get_the_value());
+   |                            ---------- another value with significant `Drop` created here
+...
+LL |     }
+   |      - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
 LL ~     let value = s.lock_m().get_the_value();
 LL ~     match value {
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:153:11
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:166:11
    |
 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 |     }
+   |      - 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 = s.lock_m_m().get_the_value();
 LL ~     match value {
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:201:11
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:214:11
    |
 LL |     match counter.temp_increment().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 = counter.temp_increment().len();
 LL ~     match value {
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:224:16
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:237:16
    |
 LL |         match (mutex1.lock().unwrap().s.len(), true) {
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
+...
+LL |                 mutex1.lock().unwrap().s.len();
+   |                 ---------------------- another value with significant `Drop` created here
+...
+LL |         };
+   |          - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
 LL ~         let value = mutex1.lock().unwrap().s.len();
 LL ~         match (value, true) {
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:233:22
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:246:22
    |
 LL |         match (true, mutex1.lock().unwrap().s.len(), true) {
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
+...
+LL |                 mutex1.lock().unwrap().s.len();
+   |                 ---------------------- another value with significant `Drop` created here
+...
+LL |         };
+   |          - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
 LL ~         let value = mutex1.lock().unwrap().s.len();
 LL ~         match (true, value, true) {
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:243:16
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:256:16
    |
 LL |         match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) {
    |                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
+...
+LL |                 mutex1.lock().unwrap().s.len();
+   |                 ---------------------- another value with significant `Drop` created here
+LL |                 mutex2.lock().unwrap().s.len();
+   |                 ---------------------- another value with significant `Drop` created here
+...
+LL |         };
+   |          - 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 = mutex1.lock().unwrap().s.len();
 LL ~         match (value, true, mutex2.lock().unwrap().s.len()) {
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:243:54
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:256:54
    |
 LL |         match (mutex1.lock().unwrap().s.len(), true, mutex2.lock().unwrap().s.len()) {
    |                                                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
+...
+LL |                 mutex1.lock().unwrap().s.len();
+   |                 ---------------------- another value with significant `Drop` created here
+LL |                 mutex2.lock().unwrap().s.len();
+   |                 ---------------------- another value with significant `Drop` created here
+...
+LL |         };
+   |          - 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 = mutex2.lock().unwrap().s.len();
 LL ~         match (mutex1.lock().unwrap().s.len(), true, value) {
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:254:15
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:267:15
    |
 LL |         match mutex3.lock().unwrap().s.as_str() {
    |               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL |                 mutex1.lock().unwrap().s.len();
+   |                 ---------------------- another value with significant `Drop` created here
+LL |                 mutex2.lock().unwrap().s.len();
+   |                 ---------------------- another value with significant `Drop` created here
+...
+LL |         };
+   |          - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:264:22
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:277:22
    |
 LL |         match (true, mutex3.lock().unwrap().s.as_str()) {
    |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL |                 mutex1.lock().unwrap().s.len();
+   |                 ---------------------- another value with significant `Drop` created here
+LL |                 mutex2.lock().unwrap().s.len();
+   |                 ---------------------- another value with significant `Drop` created here
+...
+LL |         };
+   |          - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:283:11
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:296:11
    |
 LL |     match mutex.lock().unwrap().s.len() > 1 {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
+LL |         true => {
+LL |             mutex.lock().unwrap().s.len();
+   |             --------------------- another value with significant `Drop` created here
+...
+LL |     };
+   |      - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
 LL ~     let value = mutex.lock().unwrap().s.len() > 1;
 LL ~     match value {
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:290:11
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:303:11
    |
 LL |     match 1 < mutex.lock().unwrap().s.len() {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
+LL |         true => {
+LL |             mutex.lock().unwrap().s.len();
+   |             --------------------- another value with significant `Drop` created here
+...
+LL |     };
+   |      - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
 LL ~     let value = 1 < mutex.lock().unwrap().s.len();
 LL ~     match value {
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:308:11
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:321:11
    |
 LL |     match mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len() {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
+...
+LL |                 mutex1.lock().unwrap().s.len(),
+   |                 ---------------------- another value with significant `Drop` created here
+LL |                 mutex2.lock().unwrap().s.len()
+   |                 ---------------------- another value with significant `Drop` created here
+...
+LL |     };
+   |      - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
 LL ~     let value = mutex1.lock().unwrap().s.len() < mutex2.lock().unwrap().s.len();
 LL ~     match value {
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:319:11
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:332:11
    |
 LL |     match mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len() {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
+...
+LL |                 mutex1.lock().unwrap().s.len(),
+   |                 ---------------------- another value with significant `Drop` created here
+LL |                 mutex2.lock().unwrap().s.len()
+   |                 ---------------------- another value with significant `Drop` created here
+...
+LL |     };
+   |      - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
 LL ~     let value = mutex1.lock().unwrap().s.len() >= mutex2.lock().unwrap().s.len();
 LL ~     match value {
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:354:11
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:367:11
    |
 LL |     match get_mutex_guard().s.len() > 1 {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
+LL |         true => {
+LL |             mutex1.lock().unwrap().s.len();
+   |             ---------------------- another value with significant `Drop` created here
+...
+LL |     };
+   |      - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
 LL ~     let value = get_mutex_guard().s.len() > 1;
 LL ~     match value {
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:371:11
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:384:11
    |
 LL |       match match i {
    |  ___________^
@@ -179,7 +295,14 @@ LL | |     .s
 LL | |     .len()
 LL | |         > 1
    | |___________^
-   |
+...
+LL |               mutex1.lock().unwrap().s.len();
+   |               ---------------------- another value with significant `Drop` created here
+...
+LL |       };
+   |        - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
 LL ~     let value = match i {
@@ -192,8 +315,8 @@ LL +         > 1;
 LL ~     match value
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:397:11
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:410:11
    |
 LL |       match if i > 1 {
    |  ___________^
@@ -204,7 +327,14 @@ LL | |         mutex2.lock().unwrap()
 LL | |     .len()
 LL | |         > 1
    | |___________^
-   |
+...
+LL |               mutex1.lock().unwrap().s.len();
+   |               ---------------------- another value with significant `Drop` created here
+...
+LL |       };
+   |        - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
 LL ~     let value = if i > 1 {
@@ -218,83 +348,150 @@ LL +         > 1;
 LL ~     match value
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:451:11
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:464:11
    |
 LL |     match s.lock().deref().deref() {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         0 | 1 => println!("Value was less than 2"),
+LL |         _ => println!("Value is {}", s.lock().deref()),
+   |                                      ---------------- another value with significant `Drop` created here
+LL |     };
+   |      - temporary lives until here
    |
+   = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match and create a copy
    |
 LL ~     let value = *s.lock().deref().deref();
 LL ~     match value {
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:479:11
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:492:11
    |
 LL |     match s.lock().deref().deref() {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         matcher => println!("Value is {}", s.lock().deref()),
+   |                                            ---------------- another value with significant `Drop` created here
+LL |         _ => println!("Value was not a match"),
+LL |     };
+   |      - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:498:11
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:511:11
    |
 LL |     match mutex.lock().unwrap().i = i {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
+LL |         _ => {
+LL |             println!("{}", mutex.lock().unwrap().i);
+   |                            --------------------- another value with significant `Drop` created here
+LL |         },
+LL |     };
+   |      - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
 LL ~     mutex.lock().unwrap().i = i;
 LL ~     match () {
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:504:11
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:517:11
    |
 LL |     match i = mutex.lock().unwrap().i {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
+LL |         _ => {
+LL |             println!("{}", mutex.lock().unwrap().i);
+   |                            --------------------- another value with significant `Drop` created here
+LL |         },
+LL |     };
+   |      - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
 LL ~     i = mutex.lock().unwrap().i;
 LL ~     match () {
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:510:11
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:523:11
    |
 LL |     match mutex.lock().unwrap().i += 1 {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
+LL |         _ => {
+LL |             println!("{}", mutex.lock().unwrap().i);
+   |                            --------------------- another value with significant `Drop` created here
+LL |         },
+LL |     };
+   |      - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
 LL ~     mutex.lock().unwrap().i += 1;
 LL ~     match () {
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:516:11
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:529:11
    |
 LL |     match i += mutex.lock().unwrap().i {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
+LL |         _ => {
+LL |             println!("{}", mutex.lock().unwrap().i);
+   |                            --------------------- another value with significant `Drop` created here
+LL |         },
+LL |     };
+   |      - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
 help: try moving the temporary above the match
    |
 LL ~     i += mutex.lock().unwrap().i;
 LL ~     match () {
    |
 
-error: temporary with significant drop in match scrutinee
-  --> $DIR/significant_drop_in_scrutinee.rs:579:11
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:592:11
    |
 LL |     match rwlock.read().unwrap().to_number() {
    |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL |     };
+   |      - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
 
-error: temporary with significant drop in for loop
-  --> $DIR/significant_drop_in_scrutinee.rs:589:14
+error: temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:602:14
    |
 LL |     for s in rwlock.read().unwrap().iter() {
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |         println!("{}", s);
+LL |     }
+   |      - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
+
+error: temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression
+  --> $DIR/significant_drop_in_scrutinee.rs:617:11
+   |
+LL |     match mutex.lock().unwrap().foo() {
+   |           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL |     };
+   |      - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+   |
+LL ~     let value = mutex.lock().unwrap().foo();
+LL ~     match value {
+   |
 
-error: aborting due to 25 previous errors
+error: aborting due to 26 previous errors
 
diff --git a/src/tools/clippy/tests/ui/single_match.stderr b/src/tools/clippy/tests/ui/single_match.stderr
index 318faf25717..4d2b9ec5f90 100644
--- a/src/tools/clippy/tests/ui/single_match.stderr
+++ b/src/tools/clippy/tests/ui/single_match.stderr
@@ -39,6 +39,15 @@ LL | |     };
    | |_____^ help: try this: `if let (2..=3, 7..=9) = z { dummy() }`
 
 error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+  --> $DIR/single_match.rs:54:5
+   |
+LL | /     match x {
+LL | |         Some(y) => dummy(),
+LL | |         None => (),
+LL | |     };
+   | |_____^ help: try this: `if let Some(y) = x { dummy() }`
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
   --> $DIR/single_match.rs:59:5
    |
 LL | /     match y {
@@ -146,5 +155,5 @@ LL | |         (..) => {},
 LL | |     }
    | |_____^ help: try this: `if let (.., Some(E::V), _) = (Some(42), Some(E::V), Some(42)) {}`
 
-error: aborting due to 15 previous errors
+error: aborting due to 16 previous errors
 
diff --git a/src/tools/clippy/tests/ui/single_match_else.rs b/src/tools/clippy/tests/ui/single_match_else.rs
index 82387f3d80b..70d6febb71f 100644
--- a/src/tools/clippy/tests/ui/single_match_else.rs
+++ b/src/tools/clippy/tests/ui/single_match_else.rs
@@ -97,4 +97,23 @@ fn main() {
             return;
         },
     }
+
+    // lint here
+    use std::convert::Infallible;
+    match Result::<i32, Infallible>::Ok(1) {
+        Ok(a) => println!("${:?}", a),
+        Err(_) => {
+            println!("else block");
+            return;
+        }
+    }
+
+    use std::borrow::Cow;
+    match Cow::from("moo") {
+        Cow::Owned(a) => println!("${:?}", a),
+        Cow::Borrowed(_) => {
+            println!("else block");
+            return;
+        }
+    }
 }
diff --git a/src/tools/clippy/tests/ui/single_match_else.stderr b/src/tools/clippy/tests/ui/single_match_else.stderr
index 7756c6f204e..38fd9c6a678 100644
--- a/src/tools/clippy/tests/ui/single_match_else.stderr
+++ b/src/tools/clippy/tests/ui/single_match_else.stderr
@@ -20,5 +20,85 @@ LL +         None
 LL ~     };
    |
 
-error: aborting due to previous error
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+  --> $DIR/single_match_else.rs:84:5
+   |
+LL | /     match Some(1) {
+LL | |         Some(a) => println!("${:?}", a),
+LL | |         None => {
+LL | |             println!("else block");
+LL | |             return
+LL | |         },
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL ~     if let Some(a) = Some(1) { println!("${:?}", a) } else {
+LL +         println!("else block");
+LL +         return
+LL +     }
+   |
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+  --> $DIR/single_match_else.rs:93:5
+   |
+LL | /     match Some(1) {
+LL | |         Some(a) => println!("${:?}", a),
+LL | |         None => {
+LL | |             println!("else block");
+LL | |             return;
+LL | |         },
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL ~     if let Some(a) = Some(1) { println!("${:?}", a) } else {
+LL +         println!("else block");
+LL +         return;
+LL +     }
+   |
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+  --> $DIR/single_match_else.rs:103:5
+   |
+LL | /     match Result::<i32, Infallible>::Ok(1) {
+LL | |         Ok(a) => println!("${:?}", a),
+LL | |         Err(_) => {
+LL | |             println!("else block");
+LL | |             return;
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL ~     if let Ok(a) = Result::<i32, Infallible>::Ok(1) { println!("${:?}", a) } else {
+LL +         println!("else block");
+LL +         return;
+LL +     }
+   |
+
+error: you seem to be trying to use `match` for destructuring a single pattern. Consider using `if let`
+  --> $DIR/single_match_else.rs:112:5
+   |
+LL | /     match Cow::from("moo") {
+LL | |         Cow::Owned(a) => println!("${:?}", a),
+LL | |         Cow::Borrowed(_) => {
+LL | |             println!("else block");
+LL | |             return;
+LL | |         }
+LL | |     }
+   | |_____^
+   |
+help: try this
+   |
+LL ~     if let Cow::Owned(a) = Cow::from("moo") { println!("${:?}", a) } else {
+LL +         println!("else block");
+LL +         return;
+LL +     }
+   |
+
+error: aborting due to 5 previous errors
 
diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.rs b/src/tools/clippy/tests/ui/slow_vector_initialization.rs
index 7e3d357ae50..16be9f6d203 100644
--- a/src/tools/clippy/tests/ui/slow_vector_initialization.rs
+++ b/src/tools/clippy/tests/ui/slow_vector_initialization.rs
@@ -19,6 +19,9 @@ fn extend_vector() {
     // Extend with mismatching expression should not be warned
     let mut vec3 = Vec::with_capacity(24322);
     vec3.extend(repeat(0).take(2));
+
+    let mut vec4 = Vec::with_capacity(len);
+    vec4.extend(repeat(0).take(vec4.capacity()));
 }
 
 fn mixed_extend_resize_vector() {
@@ -48,6 +51,9 @@ fn resize_vector() {
     let mut vec3 = Vec::with_capacity(len - 10);
     vec3.resize(len - 10, 0);
 
+    let mut vec4 = Vec::with_capacity(len);
+    vec4.resize(vec4.capacity(), 0);
+
     // Reinitialization should be warned
     vec1 = Vec::with_capacity(10);
     vec1.resize(10, 0);
diff --git a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr
index 5d2788ec260..cb3ce3e95a7 100644
--- a/src/tools/clippy/tests/ui/slow_vector_initialization.stderr
+++ b/src/tools/clippy/tests/ui/slow_vector_initialization.stderr
@@ -17,7 +17,15 @@ LL |     vec2.extend(repeat(0).take(len - 10));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: slow zero-filling initialization
-  --> $DIR/slow_vector_initialization.rs:31:5
+  --> $DIR/slow_vector_initialization.rs:24:5
+   |
+LL |     let mut vec4 = Vec::with_capacity(len);
+   |                    ----------------------- help: consider replace allocation with: `vec![0; len]`
+LL |     vec4.extend(repeat(0).take(vec4.capacity()));
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: slow zero-filling initialization
+  --> $DIR/slow_vector_initialization.rs:34:5
    |
 LL |     let mut resized_vec = Vec::with_capacity(30);
    |                           ---------------------- help: consider replace allocation with: `vec![0; 30]`
@@ -25,7 +33,7 @@ LL |     resized_vec.resize(30, 0);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: slow zero-filling initialization
-  --> $DIR/slow_vector_initialization.rs:34:5
+  --> $DIR/slow_vector_initialization.rs:37:5
    |
 LL |     let mut extend_vec = Vec::with_capacity(30);
    |                          ---------------------- help: consider replace allocation with: `vec![0; 30]`
@@ -33,7 +41,7 @@ LL |     extend_vec.extend(repeat(0).take(30));
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: slow zero-filling initialization
-  --> $DIR/slow_vector_initialization.rs:41:5
+  --> $DIR/slow_vector_initialization.rs:44:5
    |
 LL |     let mut vec1 = Vec::with_capacity(len);
    |                    ----------------------- help: consider replace allocation with: `vec![0; len]`
@@ -41,7 +49,7 @@ LL |     vec1.resize(len, 0);
    |     ^^^^^^^^^^^^^^^^^^^
 
 error: slow zero-filling initialization
-  --> $DIR/slow_vector_initialization.rs:49:5
+  --> $DIR/slow_vector_initialization.rs:52:5
    |
 LL |     let mut vec3 = Vec::with_capacity(len - 10);
    |                    ---------------------------- help: consider replace allocation with: `vec![0; len - 10]`
@@ -49,12 +57,20 @@ LL |     vec3.resize(len - 10, 0);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: slow zero-filling initialization
-  --> $DIR/slow_vector_initialization.rs:53:5
+  --> $DIR/slow_vector_initialization.rs:55:5
+   |
+LL |     let mut vec4 = Vec::with_capacity(len);
+   |                    ----------------------- help: consider replace allocation with: `vec![0; len]`
+LL |     vec4.resize(vec4.capacity(), 0);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: slow zero-filling initialization
+  --> $DIR/slow_vector_initialization.rs:59:5
    |
 LL |     vec1 = Vec::with_capacity(10);
    |            ---------------------- help: consider replace allocation with: `vec![0; 10]`
 LL |     vec1.resize(10, 0);
    |     ^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 7 previous errors
+error: aborting due to 9 previous errors
 
diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed
new file mode 100644
index 00000000000..e5fe9133f97
--- /dev/null
+++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.fixed
@@ -0,0 +1,78 @@
+// run-rustfix
+
+#![feature(custom_inner_attributes)]
+#![warn(clippy::transmute_ptr_to_ref)]
+#![allow(clippy::match_single_binding)]
+
+unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
+    let _: &T = &*p;
+    let _: &T = &*p;
+
+    let _: &mut T = &mut *m;
+    let _: &mut T = &mut *m;
+
+    let _: &T = &*m;
+    let _: &T = &*m;
+
+    let _: &mut T = &mut *(p as *mut T);
+    let _ = &mut *(p as *mut T);
+
+    let _: &T = &*(o as *const T);
+    let _: &T = &*(o as *const T);
+
+    let _: &mut T = &mut *(om as *mut T);
+    let _: &mut T = &mut *(om as *mut T);
+
+    let _: &T = &*(om as *const T);
+    let _: &T = &*(om as *const T);
+}
+
+fn _issue1231() {
+    struct Foo<'a, T> {
+        bar: &'a T,
+    }
+
+    let raw = 42 as *const i32;
+    let _: &Foo<u8> = unsafe { &*raw.cast::<Foo<_>>() };
+
+    let _: &Foo<&u8> = unsafe { &*raw.cast::<Foo<&_>>() };
+
+    type Bar<'a> = &'a u8;
+    let raw = 42 as *const i32;
+    unsafe { &*(raw as *const u8) };
+}
+
+unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 {
+    match 0 {
+        0 => &*x.cast::<&u32>(),
+        1 => &*y.cast::<&u32>(),
+        2 => &*x.cast::<&'b u32>(),
+        _ => &*y.cast::<&'b u32>(),
+    }
+}
+
+unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
+    #![clippy::msrv = "1.38"]
+    let a = 0u32;
+    let a = &a as *const u32;
+    let _: &u32 = &*a;
+    let _: &u32 = &*a.cast::<u32>();
+    match 0 {
+        0 => &*x.cast::<&u32>(),
+        _ => &*x.cast::<&'b u32>(),
+    }
+}
+
+unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
+    #![clippy::msrv = "1.37"]
+    let a = 0u32;
+    let a = &a as *const u32;
+    let _: &u32 = &*a;
+    let _: &u32 = &*(a as *const u32);
+    match 0 {
+        0 => &*(x as *const () as *const &u32),
+        _ => &*(x as *const () as *const &'b u32),
+    }
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs
index ba35c6adc4d..fe49cdc324f 100644
--- a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs
+++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.rs
@@ -1,4 +1,8 @@
+// run-rustfix
+
+#![feature(custom_inner_attributes)]
 #![warn(clippy::transmute_ptr_to_ref)]
+#![allow(clippy::match_single_binding)]
 
 unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
     let _: &T = std::mem::transmute(p);
@@ -23,7 +27,7 @@ unsafe fn _ptr_to_ref<T, U>(p: *const T, m: *mut T, o: *const U, om: *mut U) {
     let _: &T = &*(om as *const T);
 }
 
-fn issue1231() {
+fn _issue1231() {
     struct Foo<'a, T> {
         bar: &'a T,
     }
@@ -38,4 +42,37 @@ fn issue1231() {
     unsafe { std::mem::transmute::<_, Bar>(raw) };
 }
 
+unsafe fn _issue8924<'a, 'b, 'c>(x: *const &'a u32, y: *const &'b u32) -> &'c &'b u32 {
+    match 0 {
+        0 => std::mem::transmute(x),
+        1 => std::mem::transmute(y),
+        2 => std::mem::transmute::<_, &&'b u32>(x),
+        _ => std::mem::transmute::<_, &&'b u32>(y),
+    }
+}
+
+unsafe fn _meets_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
+    #![clippy::msrv = "1.38"]
+    let a = 0u32;
+    let a = &a as *const u32;
+    let _: &u32 = std::mem::transmute(a);
+    let _: &u32 = std::mem::transmute::<_, &u32>(a);
+    match 0 {
+        0 => std::mem::transmute(x),
+        _ => std::mem::transmute::<_, &&'b u32>(x),
+    }
+}
+
+unsafe fn _under_msrv<'a, 'b, 'c>(x: *const &'a u32) -> &'c &'b u32 {
+    #![clippy::msrv = "1.37"]
+    let a = 0u32;
+    let a = &a as *const u32;
+    let _: &u32 = std::mem::transmute(a);
+    let _: &u32 = std::mem::transmute::<_, &u32>(a);
+    match 0 {
+        0 => std::mem::transmute(x),
+        _ => std::mem::transmute::<_, &&'b u32>(x),
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr
index df0598a58cd..2993e5e7b0c 100644
--- a/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr
+++ b/src/tools/clippy/tests/ui/transmute_ptr_to_ref.stderr
@@ -1,5 +1,5 @@
 error: transmute from a pointer type (`*const T`) to a reference type (`&T`)
-  --> $DIR/transmute_ptr_to_ref.rs:4:17
+  --> $DIR/transmute_ptr_to_ref.rs:8:17
    |
 LL |     let _: &T = std::mem::transmute(p);
    |                 ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*p`
@@ -7,58 +7,130 @@ LL |     let _: &T = std::mem::transmute(p);
    = note: `-D clippy::transmute-ptr-to-ref` implied by `-D warnings`
 
 error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`)
-  --> $DIR/transmute_ptr_to_ref.rs:7:21
+  --> $DIR/transmute_ptr_to_ref.rs:11:21
    |
 LL |     let _: &mut T = std::mem::transmute(m);
    |                     ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *m`
 
 error: transmute from a pointer type (`*mut T`) to a reference type (`&T`)
-  --> $DIR/transmute_ptr_to_ref.rs:10:17
+  --> $DIR/transmute_ptr_to_ref.rs:14:17
    |
 LL |     let _: &T = std::mem::transmute(m);
    |                 ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*m`
 
 error: transmute from a pointer type (`*mut T`) to a reference type (`&mut T`)
-  --> $DIR/transmute_ptr_to_ref.rs:13:21
+  --> $DIR/transmute_ptr_to_ref.rs:17:21
    |
 LL |     let _: &mut T = std::mem::transmute(p as *mut T);
    |                     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(p as *mut T)`
 
 error: transmute from a pointer type (`*const U`) to a reference type (`&T`)
-  --> $DIR/transmute_ptr_to_ref.rs:16:17
+  --> $DIR/transmute_ptr_to_ref.rs:20:17
    |
 LL |     let _: &T = std::mem::transmute(o);
    |                 ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(o as *const T)`
 
 error: transmute from a pointer type (`*mut U`) to a reference type (`&mut T`)
-  --> $DIR/transmute_ptr_to_ref.rs:19:21
+  --> $DIR/transmute_ptr_to_ref.rs:23:21
    |
 LL |     let _: &mut T = std::mem::transmute(om);
    |                     ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&mut *(om as *mut T)`
 
 error: transmute from a pointer type (`*mut U`) to a reference type (`&T`)
-  --> $DIR/transmute_ptr_to_ref.rs:22:17
+  --> $DIR/transmute_ptr_to_ref.rs:26:17
    |
 LL |     let _: &T = std::mem::transmute(om);
    |                 ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(om as *const T)`
 
-error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<u8>`)
-  --> $DIR/transmute_ptr_to_ref.rs:32:32
+error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<u8>`)
+  --> $DIR/transmute_ptr_to_ref.rs:36:32
    |
 LL |     let _: &Foo<u8> = unsafe { std::mem::transmute::<_, &Foo<_>>(raw) };
-   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const Foo<_>)`
+   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::<Foo<_>>()`
 
-error: transmute from a pointer type (`*const i32`) to a reference type (`&issue1231::Foo<&u8>`)
-  --> $DIR/transmute_ptr_to_ref.rs:34:33
+error: transmute from a pointer type (`*const i32`) to a reference type (`&_issue1231::Foo<&u8>`)
+  --> $DIR/transmute_ptr_to_ref.rs:38:33
    |
 LL |     let _: &Foo<&u8> = unsafe { std::mem::transmute::<_, &Foo<&_>>(raw) };
-   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const Foo<&_>)`
+   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*raw.cast::<Foo<&_>>()`
 
 error: transmute from a pointer type (`*const i32`) to a reference type (`&u8`)
-  --> $DIR/transmute_ptr_to_ref.rs:38:14
+  --> $DIR/transmute_ptr_to_ref.rs:42:14
    |
 LL |     unsafe { std::mem::transmute::<_, Bar>(raw) };
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(raw as *const u8)`
 
-error: aborting due to 10 previous errors
+error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
+  --> $DIR/transmute_ptr_to_ref.rs:47:14
+   |
+LL |         0 => std::mem::transmute(x),
+   |              ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()`
+
+error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
+  --> $DIR/transmute_ptr_to_ref.rs:48:14
+   |
+LL |         1 => std::mem::transmute(y),
+   |              ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&u32>()`
+
+error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
+  --> $DIR/transmute_ptr_to_ref.rs:49:14
+   |
+LL |         2 => std::mem::transmute::<_, &&'b u32>(x),
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()`
+
+error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
+  --> $DIR/transmute_ptr_to_ref.rs:50:14
+   |
+LL |         _ => std::mem::transmute::<_, &&'b u32>(y),
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*y.cast::<&'b u32>()`
+
+error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
+  --> $DIR/transmute_ptr_to_ref.rs:58:19
+   |
+LL |     let _: &u32 = std::mem::transmute(a);
+   |                   ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a`
+
+error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
+  --> $DIR/transmute_ptr_to_ref.rs:59:19
+   |
+LL |     let _: &u32 = std::mem::transmute::<_, &u32>(a);
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a.cast::<u32>()`
+
+error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
+  --> $DIR/transmute_ptr_to_ref.rs:61:14
+   |
+LL |         0 => std::mem::transmute(x),
+   |              ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&u32>()`
+
+error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
+  --> $DIR/transmute_ptr_to_ref.rs:62:14
+   |
+LL |         _ => std::mem::transmute::<_, &&'b u32>(x),
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*x.cast::<&'b u32>()`
+
+error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
+  --> $DIR/transmute_ptr_to_ref.rs:70:19
+   |
+LL |     let _: &u32 = std::mem::transmute(a);
+   |                   ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*a`
+
+error: transmute from a pointer type (`*const u32`) to a reference type (`&u32`)
+  --> $DIR/transmute_ptr_to_ref.rs:71:19
+   |
+LL |     let _: &u32 = std::mem::transmute::<_, &u32>(a);
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(a as *const u32)`
+
+error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
+  --> $DIR/transmute_ptr_to_ref.rs:73:14
+   |
+LL |         0 => std::mem::transmute(x),
+   |              ^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &u32)`
+
+error: transmute from a pointer type (`*const &u32`) to a reference type (`&&u32`)
+  --> $DIR/transmute_ptr_to_ref.rs:74:14
+   |
+LL |         _ => std::mem::transmute::<_, &&'b u32>(x),
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `&*(x as *const () as *const &'b u32)`
+
+error: aborting due to 22 previous errors
 
diff --git a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs
index ea3dce17081..8f78f16a0a1 100644
--- a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs
+++ b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.rs
@@ -113,6 +113,44 @@ mod issue5876 {
     }
 }
 
+fn _ref_to_opt_ref_implicit(x: &u32) -> Option<&u32> {
+    Some(x)
+}
+
+#[allow(clippy::needless_lifetimes)]
+fn _ref_to_opt_ref_explicit<'a>(x: &'a u32) -> Option<&'a u32> {
+    Some(x)
+}
+
+fn _with_constraint<'a, 'b: 'a>(x: &'b u32, y: &'a u32) -> &'a u32 {
+    if true { x } else { y }
+}
+
+async fn _async_implicit(x: &u32) -> &u32 {
+    x
+}
+
+#[allow(clippy::needless_lifetimes)]
+async fn _async_explicit<'a>(x: &'a u32) -> &'a u32 {
+    x
+}
+
+fn _unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 {
+    y
+}
+
+fn _return_ptr(x: &u32) -> *const u32 {
+    x
+}
+
+fn _return_field_ptr(x: &(u32, u32)) -> *const u32 {
+    &x.0
+}
+
+fn _return_field_ptr_addr_of(x: &(u32, u32)) -> *const u32 {
+    core::ptr::addr_of!(x.0)
+}
+
 fn main() {
     let (mut foo, bar) = (Foo(0), Bar([0; 24]));
     let (mut a, b, c, x, y, z) = (0, 0, Bar([0; 24]), 0, Foo(0), 0);
diff --git a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr
index a88d35f3ea5..66ecb3d8e77 100644
--- a/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr
+++ b/src/tools/clippy/tests/ui/trivially_copy_pass_by_ref.stderr
@@ -106,5 +106,11 @@ error: this argument (N byte) is passed by reference, but would be more efficien
 LL |     fn foo(x: &i32) {
    |               ^^^^ help: consider passing by value instead: `i32`
 
-error: aborting due to 17 previous errors
+error: this argument (N byte) is passed by reference, but would be more efficient if passed by value (limit: N byte)
+  --> $DIR/trivially_copy_pass_by_ref.rs:138:37
+   |
+LL | fn _unrelated_lifetimes<'a, 'b>(_x: &'a u32, y: &'b u32) -> &'b u32 {
+   |                                     ^^^^^^^ help: consider passing by value instead: `u32`
+
+error: aborting due to 18 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unused_async.rs b/src/tools/clippy/tests/ui/unused_async.rs
index 2a3a506a57b..4ca7f29b34c 100644
--- a/src/tools/clippy/tests/ui/unused_async.rs
+++ b/src/tools/clippy/tests/ui/unused_async.rs
@@ -1,5 +1,8 @@
 #![warn(clippy::unused_async)]
 
+use std::future::Future;
+use std::pin::Pin;
+
 async fn foo() -> i32 {
     4
 }
@@ -8,6 +11,37 @@ async fn bar() -> i32 {
     foo().await
 }
 
+struct S;
+
+impl S {
+    async fn unused(&self) -> i32 {
+        1
+    }
+
+    async fn used(&self) -> i32 {
+        self.unused().await
+    }
+}
+
+trait AsyncTrait {
+    fn trait_method() -> Pin<Box<dyn Future<Output = i32>>>;
+}
+
+macro_rules! async_trait_impl {
+    () => {
+        impl AsyncTrait for S {
+            fn trait_method() -> Pin<Box<dyn Future<Output = i32>>> {
+                async fn unused() -> i32 {
+                    5
+                }
+
+                Box::pin(unused())
+            }
+        }
+    };
+}
+async_trait_impl!();
+
 fn main() {
     foo();
     bar();
diff --git a/src/tools/clippy/tests/ui/unused_async.stderr b/src/tools/clippy/tests/ui/unused_async.stderr
index cc6096d65d9..8b8ad065a4c 100644
--- a/src/tools/clippy/tests/ui/unused_async.stderr
+++ b/src/tools/clippy/tests/ui/unused_async.stderr
@@ -1,5 +1,5 @@
 error: unused `async` for function with no await statements
-  --> $DIR/unused_async.rs:3:1
+  --> $DIR/unused_async.rs:6:1
    |
 LL | / async fn foo() -> i32 {
 LL | |     4
@@ -9,5 +9,15 @@ LL | | }
    = note: `-D clippy::unused-async` implied by `-D warnings`
    = help: consider removing the `async` from this function
 
-error: aborting due to previous error
+error: unused `async` for function with no await statements
+  --> $DIR/unused_async.rs:17:5
+   |
+LL | /     async fn unused(&self) -> i32 {
+LL | |         1
+LL | |     }
+   | |_____^
+   |
+   = help: consider removing the `async` from this function
+
+error: aborting due to 2 previous errors
 
diff --git a/src/tools/clippy/tests/ui/useless_asref.fixed b/src/tools/clippy/tests/ui/useless_asref.fixed
index e431661d180..90cb8945e77 100644
--- a/src/tools/clippy/tests/ui/useless_asref.fixed
+++ b/src/tools/clippy/tests/ui/useless_asref.fixed
@@ -1,6 +1,7 @@
 // run-rustfix
 
 #![deny(clippy::useless_asref)]
+#![allow(clippy::explicit_auto_deref)]
 
 use std::fmt::Debug;
 
diff --git a/src/tools/clippy/tests/ui/useless_asref.rs b/src/tools/clippy/tests/ui/useless_asref.rs
index 6ae931d7aa4..cb9f8ae5909 100644
--- a/src/tools/clippy/tests/ui/useless_asref.rs
+++ b/src/tools/clippy/tests/ui/useless_asref.rs
@@ -1,6 +1,7 @@
 // run-rustfix
 
 #![deny(clippy::useless_asref)]
+#![allow(clippy::explicit_auto_deref)]
 
 use std::fmt::Debug;
 
diff --git a/src/tools/clippy/tests/ui/useless_asref.stderr b/src/tools/clippy/tests/ui/useless_asref.stderr
index 5876b54aca8..b21c67bb364 100644
--- a/src/tools/clippy/tests/ui/useless_asref.stderr
+++ b/src/tools/clippy/tests/ui/useless_asref.stderr
@@ -1,5 +1,5 @@
 error: this call to `as_ref` does nothing
-  --> $DIR/useless_asref.rs:43:18
+  --> $DIR/useless_asref.rs:44:18
    |
 LL |         foo_rstr(rstr.as_ref());
    |                  ^^^^^^^^^^^^^ help: try this: `rstr`
@@ -11,61 +11,61 @@ LL | #![deny(clippy::useless_asref)]
    |         ^^^^^^^^^^^^^^^^^^^^^
 
 error: this call to `as_ref` does nothing
-  --> $DIR/useless_asref.rs:45:20
+  --> $DIR/useless_asref.rs:46:20
    |
 LL |         foo_rslice(rslice.as_ref());
    |                    ^^^^^^^^^^^^^^^ help: try this: `rslice`
 
 error: this call to `as_mut` does nothing
-  --> $DIR/useless_asref.rs:49:21
+  --> $DIR/useless_asref.rs:50:21
    |
 LL |         foo_mrslice(mrslice.as_mut());
    |                     ^^^^^^^^^^^^^^^^ help: try this: `mrslice`
 
 error: this call to `as_ref` does nothing
-  --> $DIR/useless_asref.rs:51:20
+  --> $DIR/useless_asref.rs:52:20
    |
 LL |         foo_rslice(mrslice.as_ref());
    |                    ^^^^^^^^^^^^^^^^ help: try this: `mrslice`
 
 error: this call to `as_ref` does nothing
-  --> $DIR/useless_asref.rs:58:20
+  --> $DIR/useless_asref.rs:59:20
    |
 LL |         foo_rslice(rrrrrslice.as_ref());
    |                    ^^^^^^^^^^^^^^^^^^^ help: try this: `rrrrrslice`
 
 error: this call to `as_ref` does nothing
-  --> $DIR/useless_asref.rs:60:18
+  --> $DIR/useless_asref.rs:61:18
    |
 LL |         foo_rstr(rrrrrstr.as_ref());
    |                  ^^^^^^^^^^^^^^^^^ help: try this: `rrrrrstr`
 
 error: this call to `as_mut` does nothing
-  --> $DIR/useless_asref.rs:65:21
+  --> $DIR/useless_asref.rs:66:21
    |
 LL |         foo_mrslice(mrrrrrslice.as_mut());
    |                     ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice`
 
 error: this call to `as_ref` does nothing
-  --> $DIR/useless_asref.rs:67:20
+  --> $DIR/useless_asref.rs:68:20
    |
 LL |         foo_rslice(mrrrrrslice.as_ref());
    |                    ^^^^^^^^^^^^^^^^^^^^ help: try this: `mrrrrrslice`
 
 error: this call to `as_ref` does nothing
-  --> $DIR/useless_asref.rs:71:16
+  --> $DIR/useless_asref.rs:72:16
    |
 LL |     foo_rrrrmr((&&&&MoreRef).as_ref());
    |                ^^^^^^^^^^^^^^^^^^^^^^ help: try this: `(&&&&MoreRef)`
 
 error: this call to `as_mut` does nothing
-  --> $DIR/useless_asref.rs:121:13
+  --> $DIR/useless_asref.rs:122:13
    |
 LL |     foo_mrt(mrt.as_mut());
    |             ^^^^^^^^^^^^ help: try this: `mrt`
 
 error: this call to `as_ref` does nothing
-  --> $DIR/useless_asref.rs:123:12
+  --> $DIR/useless_asref.rs:124:12
    |
 LL |     foo_rt(mrt.as_ref());
    |            ^^^^^^^^^^^^ help: try this: `mrt`
diff --git a/src/tools/clippy/tests/ui/while_let_loop.rs b/src/tools/clippy/tests/ui/while_let_loop.rs
index 3ce699f551b..c42e2a79a9b 100644
--- a/src/tools/clippy/tests/ui/while_let_loop.rs
+++ b/src/tools/clippy/tests/ui/while_let_loop.rs
@@ -117,3 +117,29 @@ fn issue1948() {
         }
     };
 }
+
+fn issue_7913(m: &std::sync::Mutex<Vec<u32>>) {
+    // Don't lint. The lock shouldn't be held while printing.
+    loop {
+        let x = if let Some(x) = m.lock().unwrap().pop() {
+            x
+        } else {
+            break;
+        };
+
+        println!("{}", x);
+    }
+}
+
+fn issue_5715(mut m: core::cell::RefCell<Option<u32>>) {
+    // Don't lint. The temporary from `borrow_mut` must be dropped before overwriting the `RefCell`.
+    loop {
+        let x = if let &mut Some(x) = &mut *m.borrow_mut() {
+            x
+        } else {
+            break;
+        };
+
+        m = core::cell::RefCell::new(Some(x + 1));
+    }
+}
diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.fixed b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed
index cb8892a3f00..e9ff64431e1 100644
--- a/src/tools/clippy/tests/ui/while_let_on_iterator.fixed
+++ b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed
@@ -6,7 +6,8 @@
     unreachable_code,
     unused_mut,
     dead_code,
-    clippy::equatable_if_let
+    clippy::equatable_if_let,
+    clippy::manual_find
 )]
 
 fn base() {
diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.rs b/src/tools/clippy/tests/ui/while_let_on_iterator.rs
index d9157184495..03da39526b2 100644
--- a/src/tools/clippy/tests/ui/while_let_on_iterator.rs
+++ b/src/tools/clippy/tests/ui/while_let_on_iterator.rs
@@ -6,7 +6,8 @@
     unreachable_code,
     unused_mut,
     dead_code,
-    clippy::equatable_if_let
+    clippy::equatable_if_let,
+    clippy::manual_find
 )]
 
 fn base() {
diff --git a/src/tools/clippy/tests/ui/while_let_on_iterator.stderr b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr
index fb2b0f2467c..42859243855 100644
--- a/src/tools/clippy/tests/ui/while_let_on_iterator.stderr
+++ b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr
@@ -1,5 +1,5 @@
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:14:5
+  --> $DIR/while_let_on_iterator.rs:15:5
    |
 LL |     while let Option::Some(x) = iter.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
@@ -7,85 +7,85 @@ LL |     while let Option::Some(x) = iter.next() {
    = note: `-D clippy::while-let-on-iterator` implied by `-D warnings`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:19:5
+  --> $DIR/while_let_on_iterator.rs:20:5
    |
 LL |     while let Some(x) = iter.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in iter`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:24:5
+  --> $DIR/while_let_on_iterator.rs:25:5
    |
 LL |     while let Some(_) = iter.next() {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in iter`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:100:9
+  --> $DIR/while_let_on_iterator.rs:101:9
    |
 LL |         while let Some([..]) = it.next() {}
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [..] in it`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:107:9
+  --> $DIR/while_let_on_iterator.rs:108:9
    |
 LL |         while let Some([_x]) = it.next() {}
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for [_x] in it`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:120:9
+  --> $DIR/while_let_on_iterator.rs:121:9
    |
 LL |         while let Some(x @ [_]) = it.next() {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x @ [_] in it`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:140:9
+  --> $DIR/while_let_on_iterator.rs:141:9
    |
 LL |         while let Some(_) = y.next() {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in y`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:197:9
+  --> $DIR/while_let_on_iterator.rs:198:9
    |
 LL |         while let Some(m) = it.next() {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:208:5
+  --> $DIR/while_let_on_iterator.rs:209:5
    |
 LL |     while let Some(n) = it.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:210:9
+  --> $DIR/while_let_on_iterator.rs:211:9
    |
 LL |         while let Some(m) = it.next() {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:219:9
+  --> $DIR/while_let_on_iterator.rs:220:9
    |
 LL |         while let Some(m) = it.next() {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:228:9
+  --> $DIR/while_let_on_iterator.rs:229:9
    |
 LL |         while let Some(m) = it.next() {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:245:9
+  --> $DIR/while_let_on_iterator.rs:246:9
    |
 LL |         while let Some(m) = it.next() {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for m in it.by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:260:13
+  --> $DIR/while_let_on_iterator.rs:261:13
    |
 LL |             while let Some(i) = self.0.next() {
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.by_ref()`
 
 error: manual `!RangeInclusive::contains` implementation
-  --> $DIR/while_let_on_iterator.rs:261:20
+  --> $DIR/while_let_on_iterator.rs:262:20
    |
 LL |                 if i < 3 || i > 7 {
    |                    ^^^^^^^^^^^^^^ help: use: `!(3..=7).contains(&i)`
@@ -93,49 +93,49 @@ LL |                 if i < 3 || i > 7 {
    = note: `-D clippy::manual-range-contains` implied by `-D warnings`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:292:13
+  --> $DIR/while_let_on_iterator.rs:293:13
    |
 LL |             while let Some(i) = self.0.0.0.next() {
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for i in self.0.0.0.by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:321:5
+  --> $DIR/while_let_on_iterator.rs:322:5
    |
 LL |     while let Some(n) = it.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for n in it.by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:333:9
+  --> $DIR/while_let_on_iterator.rs:334:9
    |
 LL |         while let Some(x) = it.next() {
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:347:5
+  --> $DIR/while_let_on_iterator.rs:348:5
    |
 LL |     while let Some(x) = it.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:358:5
+  --> $DIR/while_let_on_iterator.rs:359:5
    |
 LL |     while let Some(x) = it.0.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.0.by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:393:5
+  --> $DIR/while_let_on_iterator.rs:394:5
    |
 LL |     while let Some(x) = s.x.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in s.x.by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:400:5
+  --> $DIR/while_let_on_iterator.rs:401:5
    |
 LL |     while let Some(x) = x[0].next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in x[0].by_ref()`
 
 error: this loop could be written as a `for` loop
-  --> $DIR/while_let_on_iterator.rs:407:5
+  --> $DIR/while_let_on_iterator.rs:408:5
    |
 LL |     while let Some(..) = it.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`
diff --git a/src/tools/clippy/tests/ui/zero_div_zero.rs b/src/tools/clippy/tests/ui/zero_div_zero.rs
index ed3a9208fc3..968c58f40ae 100644
--- a/src/tools/clippy/tests/ui/zero_div_zero.rs
+++ b/src/tools/clippy/tests/ui/zero_div_zero.rs
@@ -1,4 +1,4 @@
-#[allow(unused_variables)]
+#[allow(unused_variables, clippy::eq_op)]
 #[warn(clippy::zero_divided_by_zero)]
 fn main() {
     let nan = 0.0 / 0.0;
diff --git a/src/tools/clippy/tests/ui/zero_div_zero.stderr b/src/tools/clippy/tests/ui/zero_div_zero.stderr
index 0931dd32e7a..86563542e06 100644
--- a/src/tools/clippy/tests/ui/zero_div_zero.stderr
+++ b/src/tools/clippy/tests/ui/zero_div_zero.stderr
@@ -1,11 +1,3 @@
-error: equal expressions as operands to `/`
-  --> $DIR/zero_div_zero.rs:4:15
-   |
-LL |     let nan = 0.0 / 0.0;
-   |               ^^^^^^^^^
-   |
-   = note: `#[deny(clippy::eq_op)]` on by default
-
 error: constant division of `0.0` with `0.0` will always result in NaN
   --> $DIR/zero_div_zero.rs:4:15
    |
@@ -15,12 +7,6 @@ LL |     let nan = 0.0 / 0.0;
    = note: `-D clippy::zero-divided-by-zero` implied by `-D warnings`
    = help: consider using `f64::NAN` if you would like a constant representing NaN
 
-error: equal expressions as operands to `/`
-  --> $DIR/zero_div_zero.rs:5:19
-   |
-LL |     let f64_nan = 0.0 / 0.0f64;
-   |                   ^^^^^^^^^^^^
-
 error: constant division of `0.0` with `0.0` will always result in NaN
   --> $DIR/zero_div_zero.rs:5:19
    |
@@ -29,12 +15,6 @@ LL |     let f64_nan = 0.0 / 0.0f64;
    |
    = help: consider using `f64::NAN` if you would like a constant representing NaN
 
-error: equal expressions as operands to `/`
-  --> $DIR/zero_div_zero.rs:6:25
-   |
-LL |     let other_f64_nan = 0.0f64 / 0.0;
-   |                         ^^^^^^^^^^^^
-
 error: constant division of `0.0` with `0.0` will always result in NaN
   --> $DIR/zero_div_zero.rs:6:25
    |
@@ -43,12 +23,6 @@ LL |     let other_f64_nan = 0.0f64 / 0.0;
    |
    = help: consider using `f64::NAN` if you would like a constant representing NaN
 
-error: equal expressions as operands to `/`
-  --> $DIR/zero_div_zero.rs:7:28
-   |
-LL |     let one_more_f64_nan = 0.0f64 / 0.0f64;
-   |                            ^^^^^^^^^^^^^^^
-
 error: constant division of `0.0` with `0.0` will always result in NaN
   --> $DIR/zero_div_zero.rs:7:28
    |
@@ -57,5 +31,5 @@ LL |     let one_more_f64_nan = 0.0f64 / 0.0f64;
    |
    = help: consider using `f64::NAN` if you would like a constant representing NaN
 
-error: aborting due to 8 previous errors
+error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/util/gh-pages/index.html b/src/tools/clippy/util/gh-pages/index.html
index 4999cce7511..6183089911b 100644
--- a/src/tools/clippy/util/gh-pages/index.html
+++ b/src/tools/clippy/util/gh-pages/index.html
@@ -10,6 +10,7 @@ Otherwise, have a great day =^.^=
 <head>
     <meta charset="UTF-8"/>
     <meta name="viewport" content="width=device-width, initial-scale=1"/>
+    <meta name="description" content="A collection of lints to catch common mistakes and improve your Rust code.">
 
     <title>Clippy Lints</title>