about summary refs log tree commit diff
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-02-09 21:06:12 +0000
committerbors <bors@rust-lang.org>2024-02-09 21:06:12 +0000
commitd44e3b95cb9d410d89cb8ab3233906a33f43756a (patch)
tree0e7416fd47b7d79015233d6516cdece2ebd0fe88
parentf4cfd872028398da2b2d85c368c51f4d007dc6af (diff)
parent3c1b7a741f4adcc29324836db6e4fe078589debb (diff)
downloadrust-d44e3b95cb9d410d89cb8ab3233906a33f43756a.tar.gz
rust-d44e3b95cb9d410d89cb8ab3233906a33f43756a.zip
Auto merge of #120852 - matthiaskrgr:rollup-01pr8gj, r=matthiaskrgr
Rollup of 11 pull requests

Successful merges:

 - #120351 (Implement SystemTime for UEFI)
 - #120354 (improve normalization of `Pointee::Metadata`)
 - #120776 (Move path implementations into `sys`)
 - #120790 (better error message on download CI LLVM failure)
 - #120806 (Clippy subtree update)
 - #120815 (Improve `Option::inspect` docs)
 - #120822 (Emit more specific diagnostics when enums fail to cast with `as`)
 - #120827 (Print image input file and checksum in CI only)
 - #120836 (hide impls if trait bound is proven from env)
 - #120844 (Build DebugInfo for async closures)
 - #120851 (Remove duplicate release note)

r? `@ghost`
`@rustbot` modify labels: rollup
-rw-r--r--Cargo.lock10
-rw-r--r--RELEASES.md1
-rw-r--r--compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs5
-rw-r--r--compiler/rustc_codegen_llvm/src/intrinsic.rs6
-rw-r--r--compiler/rustc_const_eval/src/interpret/terminator.rs7
-rw-r--r--compiler/rustc_hir_typeck/src/cast.rs26
-rw-r--r--compiler/rustc_middle/src/ty/sty.rs48
-rw-r--r--compiler/rustc_trait_selection/src/solve/assembly/mod.rs118
-rw-r--r--compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs29
-rw-r--r--compiler/rustc_trait_selection/src/traits/project.rs38
-rw-r--r--compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs23
-rw-r--r--library/core/src/option.rs15
-rw-r--r--library/core/src/result.rs8
-rw-r--r--library/std/src/sys/mod.rs1
-rw-r--r--library/std/src/sys/pal/hermit/mod.rs2
-rw-r--r--library/std/src/sys/pal/sgx/mod.rs1
-rw-r--r--library/std/src/sys/pal/solid/mod.rs1
-rw-r--r--library/std/src/sys/pal/teeos/mod.rs2
-rw-r--r--library/std/src/sys/pal/uefi/helpers.rs8
-rw-r--r--library/std/src/sys/pal/uefi/mod.rs2
-rw-r--r--library/std/src/sys/pal/uefi/path.rs25
-rw-r--r--library/std/src/sys/pal/uefi/tests.rs20
-rw-r--r--library/std/src/sys/pal/uefi/time.rs105
-rw-r--r--library/std/src/sys/pal/unix/mod.rs1
-rw-r--r--library/std/src/sys/pal/unsupported/mod.rs2
-rw-r--r--library/std/src/sys/pal/wasi/mod.rs2
-rw-r--r--library/std/src/sys/pal/wasm/mod.rs2
-rw-r--r--library/std/src/sys/pal/windows/fs.rs2
-rw-r--r--library/std/src/sys/pal/windows/mod.rs5
-rw-r--r--library/std/src/sys/pal/xous/mod.rs2
-rw-r--r--library/std/src/sys/pal/zkvm/mod.rs4
-rw-r--r--library/std/src/sys/path/mod.rs18
-rw-r--r--library/std/src/sys/path/sgx.rs (renamed from library/std/src/sys/pal/sgx/path.rs)0
-rw-r--r--library/std/src/sys/path/unix.rs (renamed from library/std/src/sys/pal/unix/path.rs)0
-rw-r--r--library/std/src/sys/path/unsupported_backslash.rs (renamed from library/std/src/sys/pal/solid/path.rs)0
-rw-r--r--library/std/src/sys/path/windows.rs (renamed from library/std/src/sys/pal/windows/path.rs)4
-rw-r--r--library/std/src/sys/path/windows/tests.rs (renamed from library/std/src/sys/pal/windows/path/tests.rs)0
-rw-r--r--src/bootstrap/src/core/download.rs6
-rwxr-xr-xsrc/ci/docker/run.sh7
-rwxr-xr-x[-rw-r--r--]src/tools/clippy/.github/driver.sh15
-rw-r--r--src/tools/clippy/CHANGELOG.md66
-rw-r--r--src/tools/clippy/Cargo.toml2
-rw-r--r--src/tools/clippy/book/src/lint_configuration.md33
-rw-r--r--src/tools/clippy/clippy_config/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_config/src/conf.rs22
-rw-r--r--src/tools/clippy/clippy_config/src/msrvs.rs13
-rw-r--r--src/tools/clippy/clippy_lints/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_lints/src/booleans.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs168
-rw-r--r--src/tools/clippy/clippy_lints/src/cargo/mod.rs43
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/mod.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs55
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/eta_reduction.rs50
-rw-r--r--src/tools/clippy/clippy_lints/src/incompatible_msrv.rs133
-rw-r--r--src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/never_loop.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_retain.rs141
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs197
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs65
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs95
-rw-r--r--src/tools/clippy/clippy_lints/src/no_effect.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/mod.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs27
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_closure_call.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_locals.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/returns.rs31
-rw-r--r--src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs67
-rw-r--r--src/tools/clippy/clippy_lints/src/unconditional_recursion.rs86
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_io_amount.rs101
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/author.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/wildcard_imports.rs16
-rw-r--r--src/tools/clippy/clippy_utils/Cargo.toml2
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs359
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs29
-rw-r--r--src/tools/clippy/declare_clippy_lint/Cargo.toml2
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/src/driver.rs25
-rw-r--r--src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr45
-rw-r--r--src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml20
-rw-r--r--src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/src/lib.rs1
-rw-r--r--src/tools/clippy/tests/ui-cargo/lint_groups_priority/pass/Cargo.toml10
-rw-r--r--src/tools/clippy/tests/ui-cargo/lint_groups_priority/pass/src/lib.rs1
-rw-r--r--src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.fixed2
-rw-r--r--src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs2
-rw-r--r--src/tools/clippy/tests/ui-toml/modulo_arithmetic/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/modulo_arithmetic/modulo_arithmetic.rs10
-rw-r--r--src/tools/clippy/tests/ui-toml/modulo_arithmetic/modulo_arithmetic.stderr40
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr6
-rw-r--r--src/tools/clippy/tests/ui-toml/wildcard_imports/clippy.toml3
-rw-r--r--src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.fixed19
-rw-r--r--src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.rs19
-rw-r--r--src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.stderr20
-rw-r--r--src/tools/clippy/tests/ui-toml/wildcard_imports_whitelist/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.fixed26
-rw-r--r--src/tools/clippy/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.rs26
-rw-r--r--src/tools/clippy/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.stderr11
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr.fixed1
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr.rs1
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr.stderr4
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr_no_std.fixed1
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr_no_std.rs1
-rw-r--r--src/tools/clippy/tests/ui/borrow_as_ptr_no_std.stderr4
-rw-r--r--src/tools/clippy/tests/ui/eta.fixed71
-rw-r--r--src/tools/clippy/tests/ui/eta.rs71
-rw-r--r--src/tools/clippy/tests/ui/eta.stderr28
-rw-r--r--src/tools/clippy/tests/ui/format_args.fixed1
-rw-r--r--src/tools/clippy/tests/ui/format_args.rs1
-rw-r--r--src/tools/clippy/tests/ui/format_args.stderr50
-rw-r--r--src/tools/clippy/tests/ui/ignored_unit_patterns.fixed7
-rw-r--r--src/tools/clippy/tests/ui/ignored_unit_patterns.rs7
-rw-r--r--src/tools/clippy/tests/ui/ignored_unit_patterns.stderr18
-rw-r--r--src/tools/clippy/tests/ui/incompatible_msrv.rs23
-rw-r--r--src/tools/clippy/tests/ui/incompatible_msrv.stderr23
-rw-r--r--src/tools/clippy/tests/ui/manual_c_str_literals.fixed60
-rw-r--r--src/tools/clippy/tests/ui/manual_c_str_literals.rs60
-rw-r--r--src/tools/clippy/tests/ui/manual_c_str_literals.stderr83
-rw-r--r--src/tools/clippy/tests/ui/manual_retain.fixed63
-rw-r--r--src/tools/clippy/tests/ui/manual_retain.rs63
-rw-r--r--src/tools/clippy/tests/ui/manual_retain.stderr142
-rw-r--r--src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs8
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.fixed3
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.rs3
-rw-r--r--src/tools/clippy/tests/ui/needless_borrow.stderr20
-rw-r--r--src/tools/clippy/tests/ui/needless_return_with_question_mark.fixed50
-rw-r--r--src/tools/clippy/tests/ui/needless_return_with_question_mark.rs50
-rw-r--r--src/tools/clippy/tests/ui/never_loop.rs11
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool.rs11
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed9
-rw-r--r--src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs9
-rw-r--r--src/tools/clippy/tests/ui/redundant_locals.rs46
-rw-r--r--src/tools/clippy/tests/ui/redundant_locals.stderr56
-rw-r--r--src/tools/clippy/tests/ui/redundant_type_annotations.rs7
-rw-r--r--src/tools/clippy/tests/ui/redundant_type_annotations.stderr4
-rw-r--r--src/tools/clippy/tests/ui/ref_as_ptr.fixed110
-rw-r--r--src/tools/clippy/tests/ui/ref_as_ptr.rs110
-rw-r--r--src/tools/clippy/tests/ui/ref_as_ptr.stderr269
-rw-r--r--src/tools/clippy/tests/ui/strlen_on_c_strings.fixed2
-rw-r--r--src/tools/clippy/tests/ui/strlen_on_c_strings.rs2
-rw-r--r--src/tools/clippy/tests/ui/to_string_trait_impl.rs31
-rw-r--r--src/tools/clippy/tests/ui/to_string_trait_impl.stderr16
-rw-r--r--src/tools/clippy/tests/ui/unconditional_recursion.rs62
-rw-r--r--src/tools/clippy/tests/ui/unconditional_recursion.stderr14
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_fold.fixed6
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_fold.rs6
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_fold.stderr41
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.fixed7
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.rs7
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_result_map_or_else.fixed61
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_result_map_or_else.rs69
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_result_map_or_else.stderr35
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.fixed3
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.rs3
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.stderr170
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.fixed1
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.rs1
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.stderr18
-rw-r--r--src/tools/clippy/tests/ui/unused_io_amount.rs43
-rw-r--r--src/tools/clippy/tests/ui/unused_io_amount.stderr8
-rw-r--r--src/tools/clippy/tests/ui/while_let_on_iterator.fixed16
-rw-r--r--src/tools/clippy/tests/ui/while_let_on_iterator.rs14
-rw-r--r--src/tools/clippy/tests/ui/while_let_on_iterator.stderr12
-rw-r--r--tests/codegen/async-closure-debug.rs21
-rw-r--r--tests/ui/async-await/async-closures/async-fn-mut-for-async-fn.rs3
-rw-r--r--tests/ui/async-await/async-closures/async-fn-once-for-async-fn.rs3
-rw-r--r--tests/ui/cast/enum-to-numeric-cast.rs46
-rw-r--r--tests/ui/cast/enum-to-numeric-cast.stderr35
-rw-r--r--tests/ui/cast/issue-88621.stderr4
-rw-r--r--tests/ui/tag-variant-cast-non-nullary.stderr3
-rw-r--r--tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs12
-rw-r--r--tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.stderr2
-rw-r--r--tests/ui/traits/next-solver/env-shadows-impls/ambig-env-no-shadow.rs40
-rw-r--r--tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-1.rs30
-rw-r--r--tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-2.rs29
-rw-r--r--tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-3.rs18
-rw-r--r--tests/ui/traits/next-solver/env-shadows-impls/normalizes_to_ignores_unnormalizable_candidate.rs29
-rw-r--r--tests/ui/traits/next-solver/env-shadows-impls/normalizes_to_ignores_unnormalizable_candidate.stderr (renamed from tests/ui/traits/next-solver/normalizes_to_ignores_unnormalizable_candidate.self_infer.stderr)10
-rw-r--r--tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.rs31
-rw-r--r--tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.stderr19
-rw-r--r--tests/ui/traits/next-solver/generalize/occurs-check-nested-alias.next.stderr8
-rw-r--r--tests/ui/traits/next-solver/normalize-param-env-2.rs17
-rw-r--r--tests/ui/traits/next-solver/normalize-param-env-2.stderr35
-rw-r--r--tests/ui/traits/next-solver/normalize-param-env-4.next.stderr19
-rw-r--r--tests/ui/traits/next-solver/normalize-param-env-4.rs34
-rw-r--r--tests/ui/traits/next-solver/normalizes_to_ignores_unnormalizable_candidate.rs40
-rw-r--r--tests/ui/traits/next-solver/param-candidate-doesnt-shadow-project.rs25
-rw-r--r--tests/ui/traits/pointee-normalize-equate.rs56
195 files changed, 4726 insertions, 872 deletions
diff --git a/Cargo.lock b/Cargo.lock
index 300cc02330f..4daf6f42b78 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -556,7 +556,7 @@ checksum = "2da6da31387c7e4ef160ffab6d5e7f00c42626fe39aea70a7b0f1773f7dd6c1b"
 
 [[package]]
 name = "clippy"
-version = "0.1.77"
+version = "0.1.78"
 dependencies = [
  "anstream",
  "clippy_config",
@@ -584,7 +584,7 @@ dependencies = [
 
 [[package]]
 name = "clippy_config"
-version = "0.1.77"
+version = "0.1.78"
 dependencies = [
  "rustc-semver",
  "serde",
@@ -607,7 +607,7 @@ dependencies = [
 
 [[package]]
 name = "clippy_lints"
-version = "0.1.77"
+version = "0.1.78"
 dependencies = [
  "arrayvec",
  "cargo_metadata 0.18.0",
@@ -632,7 +632,7 @@ dependencies = [
 
 [[package]]
 name = "clippy_utils"
-version = "0.1.77"
+version = "0.1.78"
 dependencies = [
  "arrayvec",
  "clippy_config",
@@ -1003,7 +1003,7 @@ checksum = "a0afaad2b26fa326569eb264b1363e8ae3357618c43982b3f285f0774ce76b69"
 
 [[package]]
 name = "declare_clippy_lint"
-version = "0.1.77"
+version = "0.1.78"
 dependencies = [
  "itertools",
  "quote",
diff --git a/RELEASES.md b/RELEASES.md
index 3751b15b361..20e317a4d23 100644
--- a/RELEASES.md
+++ b/RELEASES.md
@@ -7,7 +7,6 @@ Language
 --------
 - [Document Rust ABI compatibility between various types](https://github.com/rust-lang/rust/pull/115476/)
 - [Also: guarantee that char and u32 are ABI-compatible](https://github.com/rust-lang/rust/pull/118032/)
-- [Warn against ambiguous wide pointer comparisons](https://github.com/rust-lang/rust/pull/117758/)
 - [Add lint `ambiguous_wide_pointer_comparisons` that supersedes `clippy::vtable_address_comparisons`](https://github.com/rust-lang/rust/pull/117758)
 
 <a id="1.76.0-Compiler"></a>
diff --git a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
index 76c9ac6614a..f961cd2d00b 100644
--- a/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
+++ b/compiler/rustc_codegen_llvm/src/debuginfo/metadata.rs
@@ -461,6 +461,7 @@ pub fn type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll D
         }
         ty::FnDef(..) | ty::FnPtr(_) => build_subroutine_type_di_node(cx, unique_type_id),
         ty::Closure(..) => build_closure_env_di_node(cx, unique_type_id),
+        ty::CoroutineClosure(..) => build_closure_env_di_node(cx, unique_type_id),
         ty::Coroutine(..) => enums::build_coroutine_di_node(cx, unique_type_id),
         ty::Adt(def, ..) => match def.adt_kind() {
             AdtKind::Struct => build_struct_type_di_node(cx, unique_type_id),
@@ -1068,6 +1069,7 @@ fn build_upvar_field_di_nodes<'ll, 'tcx>(
     let (&def_id, up_var_tys) = match closure_or_coroutine_ty.kind() {
         ty::Coroutine(def_id, args) => (def_id, args.as_coroutine().prefix_tys()),
         ty::Closure(def_id, args) => (def_id, args.as_closure().upvar_tys()),
+        ty::CoroutineClosure(def_id, args) => (def_id, args.as_coroutine_closure().upvar_tys()),
         _ => {
             bug!(
                 "build_upvar_field_di_nodes() called with non-closure-or-coroutine-type: {:?}",
@@ -1153,7 +1155,8 @@ fn build_closure_env_di_node<'ll, 'tcx>(
     unique_type_id: UniqueTypeId<'tcx>,
 ) -> DINodeCreationResult<'ll> {
     let closure_env_type = unique_type_id.expect_ty();
-    let &ty::Closure(def_id, _args) = closure_env_type.kind() else {
+    let &(ty::Closure(def_id, _) | ty::CoroutineClosure(def_id, _)) = closure_env_type.kind()
+    else {
         bug!("build_closure_env_di_node() called with non-closure-type: {:?}", closure_env_type)
     };
     let containing_scope = get_namespace_for_item(cx, def_id);
diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs
index b4512af38e3..e3e48ecb3aa 100644
--- a/compiler/rustc_codegen_llvm/src/intrinsic.rs
+++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs
@@ -1985,10 +1985,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
 
         match in_elem.kind() {
             ty::RawPtr(p) => {
-                let (metadata, check_sized) = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
+                let metadata = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
                     bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty)
                 });
-                assert!(!check_sized); // we are in codegen, so we shouldn't see these types
                 require!(
                     metadata.is_unit(),
                     InvalidMonomorphization::CastFatPointer { span, name, ty: in_elem }
@@ -2000,10 +1999,9 @@ fn generic_simd_intrinsic<'ll, 'tcx>(
         }
         match out_elem.kind() {
             ty::RawPtr(p) => {
-                let (metadata, check_sized) = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
+                let metadata = p.ty.ptr_metadata_ty(bx.tcx, |ty| {
                     bx.tcx.normalize_erasing_regions(ty::ParamEnv::reveal_all(), ty)
                 });
-                assert!(!check_sized); // we are in codegen, so we shouldn't see these types
                 require!(
                     metadata.is_unit(),
                     InvalidMonomorphization::CastFatPointer { span, name, ty: out_elem }
diff --git a/compiler/rustc_const_eval/src/interpret/terminator.rs b/compiler/rustc_const_eval/src/interpret/terminator.rs
index 85a2e4778d2..ff20fc5092c 100644
--- a/compiler/rustc_const_eval/src/interpret/terminator.rs
+++ b/compiler/rustc_const_eval/src/interpret/terminator.rs
@@ -377,12 +377,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
                 // to fields, which can yield non-normalized types. So we need to provide a
                 // normalization function.
                 let normalize = |ty| self.tcx.normalize_erasing_regions(self.param_env, ty);
-                let (meta, only_if_sized) = ty.ptr_metadata_ty(*self.tcx, normalize);
-                assert!(
-                    !only_if_sized,
-                    "there should be no more 'maybe has that metadata' types during interpretation"
-                );
-                meta
+                ty.ptr_metadata_ty(*self.tcx, normalize)
             };
             return Ok(meta_ty(caller) == meta_ty(callee));
         }
diff --git a/compiler/rustc_hir_typeck/src/cast.rs b/compiler/rustc_hir_typeck/src/cast.rs
index 58823ea30ce..f21de1609cb 100644
--- a/compiler/rustc_hir_typeck/src/cast.rs
+++ b/compiler/rustc_hir_typeck/src/cast.rs
@@ -448,13 +448,35 @@ impl<'a, 'tcx> CastCheck<'tcx> {
                             );
                         }
                     }
-                    let msg = "an `as` expression can only be used to convert between primitive \
-                               types or to coerce to a specific trait object";
+
+                    let (msg, note) = if let ty::Adt(adt, _) = self.expr_ty.kind()
+                        && adt.is_enum()
+                        && self.cast_ty.is_numeric()
+                    {
+                        (
+                            "an `as` expression can be used to convert enum types to numeric \
+                             types only if the enum type is unit-only or field-less",
+                            Some(
+                                "see https://doc.rust-lang.org/reference/items/enumerations.html#casting for more information",
+                            ),
+                        )
+                    } else {
+                        (
+                            "an `as` expression can only be used to convert between primitive \
+                             types or to coerce to a specific trait object",
+                            None,
+                        )
+                    };
+
                     if label {
                         err.span_label(self.span, msg);
                     } else {
                         err.note(msg);
                     }
+
+                    if let Some(note) = note {
+                        err.note(note);
+                    }
                 } else {
                     err.span_label(self.span, "invalid cast");
                 }
diff --git a/compiler/rustc_middle/src/ty/sty.rs b/compiler/rustc_middle/src/ty/sty.rs
index d6e3385c600..f592acd4b6f 100644
--- a/compiler/rustc_middle/src/ty/sty.rs
+++ b/compiler/rustc_middle/src/ty/sty.rs
@@ -2279,12 +2279,12 @@ impl<'tcx> Ty<'tcx> {
     }
 
     /// Returns the type of metadata for (potentially fat) pointers to this type,
-    /// and a boolean signifying if this is conditional on this type being `Sized`.
-    pub fn ptr_metadata_ty(
+    /// or the struct tail if the metadata type cannot be determined.
+    pub fn ptr_metadata_ty_or_tail(
         self,
         tcx: TyCtxt<'tcx>,
         normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>,
-    ) -> (Ty<'tcx>, bool) {
+    ) -> Result<Ty<'tcx>, Ty<'tcx>> {
         let tail = tcx.struct_tail_with_normalize(self, normalize, || {});
         match tail.kind() {
             // Sized types
@@ -2307,31 +2307,47 @@ impl<'tcx> Ty<'tcx> {
             | ty::Error(_)
             // Extern types have metadata = ().
             | ty::Foreign(..)
-            // `dyn*` has no metadata
+            // `dyn*` has metadata = ().
             | ty::Dynamic(_, _, ty::DynStar)
-            // If returned by `struct_tail_without_normalization` this is a unit struct
+            // If returned by `struct_tail_with_normalize` this is a unit struct
             // without any fields, or not a struct, and therefore is Sized.
             | ty::Adt(..)
-            // If returned by `struct_tail_without_normalization` this is the empty tuple,
+            // If returned by `struct_tail_with_normalize` this is the empty tuple,
             // a.k.a. unit type, which is Sized
-            | ty::Tuple(..) => (tcx.types.unit, false),
+            | ty::Tuple(..) => Ok(tcx.types.unit),
+
+            ty::Str | ty::Slice(_) => Ok(tcx.types.usize),
 
-            ty::Str | ty::Slice(_) => (tcx.types.usize, false),
             ty::Dynamic(_, _, ty::Dyn) => {
                 let dyn_metadata = tcx.require_lang_item(LangItem::DynMetadata, None);
-                (tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()]), false)
-            },
+                Ok(tcx.type_of(dyn_metadata).instantiate(tcx, &[tail.into()]))
+            }
 
-            // type parameters only have unit metadata if they're sized, so return true
-            // to make sure we double check this during confirmation
-            ty::Param(_) |  ty::Alias(..) => (tcx.types.unit, true),
+            // We don't know the metadata of `self`, but it must be equal to the
+            // metadata of `tail`.
+            ty::Param(_) | ty::Alias(..) => Err(tail),
 
             ty::Infer(ty::TyVar(_))
             | ty::Bound(..)
             | ty::Placeholder(..)
-            | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => {
-                bug!("`ptr_metadata_ty` applied to unexpected type: {:?} (tail = {:?})", self, tail)
-            }
+            | ty::Infer(ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_)) => bug!(
+                "`ptr_metadata_ty_or_tail` applied to unexpected type: {self:?} (tail = {tail:?})"
+            ),
+        }
+    }
+
+    /// Returns the type of metadata for (potentially fat) pointers to this type.
+    /// Causes an ICE if the metadata type cannot be determined.
+    pub fn ptr_metadata_ty(
+        self,
+        tcx: TyCtxt<'tcx>,
+        normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>,
+    ) -> Ty<'tcx> {
+        match self.ptr_metadata_ty_or_tail(tcx, normalize) {
+            Ok(metadata) => metadata,
+            Err(tail) => bug!(
+                "`ptr_metadata_ty` failed to get metadata for type: {self:?} (tail = {tail:?})"
+            ),
         }
     }
 
diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
index 239072dfc8e..3b9515e1670 100644
--- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs
@@ -8,7 +8,7 @@ use rustc_infer::traits::query::NoSolution;
 use rustc_infer::traits::Reveal;
 use rustc_middle::traits::solve::inspect::ProbeKind;
 use rustc_middle::traits::solve::{
-    CandidateSource, CanonicalResponse, Certainty, Goal, QueryResult,
+    CandidateSource, CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult,
 };
 use rustc_middle::traits::BuiltinImplSource;
 use rustc_middle::ty::fast_reject::{SimplifiedType, TreatParams};
@@ -276,25 +276,16 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         &mut self,
         goal: Goal<'tcx, G>,
     ) -> Vec<Candidate<'tcx>> {
-        let dummy_candidate = |this: &mut EvalCtxt<'_, 'tcx>, certainty| {
-            let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc);
-            let result = this.evaluate_added_goals_and_make_canonical_response(certainty).unwrap();
-            let mut dummy_probe = this.inspect.new_probe();
-            dummy_probe.probe_kind(ProbeKind::TraitCandidate { source, result: Ok(result) });
-            this.inspect.finish_probe(dummy_probe);
-            vec![Candidate { source, result }]
-        };
-
         let Some(normalized_self_ty) =
             self.try_normalize_ty(goal.param_env, goal.predicate.self_ty())
         else {
             debug!("overflow while evaluating self type");
-            return dummy_candidate(self, Certainty::OVERFLOW);
+            return self.forced_ambiguity(MaybeCause::Overflow);
         };
 
         if normalized_self_ty.is_ty_var() {
             debug!("self type has been normalized to infer");
-            return dummy_candidate(self, Certainty::AMBIGUOUS);
+            return self.forced_ambiguity(MaybeCause::Ambiguity);
         }
 
         let goal =
@@ -315,11 +306,26 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
 
         self.assemble_param_env_candidates(goal, &mut candidates);
 
-        self.assemble_coherence_unknowable_candidates(goal, &mut candidates);
+        match self.solver_mode() {
+            SolverMode::Normal => self.discard_impls_shadowed_by_env(goal, &mut candidates),
+            SolverMode::Coherence => {
+                self.assemble_coherence_unknowable_candidates(goal, &mut candidates)
+            }
+        }
 
         candidates
     }
 
+    fn forced_ambiguity(&mut self, cause: MaybeCause) -> Vec<Candidate<'tcx>> {
+        let source = CandidateSource::BuiltinImpl(BuiltinImplSource::Misc);
+        let certainty = Certainty::Maybe(cause);
+        let result = self.evaluate_added_goals_and_make_canonical_response(certainty).unwrap();
+        let mut dummy_probe = self.inspect.new_probe();
+        dummy_probe.probe_kind(ProbeKind::TraitCandidate { source, result: Ok(result) });
+        self.inspect.finish_probe(dummy_probe);
+        vec![Candidate { source, result }]
+    }
+
     #[instrument(level = "debug", skip_all)]
     fn assemble_non_blanket_impl_candidates<G: GoalKind<'tcx>>(
         &mut self,
@@ -779,6 +785,12 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         }
     }
 
+    /// In coherence we have to not only care about all impls we know about, but
+    /// also consider impls which may get added in a downstream or sibling crate
+    /// or which an upstream impl may add in a minor release.
+    ///
+    /// To do so we add an ambiguous candidate in case such an unknown impl could
+    /// apply to the current goal.
     #[instrument(level = "debug", skip_all)]
     fn assemble_coherence_unknowable_candidates<G: GoalKind<'tcx>>(
         &mut self,
@@ -786,11 +798,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         candidates: &mut Vec<Candidate<'tcx>>,
     ) {
         let tcx = self.tcx();
-        match self.solver_mode() {
-            SolverMode::Normal => return,
-            SolverMode::Coherence => {}
-        };
-
         let result = self.probe_misc_candidate("coherence unknowable").enter(|ecx| {
             let trait_ref = goal.predicate.trait_ref(tcx);
             #[derive(Debug)]
@@ -820,6 +827,51 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         }
     }
 
+    /// If there's a where-bound for the current goal, do not use any impl candidates
+    /// to prove the current goal. Most importantly, if there is a where-bound which does
+    /// not specify any associated types, we do not allow normalizing the associated type
+    /// by using an impl, even if it would apply.
+    ///
+    ///  <https://github.com/rust-lang/trait-system-refactor-initiative/issues/76>
+    // FIXME(@lcnr): The current structure here makes me unhappy and feels ugly. idk how
+    // to improve this however. However, this should make it fairly straightforward to refine
+    // the filtering going forward, so it seems alright-ish for now.
+    fn discard_impls_shadowed_by_env<G: GoalKind<'tcx>>(
+        &mut self,
+        goal: Goal<'tcx, G>,
+        candidates: &mut Vec<Candidate<'tcx>>,
+    ) {
+        let tcx = self.tcx();
+        let trait_goal: Goal<'tcx, ty::TraitPredicate<'tcx>> =
+            goal.with(tcx, goal.predicate.trait_ref(tcx));
+        let mut trait_candidates_from_env = Vec::new();
+        self.assemble_param_env_candidates(trait_goal, &mut trait_candidates_from_env);
+        self.assemble_alias_bound_candidates(trait_goal, &mut trait_candidates_from_env);
+        if !trait_candidates_from_env.is_empty() {
+            let trait_env_result = self.merge_candidates(trait_candidates_from_env);
+            match trait_env_result.unwrap().value.certainty {
+                // If proving the trait goal succeeds by using the env,
+                // we freely drop all impl candidates.
+                //
+                // FIXME(@lcnr): It feels like this could easily hide
+                // a forced ambiguity candidate added earlier.
+                // This feels dangerous.
+                Certainty::Yes => {
+                    candidates.retain(|c| match c.source {
+                        CandidateSource::Impl(_) | CandidateSource::BuiltinImpl(_) => false,
+                        CandidateSource::ParamEnv(_) | CandidateSource::AliasBound => true,
+                    });
+                }
+                // If it is still ambiguous we instead just force the whole goal
+                // to be ambig and wait for inference constraints. See
+                // tests/ui/traits/next-solver/env-shadows-impls/ambig-env-no-shadow.rs
+                Certainty::Maybe(cause) => {
+                    *candidates = self.forced_ambiguity(cause);
+                }
+            }
+        }
+    }
+
     /// If there are multiple ways to prove a trait or projection goal, we have
     /// to somehow try to merge the candidates into one. If that fails, we return
     /// ambiguity.
@@ -832,34 +884,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
         let responses = candidates.iter().map(|c| c.result).collect::<Vec<_>>();
         if let Some(result) = self.try_merge_responses(&responses) {
             return Ok(result);
+        } else {
+            self.flounder(&responses)
         }
-
-        // We then check whether we should prioritize `ParamEnv` candidates.
-        //
-        // Doing so is incomplete and would therefore be unsound during coherence.
-        match self.solver_mode() {
-            SolverMode::Coherence => (),
-            // Prioritize `ParamEnv` candidates only if they do not guide inference.
-            //
-            // This is still incomplete as we may add incorrect region bounds.
-            SolverMode::Normal => {
-                let param_env_responses = candidates
-                    .iter()
-                    .filter(|c| {
-                        matches!(
-                            c.source,
-                            CandidateSource::ParamEnv(_) | CandidateSource::AliasBound
-                        )
-                    })
-                    .map(|c| c.result)
-                    .collect::<Vec<_>>();
-                if let Some(result) = self.try_merge_responses(&param_env_responses) {
-                    // We strongly prefer alias and param-env bounds here, even if they affect inference.
-                    // See https://github.com/rust-lang/trait-system-refactor-initiative/issues/11.
-                    return Ok(result);
-                }
-            }
-        }
-        self.flounder(&responses)
     }
 }
diff --git a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
index 47ba549022d..d177109c420 100644
--- a/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
+++ b/compiler/rustc_trait_selection/src/solve/normalizes_to/mod.rs
@@ -491,6 +491,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
         goal: Goal<'tcx, Self>,
     ) -> QueryResult<'tcx> {
         let tcx = ecx.tcx();
+        let metadata_def_id = tcx.require_lang_item(LangItem::Metadata, None);
+        assert_eq!(metadata_def_id, goal.predicate.def_id());
         ecx.probe_misc_candidate("builtin pointee").enter(|ecx| {
             let metadata_ty = match goal.predicate.self_ty().kind() {
                 ty::Bool
@@ -522,7 +524,10 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
                 }
 
                 ty::Alias(_, _) | ty::Param(_) | ty::Placeholder(..) => {
-                    // FIXME(ptr_metadata): It would also be possible to return a `Ok(Ambig)` with no constraints.
+                    // This is the "fallback impl" for type parameters, unnormalizable projections
+                    // and opaque types: If the `self_ty` is `Sized`, then the metadata is `()`.
+                    // FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't
+                    // exist. Instead, `Pointee<Metadata = ()>` should be a supertrait of `Sized`.
                     let sized_predicate = ty::TraitRef::from_lang_item(
                         tcx,
                         LangItem::Sized,
@@ -536,30 +541,16 @@ impl<'tcx> assembly::GoalKind<'tcx> for NormalizesTo<'tcx> {
 
                 ty::Adt(def, args) if def.is_struct() => match def.non_enum_variant().tail_opt() {
                     None => tcx.types.unit,
-                    Some(field_def) => {
-                        let self_ty = field_def.ty(tcx, args);
-                        // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
-                        ecx.add_goal(
-                            GoalSource::Misc,
-                            goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)),
-                        );
-                        return ecx
-                            .evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
+                    Some(tail_def) => {
+                        let tail_ty = tail_def.ty(tcx, args);
+                        Ty::new_projection(tcx, metadata_def_id, [tail_ty])
                     }
                 },
                 ty::Adt(_, _) => tcx.types.unit,
 
                 ty::Tuple(elements) => match elements.last() {
                     None => tcx.types.unit,
-                    Some(&self_ty) => {
-                        // FIXME(-Znext-solver=coinductive): Should this be `GoalSource::ImplWhereBound`?
-                        ecx.add_goal(
-                            GoalSource::Misc,
-                            goal.with(tcx, goal.predicate.with_self_ty(tcx, self_ty)),
-                        );
-                        return ecx
-                            .evaluate_added_goals_and_make_canonical_response(Certainty::Yes);
-                    }
+                    Some(&tail_ty) => Ty::new_projection(tcx, metadata_def_id, [tail_ty]),
                 },
 
                 ty::Infer(
diff --git a/compiler/rustc_trait_selection/src/traits/project.rs b/compiler/rustc_trait_selection/src/traits/project.rs
index fd8306bbc0b..d271a0ea33b 100644
--- a/compiler/rustc_trait_selection/src/traits/project.rs
+++ b/compiler/rustc_trait_selection/src/traits/project.rs
@@ -1935,10 +1935,11 @@ fn assemble_candidates_from_impls<'cx, 'tcx>(
                         // Integers and floats are always Sized, and so have unit type metadata.
                         | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(..)) => true,
 
-                        // type parameters, opaques, and unnormalized projections have pointer
-                        // metadata if they're known (e.g. by the param_env) to be sized
+                        // We normalize from `Wrapper<Tail>::Metadata` to `Tail::Metadata` if able.
+                        // Otherwise, type parameters, opaques, and unnormalized projections have
+                        // unit metadata if they're known (e.g. by the param_env) to be sized.
                         ty::Param(_) | ty::Alias(..)
-                            if selcx.infcx.predicate_must_hold_modulo_regions(
+                            if self_ty != tail || selcx.infcx.predicate_must_hold_modulo_regions(
                                 &obligation.with(
                                     selcx.tcx(),
                                     ty::TraitRef::from_lang_item(selcx.tcx(), LangItem::Sized, obligation.cause.span(),[self_ty]),
@@ -2312,7 +2313,7 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
         assert_eq!(metadata_def_id, item_def_id);
 
         let mut obligations = Vec::new();
-        let (metadata_ty, check_is_sized) = self_ty.ptr_metadata_ty(tcx, |ty| {
+        let normalize = |ty| {
             normalize_with_depth_to(
                 selcx,
                 obligation.param_env,
@@ -2321,16 +2322,27 @@ fn confirm_builtin_candidate<'cx, 'tcx>(
                 ty,
                 &mut obligations,
             )
+        };
+        let metadata_ty = self_ty.ptr_metadata_ty_or_tail(tcx, normalize).unwrap_or_else(|tail| {
+            if tail == self_ty {
+                // This is the "fallback impl" for type parameters, unnormalizable projections
+                // and opaque types: If the `self_ty` is `Sized`, then the metadata is `()`.
+                // FIXME(ptr_metadata): This impl overlaps with the other impls and shouldn't
+                // exist. Instead, `Pointee<Metadata = ()>` should be a supertrait of `Sized`.
+                let sized_predicate = ty::TraitRef::from_lang_item(
+                    tcx,
+                    LangItem::Sized,
+                    obligation.cause.span(),
+                    [self_ty],
+                );
+                obligations.push(obligation.with(tcx, sized_predicate));
+                tcx.types.unit
+            } else {
+                // We know that `self_ty` has the same metadata as `tail`. This allows us
+                // to prove predicates like `Wrapper<Tail>::Metadata == Tail::Metadata`.
+                Ty::new_projection(tcx, metadata_def_id, [tail])
+            }
         });
-        if check_is_sized {
-            let sized_predicate = ty::TraitRef::from_lang_item(
-                tcx,
-                LangItem::Sized,
-                obligation.cause.span(),
-                [self_ty],
-            );
-            obligations.push(obligation.with(tcx, sized_predicate));
-        }
         (metadata_ty.into(), obligations)
     } else {
         bug!("unexpected builtin trait with associated type: {:?}", obligation.predicate);
diff --git a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
index 4c0c57377e0..a050b30317a 100644
--- a/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
+++ b/compiler/rustc_trait_selection/src/traits/query/evaluate_obligation.rs
@@ -41,7 +41,28 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
     /// not entirely accurate if inference variables are involved.
     ///
     /// This version may conservatively fail when outlives obligations
-    /// are required.
+    /// are required. Therefore, this version should only be used for
+    /// optimizations or diagnostics and be treated as if it can always
+    /// return `false`.
+    ///
+    /// # Example
+    ///
+    /// ```
+    /// # #![allow(dead_code)]
+    /// trait Trait {}
+    ///
+    /// fn check<T: Trait>() {}
+    ///
+    /// fn foo<T: 'static>()
+    /// where
+    ///     &'static T: Trait,
+    /// {
+    ///     // Evaluating `&'?0 T: Trait` adds a `'?0: 'static` outlives obligation,
+    ///     // which means that `predicate_must_hold_considering_regions` will return
+    ///     // `false`.
+    ///     check::<&'_ T>();
+    /// }
+    /// ```
     fn predicate_must_hold_considering_regions(
         &self,
         obligation: &PredicateObligation<'tcx>,
diff --git a/library/core/src/option.rs b/library/core/src/option.rs
index 901fb962a78..d4c40c49934 100644
--- a/library/core/src/option.rs
+++ b/library/core/src/option.rs
@@ -1073,18 +1073,23 @@ impl<T> Option<T> {
         }
     }
 
-    /// Calls the provided closure with a reference to the contained value (if [`Some`]).
+    /// Calls a function with a reference to the contained value if [`Some`].
+    ///
+    /// Returns the original option.
     ///
     /// # Examples
     ///
     /// ```
-    /// let v = vec![1, 2, 3, 4, 5];
+    /// let list = vec![1, 2, 3];
     ///
-    /// // prints "got: 4"
-    /// let x: Option<&usize> = v.get(3).inspect(|x| println!("got: {x}"));
+    /// // prints "got: 2"
+    /// let x = list
+    ///     .get(1)
+    ///     .inspect(|x| println!("got: {x}"))
+    ///     .expect("list should be long enough");
     ///
     /// // prints nothing
-    /// let x: Option<&usize> = v.get(5).inspect(|x| println!("got: {x}"));
+    /// list.get(5).inspect(|x| println!("got: {x}"));
     /// ```
     #[inline]
     #[stable(feature = "result_option_inspect", since = "1.76.0")]
diff --git a/library/core/src/result.rs b/library/core/src/result.rs
index 1f448984e53..80f7fe75e82 100644
--- a/library/core/src/result.rs
+++ b/library/core/src/result.rs
@@ -830,7 +830,9 @@ impl<T, E> Result<T, E> {
         }
     }
 
-    /// Calls the provided closure with a reference to the contained value (if [`Ok`]).
+    /// Calls a function with a reference to the contained value if [`Ok`].
+    ///
+    /// Returns the original result.
     ///
     /// # Examples
     ///
@@ -851,7 +853,9 @@ impl<T, E> Result<T, E> {
         self
     }
 
-    /// Calls the provided closure with a reference to the contained error (if [`Err`]).
+    /// Calls a function with a reference to the contained value if [`Err`].
+    ///
+    /// Returns the original result.
     ///
     /// # Examples
     ///
diff --git a/library/std/src/sys/mod.rs b/library/std/src/sys/mod.rs
index e03e98b18d2..fae21636897 100644
--- a/library/std/src/sys/mod.rs
+++ b/library/std/src/sys/mod.rs
@@ -7,6 +7,7 @@ mod personality;
 
 pub mod cmath;
 pub mod os_str;
+pub mod path;
 
 // FIXME(117276): remove this, move feature implementations into individual
 //                submodules.
diff --git a/library/std/src/sys/pal/hermit/mod.rs b/library/std/src/sys/pal/hermit/mod.rs
index 3c83afa280b..57cc656e266 100644
--- a/library/std/src/sys/pal/hermit/mod.rs
+++ b/library/std/src/sys/pal/hermit/mod.rs
@@ -28,8 +28,6 @@ pub mod io;
 pub mod memchr;
 pub mod net;
 pub mod os;
-#[path = "../unix/path.rs"]
-pub mod path;
 #[path = "../unsupported/pipe.rs"]
 pub mod pipe;
 #[path = "../unsupported/process.rs"]
diff --git a/library/std/src/sys/pal/sgx/mod.rs b/library/std/src/sys/pal/sgx/mod.rs
index a769fc1ef59..46f3e5b401d 100644
--- a/library/std/src/sys/pal/sgx/mod.rs
+++ b/library/std/src/sys/pal/sgx/mod.rs
@@ -22,7 +22,6 @@ pub mod io;
 pub mod memchr;
 pub mod net;
 pub mod os;
-pub mod path;
 #[path = "../unsupported/pipe.rs"]
 pub mod pipe;
 #[path = "../unsupported/process.rs"]
diff --git a/library/std/src/sys/pal/solid/mod.rs b/library/std/src/sys/pal/solid/mod.rs
index 46699e64169..be8e0033902 100644
--- a/library/std/src/sys/pal/solid/mod.rs
+++ b/library/std/src/sys/pal/solid/mod.rs
@@ -29,7 +29,6 @@ pub mod fs;
 pub mod io;
 pub mod net;
 pub mod os;
-pub mod path;
 #[path = "../unsupported/pipe.rs"]
 pub mod pipe;
 #[path = "../unsupported/process.rs"]
diff --git a/library/std/src/sys/pal/teeos/mod.rs b/library/std/src/sys/pal/teeos/mod.rs
index 95a5b97ea42..7953104486c 100644
--- a/library/std/src/sys/pal/teeos/mod.rs
+++ b/library/std/src/sys/pal/teeos/mod.rs
@@ -25,8 +25,6 @@ pub mod net;
 #[path = "../unsupported/once.rs"]
 pub mod once;
 pub mod os;
-#[path = "../unix/path.rs"]
-pub mod path;
 #[path = "../unsupported/pipe.rs"]
 pub mod pipe;
 #[path = "../unsupported/process.rs"]
diff --git a/library/std/src/sys/pal/uefi/helpers.rs b/library/std/src/sys/pal/uefi/helpers.rs
index 9837cc89f2d..ba53ed88f37 100644
--- a/library/std/src/sys/pal/uefi/helpers.rs
+++ b/library/std/src/sys/pal/uefi/helpers.rs
@@ -146,3 +146,11 @@ pub(crate) fn image_handle_protocol<T>(protocol_guid: Guid) -> Option<NonNull<T>
     let system_handle = uefi::env::try_image_handle()?;
     open_protocol(system_handle, protocol_guid).ok()
 }
+
+/// Get RuntimeServices
+pub(crate) fn runtime_services() -> Option<NonNull<r_efi::efi::RuntimeServices>> {
+    let system_table: NonNull<r_efi::efi::SystemTable> =
+        crate::os::uefi::env::try_system_table()?.cast();
+    let runtime_services = unsafe { (*system_table.as_ptr()).runtime_services };
+    NonNull::new(runtime_services)
+}
diff --git a/library/std/src/sys/pal/uefi/mod.rs b/library/std/src/sys/pal/uefi/mod.rs
index 9ee753aa1a0..efa4ed67c50 100644
--- a/library/std/src/sys/pal/uefi/mod.rs
+++ b/library/std/src/sys/pal/uefi/mod.rs
@@ -26,7 +26,6 @@ pub mod net;
 #[path = "../unsupported/once.rs"]
 pub mod once;
 pub mod os;
-pub mod path;
 #[path = "../unsupported/pipe.rs"]
 pub mod pipe;
 #[path = "../unsupported/process.rs"]
@@ -38,7 +37,6 @@ pub mod thread;
 pub mod thread_local_key;
 #[path = "../unsupported/thread_parking.rs"]
 pub mod thread_parking;
-#[path = "../unsupported/time.rs"]
 pub mod time;
 
 mod helpers;
diff --git a/library/std/src/sys/pal/uefi/path.rs b/library/std/src/sys/pal/uefi/path.rs
deleted file mode 100644
index 106682eee56..00000000000
--- a/library/std/src/sys/pal/uefi/path.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-use super::unsupported;
-use crate::ffi::OsStr;
-use crate::io;
-use crate::path::{Path, PathBuf, Prefix};
-
-pub const MAIN_SEP_STR: &str = "\\";
-pub const MAIN_SEP: char = '\\';
-
-#[inline]
-pub fn is_sep_byte(b: u8) -> bool {
-    b == b'\\'
-}
-
-#[inline]
-pub fn is_verbatim_sep(b: u8) -> bool {
-    b == b'\\'
-}
-
-pub fn parse_prefix(_p: &OsStr) -> Option<Prefix<'_>> {
-    None
-}
-
-pub(crate) fn absolute(_path: &Path) -> io::Result<PathBuf> {
-    unsupported()
-}
diff --git a/library/std/src/sys/pal/uefi/tests.rs b/library/std/src/sys/pal/uefi/tests.rs
index 8806eda3ac0..5eb36da922b 100644
--- a/library/std/src/sys/pal/uefi/tests.rs
+++ b/library/std/src/sys/pal/uefi/tests.rs
@@ -1,4 +1,6 @@
 use super::alloc::*;
+use super::time::*;
+use crate::time::Duration;
 
 #[test]
 fn align() {
@@ -19,3 +21,21 @@ fn align() {
         }
     }
 }
+
+#[test]
+fn epoch() {
+    let t = r_efi::system::Time {
+        year: 1970,
+        month: 1,
+        day: 1,
+        hour: 0,
+        minute: 0,
+        second: 0,
+        nanosecond: 0,
+        timezone: r_efi::efi::UNSPECIFIED_TIMEZONE,
+        daylight: 0,
+        pad1: 0,
+        pad2: 0,
+    };
+    assert_eq!(system_time_internal::uefi_time_to_duration(t), Duration::new(0, 0));
+}
diff --git a/library/std/src/sys/pal/uefi/time.rs b/library/std/src/sys/pal/uefi/time.rs
new file mode 100644
index 00000000000..68f428c38fb
--- /dev/null
+++ b/library/std/src/sys/pal/uefi/time.rs
@@ -0,0 +1,105 @@
+use crate::time::Duration;
+
+const SECS_IN_MINUTE: u64 = 60;
+const SECS_IN_HOUR: u64 = SECS_IN_MINUTE * 60;
+const SECS_IN_DAY: u64 = SECS_IN_HOUR * 24;
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct Instant(Duration);
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
+pub struct SystemTime(Duration);
+
+pub const UNIX_EPOCH: SystemTime = SystemTime(Duration::from_secs(0));
+
+impl Instant {
+    pub fn now() -> Instant {
+        panic!("time not implemented on this platform")
+    }
+
+    pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
+        self.0.checked_sub(other.0)
+    }
+
+    pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
+        Some(Instant(self.0.checked_add(*other)?))
+    }
+
+    pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
+        Some(Instant(self.0.checked_sub(*other)?))
+    }
+}
+
+impl SystemTime {
+    pub fn now() -> SystemTime {
+        system_time_internal::now()
+            .unwrap_or_else(|| panic!("time not implemented on this platform"))
+    }
+
+    pub fn sub_time(&self, other: &SystemTime) -> Result<Duration, Duration> {
+        self.0.checked_sub(other.0).ok_or_else(|| other.0 - self.0)
+    }
+
+    pub fn checked_add_duration(&self, other: &Duration) -> Option<SystemTime> {
+        Some(SystemTime(self.0.checked_add(*other)?))
+    }
+
+    pub fn checked_sub_duration(&self, other: &Duration) -> Option<SystemTime> {
+        Some(SystemTime(self.0.checked_sub(*other)?))
+    }
+}
+
+pub(crate) mod system_time_internal {
+    use super::super::helpers;
+    use super::*;
+    use crate::mem::MaybeUninit;
+    use crate::ptr::NonNull;
+    use r_efi::efi::{RuntimeServices, Time};
+
+    pub fn now() -> Option<SystemTime> {
+        let runtime_services: NonNull<RuntimeServices> = helpers::runtime_services()?;
+        let mut t: MaybeUninit<Time> = MaybeUninit::uninit();
+        let r = unsafe {
+            ((*runtime_services.as_ptr()).get_time)(t.as_mut_ptr(), crate::ptr::null_mut())
+        };
+
+        if r.is_error() {
+            return None;
+        }
+
+        let t = unsafe { t.assume_init() };
+
+        Some(SystemTime(uefi_time_to_duration(t)))
+    }
+
+    // This algorithm is based on the one described in the post
+    // https://blog.reverberate.org/2020/05/12/optimizing-date-algorithms.html
+    pub const fn uefi_time_to_duration(t: r_efi::system::Time) -> Duration {
+        assert!(t.month <= 12);
+        assert!(t.month != 0);
+
+        const YEAR_BASE: u32 = 4800; /* Before min year, multiple of 400. */
+
+        // Calculate the number of days since 1/1/1970
+        // Use 1 March as the start
+        let (m_adj, overflow): (u32, bool) = (t.month as u32).overflowing_sub(3);
+        let (carry, adjust): (u32, u32) = if overflow { (1, 12) } else { (0, 0) };
+        let y_adj: u32 = (t.year as u32) + YEAR_BASE - carry;
+        let month_days: u32 = (m_adj.wrapping_add(adjust) * 62719 + 769) / 2048;
+        let leap_days: u32 = y_adj / 4 - y_adj / 100 + y_adj / 400;
+        let days: u32 = y_adj * 365 + leap_days + month_days + (t.day as u32 - 1) - 2472632;
+
+        let localtime_epoch: u64 = (days as u64) * SECS_IN_DAY
+            + (t.second as u64)
+            + (t.minute as u64) * SECS_IN_MINUTE
+            + (t.hour as u64) * SECS_IN_HOUR;
+
+        let utc_epoch: u64 = if t.timezone == r_efi::efi::UNSPECIFIED_TIMEZONE {
+            localtime_epoch
+        } else {
+            (localtime_epoch as i64 + (t.timezone as i64) * SECS_IN_MINUTE as i64) as u64
+        };
+
+        Duration::new(utc_epoch, t.nanosecond)
+    }
+}
diff --git a/library/std/src/sys/pal/unix/mod.rs b/library/std/src/sys/pal/unix/mod.rs
index 43cb9d89be9..976a437c17f 100644
--- a/library/std/src/sys/pal/unix/mod.rs
+++ b/library/std/src/sys/pal/unix/mod.rs
@@ -27,7 +27,6 @@ pub mod net;
 #[cfg(target_os = "l4re")]
 pub use self::l4re::net;
 pub mod os;
-pub mod path;
 pub mod pipe;
 pub mod process;
 pub mod rand;
diff --git a/library/std/src/sys/pal/unsupported/mod.rs b/library/std/src/sys/pal/unsupported/mod.rs
index b56ded8579c..88f939cbab9 100644
--- a/library/std/src/sys/pal/unsupported/mod.rs
+++ b/library/std/src/sys/pal/unsupported/mod.rs
@@ -9,8 +9,6 @@ pub mod locks;
 pub mod net;
 pub mod once;
 pub mod os;
-#[path = "../unix/path.rs"]
-pub mod path;
 pub mod pipe;
 pub mod process;
 pub mod stdio;
diff --git a/library/std/src/sys/pal/wasi/mod.rs b/library/std/src/sys/pal/wasi/mod.rs
index 4ffc8ecdd67..116878ee996 100644
--- a/library/std/src/sys/pal/wasi/mod.rs
+++ b/library/std/src/sys/pal/wasi/mod.rs
@@ -30,8 +30,6 @@ pub mod io;
 
 pub mod net;
 pub mod os;
-#[path = "../unix/path.rs"]
-pub mod path;
 #[path = "../unsupported/pipe.rs"]
 pub mod pipe;
 #[path = "../unsupported/process.rs"]
diff --git a/library/std/src/sys/pal/wasm/mod.rs b/library/std/src/sys/pal/wasm/mod.rs
index 76306b618d8..567555118d7 100644
--- a/library/std/src/sys/pal/wasm/mod.rs
+++ b/library/std/src/sys/pal/wasm/mod.rs
@@ -28,8 +28,6 @@ pub mod io;
 pub mod net;
 #[path = "../unsupported/os.rs"]
 pub mod os;
-#[path = "../unix/path.rs"]
-pub mod path;
 #[path = "../unsupported/pipe.rs"]
 pub mod pipe;
 #[path = "../unsupported/process.rs"]
diff --git a/library/std/src/sys/pal/windows/fs.rs b/library/std/src/sys/pal/windows/fs.rs
index 2bdd3d96fa4..b82a83ae7a3 100644
--- a/library/std/src/sys/pal/windows/fs.rs
+++ b/library/std/src/sys/pal/windows/fs.rs
@@ -16,8 +16,8 @@ use crate::sys::{c, cvt, Align8};
 use crate::sys_common::{AsInner, FromInner, IntoInner};
 use crate::thread;
 
-use super::path::maybe_verbatim;
 use super::{api, to_u16s, IoResult};
+use crate::sys::path::maybe_verbatim;
 
 pub struct File {
     handle: Handle,
diff --git a/library/std/src/sys/pal/windows/mod.rs b/library/std/src/sys/pal/windows/mod.rs
index 364521dba40..726a4509f28 100644
--- a/library/std/src/sys/pal/windows/mod.rs
+++ b/library/std/src/sys/pal/windows/mod.rs
@@ -23,7 +23,6 @@ pub mod locks;
 pub mod memchr;
 pub mod net;
 pub mod os;
-pub mod path;
 pub mod pipe;
 pub mod process;
 pub mod rand;
@@ -210,7 +209,7 @@ pub fn to_u16s<S: AsRef<OsStr>>(s: S) -> crate::io::Result<Vec<u16>> {
 // Once the syscall has completed (errors bail out early) the second closure is
 // yielded the data which has been read from the syscall. The return value
 // from this closure is then the return value of the function.
-fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> crate::io::Result<T>
+pub fn fill_utf16_buf<F1, F2, T>(mut f1: F1, f2: F2) -> crate::io::Result<T>
 where
     F1: FnMut(*mut u16, c::DWORD) -> c::DWORD,
     F2: FnOnce(&[u16]) -> T,
@@ -274,7 +273,7 @@ where
     }
 }
 
-fn os2path(s: &[u16]) -> PathBuf {
+pub fn os2path(s: &[u16]) -> PathBuf {
     PathBuf::from(OsString::from_wide(s))
 }
 
diff --git a/library/std/src/sys/pal/xous/mod.rs b/library/std/src/sys/pal/xous/mod.rs
index b4948d7e583..c9bad4ef019 100644
--- a/library/std/src/sys/pal/xous/mod.rs
+++ b/library/std/src/sys/pal/xous/mod.rs
@@ -12,8 +12,6 @@ pub mod io;
 pub mod locks;
 pub mod net;
 pub mod os;
-#[path = "../unix/path.rs"]
-pub mod path;
 #[path = "../unsupported/pipe.rs"]
 pub mod pipe;
 #[path = "../unsupported/process.rs"]
diff --git a/library/std/src/sys/pal/zkvm/mod.rs b/library/std/src/sys/pal/zkvm/mod.rs
index 7f221dc4fd9..e859269831a 100644
--- a/library/std/src/sys/pal/zkvm/mod.rs
+++ b/library/std/src/sys/pal/zkvm/mod.rs
@@ -24,10 +24,6 @@ pub mod net;
 #[path = "../unsupported/once.rs"]
 pub mod once;
 pub mod os;
-#[path = "../unix/os_str.rs"]
-pub mod os_str;
-#[path = "../unix/path.rs"]
-pub mod path;
 #[path = "../unsupported/pipe.rs"]
 pub mod pipe;
 #[path = "../unsupported/process.rs"]
diff --git a/library/std/src/sys/path/mod.rs b/library/std/src/sys/path/mod.rs
new file mode 100644
index 00000000000..24a94ec7828
--- /dev/null
+++ b/library/std/src/sys/path/mod.rs
@@ -0,0 +1,18 @@
+cfg_if::cfg_if! {
+    if #[cfg(target_os = "windows")] {
+        mod windows;
+        pub use windows::*;
+    } else if #[cfg(all(target_vendor = "fortanix", target_env = "sgx"))] {
+        mod sgx;
+        pub use sgx::*;
+    } else if #[cfg(any(
+        target_os = "uefi",
+        target_os = "solid_asp3",
+    ))] {
+        mod unsupported_backslash;
+        pub use unsupported_backslash::*;
+    } else {
+        mod unix;
+        pub use unix::*;
+    }
+}
diff --git a/library/std/src/sys/pal/sgx/path.rs b/library/std/src/sys/path/sgx.rs
index c805c15e702..c805c15e702 100644
--- a/library/std/src/sys/pal/sgx/path.rs
+++ b/library/std/src/sys/path/sgx.rs
diff --git a/library/std/src/sys/pal/unix/path.rs b/library/std/src/sys/path/unix.rs
index 837f68d3eaf..837f68d3eaf 100644
--- a/library/std/src/sys/pal/unix/path.rs
+++ b/library/std/src/sys/path/unix.rs
diff --git a/library/std/src/sys/pal/solid/path.rs b/library/std/src/sys/path/unsupported_backslash.rs
index 7045c9be25b..7045c9be25b 100644
--- a/library/std/src/sys/pal/solid/path.rs
+++ b/library/std/src/sys/path/unsupported_backslash.rs
diff --git a/library/std/src/sys/pal/windows/path.rs b/library/std/src/sys/path/windows.rs
index d9684f21753..cebc7910231 100644
--- a/library/std/src/sys/pal/windows/path.rs
+++ b/library/std/src/sys/path/windows.rs
@@ -1,8 +1,8 @@
-use super::{c, fill_utf16_buf, to_u16s};
 use crate::ffi::{OsStr, OsString};
 use crate::io;
 use crate::path::{Path, PathBuf, Prefix};
 use crate::ptr;
+use crate::sys::pal::{c, fill_utf16_buf, os2path, to_u16s};
 
 #[cfg(test)]
 mod tests;
@@ -339,6 +339,6 @@ pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {
         // `lpfilename` is a pointer to a null terminated string that is not
         // invalidated until after `GetFullPathNameW` returns successfully.
         |buffer, size| unsafe { c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()) },
-        super::os2path,
+        os2path,
     )
 }
diff --git a/library/std/src/sys/pal/windows/path/tests.rs b/library/std/src/sys/path/windows/tests.rs
index 623c6236166..623c6236166 100644
--- a/library/std/src/sys/pal/windows/path/tests.rs
+++ b/library/std/src/sys/path/windows/tests.rs
diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs
index ec404ab8580..e63d60feff1 100644
--- a/src/bootstrap/src/core/download.rs
+++ b/src/bootstrap/src/core/download.rs
@@ -720,8 +720,10 @@ download-rustc = false
         if !tarball.exists() {
             let help_on_error = "ERROR: failed to download llvm from ci
 
-    HELP: old builds get deleted after a certain time
-    HELP: if trying to compile an old commit of rustc, disable `download-ci-llvm` in config.toml:
+    HELP: There could be two reasons behind this:
+        1) The host triple is not supported for `download-ci-llvm`.
+        2) Old builds get deleted after a certain time.
+    HELP: In either case, disable `download-ci-llvm` in your config.toml:
 
     [llvm]
     download-ci-llvm = false
diff --git a/src/ci/docker/run.sh b/src/ci/docker/run.sh
index 0db61204f77..bd5447ac835 100755
--- a/src/ci/docker/run.sh
+++ b/src/ci/docker/run.sh
@@ -72,8 +72,12 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then
       # Include cache version. Can be used to manually bust the Docker cache.
       echo "2" >> $hash_key
 
+      echo "Image input"
+      cat $hash_key
+
       cksum=$(sha512sum $hash_key | \
         awk '{print $1}')
+      echo "Image input checksum ${cksum}"
     fi
 
     dockerfile="$docker_dir/$image/Dockerfile"
@@ -84,9 +88,6 @@ if [ -f "$docker_dir/$image/Dockerfile" ]; then
         context="$script_dir"
     fi
     echo "::group::Building docker image for $image"
-    echo "Image input"
-    cat $hash_key
-    echo "Image input checksum ${cksum}"
 
     # Print docker version
     docker --version
diff --git a/src/tools/clippy/.github/driver.sh b/src/tools/clippy/.github/driver.sh
index c05c6ecc115..40a2aad0f53 100644..100755
--- a/src/tools/clippy/.github/driver.sh
+++ b/src/tools/clippy/.github/driver.sh
@@ -11,9 +11,16 @@ if [[ ${OS} == "Windows" ]]; then
 else
 	desired_sysroot=/tmp
 fi
+# Set --sysroot in command line
 sysroot=$(./target/debug/clippy-driver --sysroot $desired_sysroot --print sysroot)
 test "$sysroot" = $desired_sysroot
 
+# Set --sysroot in arg_file.txt and pass @arg_file.txt to command line
+echo "--sysroot=$desired_sysroot" > arg_file.txt
+sysroot=$(./target/debug/clippy-driver @arg_file.txt --print sysroot)
+test "$sysroot" = $desired_sysroot
+
+# Setting SYSROOT in command line
 sysroot=$(SYSROOT=$desired_sysroot ./target/debug/clippy-driver --print sysroot)
 test "$sysroot" = $desired_sysroot
 
@@ -24,6 +31,14 @@ test "$sysroot" = $desired_sysroot
     SYSROOT=/tmp RUSTFLAGS="--sysroot=$(rustc --print sysroot)" ../target/debug/cargo-clippy clippy --verbose
 )
 
+# Check that the --sysroot argument is only passed once via arg_file.txt (SYSROOT is ignored)
+(  
+    echo "fn main() {}" > target/driver_test.rs
+    echo "--sysroot="$(./target/debug/clippy-driver --print sysroot)"" > arg_file.txt
+    echo "--verbose" >> arg_file.txt
+    SYSROOT=/tmp ./target/debug/clippy-driver @arg_file.txt ./target/driver_test.rs
+)
+
 # Make sure this isn't set - clippy-driver should cope without it
 unset CARGO_MANIFEST_DIR
 
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 5fa45ceeb39..9b853567219 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -6,11 +6,65 @@ document.
 
 ## Unreleased / Beta / In Rust Nightly
 
-[09ac14c9...master](https://github.com/rust-lang/rust-clippy/compare/09ac14c9...master)
+[a859e5cc...master](https://github.com/rust-lang/rust-clippy/compare/a859e5cc...master)
+
+## Rust 1.76
+
+Current stable, released 2024-02-08
+
+[View all 85 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-11-02T20%3A23%3A40Z..2023-12-16T13%3A11%3A08Z+base%3Amaster)
+
+### New Lints
+
+- [`infinite_loop`]
+  [#11829](https://github.com/rust-lang/rust-clippy/pull/11829)
+- [`ineffective_open_options`]
+  [#11902](https://github.com/rust-lang/rust-clippy/pull/11902)
+- [`uninhabited_references`]
+  [#11878](https://github.com/rust-lang/rust-clippy/pull/11878)
+- [`repeat_vec_with_capacity`]
+  [#11597](https://github.com/rust-lang/rust-clippy/pull/11597)
+- [`test_attr_in_doctest`]
+  [#11872](https://github.com/rust-lang/rust-clippy/pull/11872)
+- [`option_map_or_err_ok`]
+  [#11864](https://github.com/rust-lang/rust-clippy/pull/11864)
+- [`join_absolute_paths`]
+  [#11453](https://github.com/rust-lang/rust-clippy/pull/11453)
+- [`impl_hash_borrow_with_str_and_bytes`]
+  [#11781](https://github.com/rust-lang/rust-clippy/pull/11781)
+- [`iter_over_hash_type`]
+  [#11791](https://github.com/rust-lang/rust-clippy/pull/11791)
+
+### Moves and Deprecations
+
+- Renamed `blocks_in_if_conditions` to [`blocks_in_conditions`]
+  [#11853](https://github.com/rust-lang/rust-clippy/pull/11853)
+- Moved [`implied_bounds_in_impls`] to `complexity` (Now warn-by-default)
+  [#11867](https://github.com/rust-lang/rust-clippy/pull/11867)
+- Moved [`if_same_then_else`] to `style` (Now warn-by-default)
+  [#11809](https://github.com/rust-lang/rust-clippy/pull/11809)
+
+### Enhancements
+
+- [`missing_safety_doc`], [`unnecessary_safety_doc`], [`missing_panics_doc`], [`missing_errors_doc`]:
+  Added the [`check-private-items`] configuration to enable lints on private items
+  [#11842](https://github.com/rust-lang/rust-clippy/pull/11842)
+
+### ICE Fixes
+
+- [`impl_trait_in_params`]: No longer crashes when a function has generics but no function parameters
+  [#11804](https://github.com/rust-lang/rust-clippy/pull/11804)
+- [`unused_enumerate_index`]: No longer crashes on empty tuples
+  [#11756](https://github.com/rust-lang/rust-clippy/pull/11756)
+
+### Others
+
+- Clippy now respects the `CARGO` environment value
+  [#11944](https://github.com/rust-lang/rust-clippy/pull/11944)
 
 ## Rust 1.75
 
-Current stable, released 2023-12-28
+Released 2023-12-28
 
 [View all 69 merged pull requests](https://github.com/rust-lang/rust-clippy/pulls?q=merged%3A2023-09-25T11%3A47%3A47Z..2023-11-02T16%3A41%3A59Z+base%3Amaster)
 
@@ -5198,6 +5252,7 @@ Released 2018-09-13
 [`implied_bounds_in_impls`]: https://rust-lang.github.io/rust-clippy/master/index.html#implied_bounds_in_impls
 [`impossible_comparisons`]: https://rust-lang.github.io/rust-clippy/master/index.html#impossible_comparisons
 [`imprecise_flops`]: https://rust-lang.github.io/rust-clippy/master/index.html#imprecise_flops
+[`incompatible_msrv`]: https://rust-lang.github.io/rust-clippy/master/index.html#incompatible_msrv
 [`inconsistent_digit_grouping`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_digit_grouping
 [`inconsistent_struct_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#inconsistent_struct_constructor
 [`incorrect_clone_impl_on_copy_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#incorrect_clone_impl_on_copy_type
@@ -5276,6 +5331,7 @@ Released 2018-09-13
 [`let_with_type_underscore`]: https://rust-lang.github.io/rust-clippy/master/index.html#let_with_type_underscore
 [`lines_filter_map_ok`]: https://rust-lang.github.io/rust-clippy/master/index.html#lines_filter_map_ok
 [`linkedlist`]: https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist
+[`lint_groups_priority`]: https://rust-lang.github.io/rust-clippy/master/index.html#lint_groups_priority
 [`little_endian_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#little_endian_bytes
 [`logic_bug`]: https://rust-lang.github.io/rust-clippy/master/index.html#logic_bug
 [`lossy_float_literal`]: https://rust-lang.github.io/rust-clippy/master/index.html#lossy_float_literal
@@ -5284,6 +5340,7 @@ Released 2018-09-13
 [`manual_assert`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_assert
 [`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_c_str_literals`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals
 [`manual_clamp`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_clamp
 [`manual_filter`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter
 [`manual_filter_map`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_filter_map
@@ -5523,6 +5580,7 @@ Released 2018-09-13
 [`redundant_slicing`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_slicing
 [`redundant_static_lifetimes`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_static_lifetimes
 [`redundant_type_annotations`]: https://rust-lang.github.io/rust-clippy/master/index.html#redundant_type_annotations
+[`ref_as_ptr`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_as_ptr
 [`ref_binding_to_reference`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_binding_to_reference
 [`ref_in_deref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_in_deref
 [`ref_option_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#ref_option_ref
@@ -5622,6 +5680,7 @@ Released 2018-09-13
 [`to_digit_is_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_digit_is_some
 [`to_string_in_display`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_display
 [`to_string_in_format_args`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_in_format_args
+[`to_string_trait_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#to_string_trait_impl
 [`todo`]: https://rust-lang.github.io/rust-clippy/master/index.html#todo
 [`too_many_arguments`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_arguments
 [`too_many_lines`]: https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines
@@ -5677,6 +5736,7 @@ Released 2018-09-13
 [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
 [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
 [`unnecessary_owned_empty_strings`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_owned_empty_strings
+[`unnecessary_result_map_or_else`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_result_map_or_else
 [`unnecessary_safety_comment`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_comment
 [`unnecessary_safety_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_safety_doc
 [`unnecessary_self_imports`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_self_imports
@@ -5819,4 +5879,6 @@ Released 2018-09-13
 [`enforce-iter-loop-reborrow`]: https://doc.rust-lang.org/clippy/lint_configuration.html#enforce-iter-loop-reborrow
 [`check-private-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#check-private-items
 [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior
+[`allow-comparison-to-zero`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allow-comparison-to-zero
+[`allowed-wildcard-imports`]: https://doc.rust-lang.org/clippy/lint_configuration.html#allowed-wildcard-imports
 <!-- end autogenerated links to configuration documentation -->
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index eda20531e40..321424880d1 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy"
-version = "0.1.77"
+version = "0.1.78"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md
index 7f7aff92bf1..f2357e2b5de 100644
--- a/src/tools/clippy/book/src/lint_configuration.md
+++ b/src/tools/clippy/book/src/lint_configuration.md
@@ -151,6 +151,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
 * [`manual_try_fold`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_try_fold)
 * [`manual_hash_one`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_hash_one)
 * [`iter_kv_map`](https://rust-lang.github.io/rust-clippy/master/index.html#iter_kv_map)
+* [`manual_c_str_literals`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_c_str_literals)
 
 
 ## `cognitive-complexity-threshold`
@@ -828,3 +829,35 @@ exported visibility, or whether they are marked as "pub".
 * [`pub_underscore_fields`](https://rust-lang.github.io/rust-clippy/master/index.html#pub_underscore_fields)
 
 
+## `allow-comparison-to-zero`
+Don't lint when comparing the result of a modulo operation to zero.
+
+**Default Value:** `true`
+
+---
+**Affected lints:**
+* [`modulo_arithmetic`](https://rust-lang.github.io/rust-clippy/master/index.html#modulo_arithmetic)
+
+
+## `allowed-wildcard-imports`
+List of path segments allowed to have wildcard imports.
+
+#### Example
+
+```toml
+allowed-wildcard-imports = [ "utils", "common" ]
+```
+
+#### Noteworthy
+
+1. This configuration has no effects if used with `warn_on_all_wildcard_imports = true`.
+2. Paths with any segment that containing the word 'prelude'
+are already allowed by default.
+
+**Default Value:** `[]`
+
+---
+**Affected lints:**
+* [`wildcard_imports`](https://rust-lang.github.io/rust-clippy/master/index.html#wildcard_imports)
+
+
diff --git a/src/tools/clippy/clippy_config/Cargo.toml b/src/tools/clippy/clippy_config/Cargo.toml
index 74b8e5eaa1c..2edc5ed592c 100644
--- a/src/tools/clippy/clippy_config/Cargo.toml
+++ b/src/tools/clippy/clippy_config/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "clippy_config"
-version = "0.1.77"
+version = "0.1.78"
 edition = "2021"
 
 # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs
index 4e9ddbf8259..9741b94d504 100644
--- a/src/tools/clippy/clippy_config/src/conf.rs
+++ b/src/tools/clippy/clippy_config/src/conf.rs
@@ -260,7 +260,7 @@ define_Conf! {
     ///
     /// Suppress lints whenever the suggested change would cause breakage for other crates.
     (avoid_breaking_exported_api: bool = true),
-    /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP.
+    /// Lint: MANUAL_SPLIT_ONCE, MANUAL_STR_REPEAT, CLONED_INSTEAD_OF_COPIED, REDUNDANT_FIELD_NAMES, OPTION_MAP_UNWRAP_OR, REDUNDANT_STATIC_LIFETIMES, FILTER_MAP_NEXT, CHECKED_CONVERSIONS, MANUAL_RANGE_CONTAINS, USE_SELF, MEM_REPLACE_WITH_DEFAULT, MANUAL_NON_EXHAUSTIVE, OPTION_AS_REF_DEREF, MAP_UNWRAP_OR, MATCH_LIKE_MATCHES_MACRO, MANUAL_STRIP, MISSING_CONST_FOR_FN, UNNESTED_OR_PATTERNS, FROM_OVER_INTO, PTR_AS_PTR, IF_THEN_SOME_ELSE_NONE, APPROX_CONSTANT, DEPRECATED_CFG_ATTR, INDEX_REFUTABLE_SLICE, MAP_CLONE, BORROW_AS_PTR, MANUAL_BITS, ERR_EXPECT, CAST_ABS_TO_UNSIGNED, UNINLINED_FORMAT_ARGS, MANUAL_CLAMP, MANUAL_LET_ELSE, UNCHECKED_DURATION_SUBTRACTION, COLLAPSIBLE_STR_REPLACE, SEEK_FROM_CURRENT, SEEK_REWIND, UNNECESSARY_LAZY_EVALUATIONS, TRANSMUTE_PTR_TO_REF, ALMOST_COMPLETE_RANGE, NEEDLESS_BORROW, DERIVABLE_IMPLS, MANUAL_IS_ASCII_CHECK, MANUAL_REM_EUCLID, MANUAL_RETAIN, TYPE_REPETITION_IN_BOUNDS, TUPLE_ARRAY_CONVERSIONS, MANUAL_TRY_FOLD, MANUAL_HASH_ONE, ITER_KV_MAP, MANUAL_C_STR_LITERALS.
     ///
     /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml`
     #[default_text = ""]
@@ -567,6 +567,26 @@ define_Conf! {
     /// Lint "public" fields in a struct that are prefixed with an underscore based on their
     /// exported visibility, or whether they are marked as "pub".
     (pub_underscore_fields_behavior: PubUnderscoreFieldsBehaviour = PubUnderscoreFieldsBehaviour::PubliclyExported),
+    /// Lint: MODULO_ARITHMETIC.
+    ///
+    /// Don't lint when comparing the result of a modulo operation to zero.
+    (allow_comparison_to_zero: bool = true),
+    /// Lint: WILDCARD_IMPORTS.
+    ///
+    /// List of path segments allowed to have wildcard imports.
+    ///
+    /// #### Example
+    ///
+    /// ```toml
+    /// allowed-wildcard-imports = [ "utils", "common" ]
+    /// ```
+    ///
+    /// #### Noteworthy
+    ///
+    /// 1. This configuration has no effects if used with `warn_on_all_wildcard_imports = true`.
+    /// 2. Paths with any segment that containing the word 'prelude'
+    /// are already allowed by default.
+    (allowed_wildcard_imports: FxHashSet<String> = FxHashSet::default()),
 }
 
 /// Search for the configuration file.
diff --git a/src/tools/clippy/clippy_config/src/msrvs.rs b/src/tools/clippy/clippy_config/src/msrvs.rs
index 72d5b9aff28..f4389db627d 100644
--- a/src/tools/clippy/clippy_config/src/msrvs.rs
+++ b/src/tools/clippy/clippy_config/src/msrvs.rs
@@ -3,6 +3,7 @@ use rustc_semver::RustcVersion;
 use rustc_session::Session;
 use rustc_span::{sym, Symbol};
 use serde::Deserialize;
+use std::fmt;
 
 macro_rules! msrv_aliases {
     ($($major:literal,$minor:literal,$patch:literal {
@@ -16,6 +17,8 @@ macro_rules! msrv_aliases {
 
 // names may refer to stabilized feature flags or library items
 msrv_aliases! {
+    1,77,0 { C_STR_LITERALS }
+    1,76,0 { PTR_FROM_REF }
     1,71,0 { TUPLE_ARRAY_CONVERSIONS, BUILD_HASHER_HASH_ONE }
     1,70,0 { OPTION_RESULT_IS_VARIANT_AND, BINARY_HEAP_RETAIN }
     1,68,0 { PATH_MAIN_SEPARATOR_STR }
@@ -58,6 +61,16 @@ pub struct Msrv {
     stack: Vec<RustcVersion>,
 }
 
+impl fmt::Display for Msrv {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        if let Some(msrv) = self.current() {
+            write!(f, "{msrv}")
+        } else {
+            f.write_str("1.0.0")
+        }
+    }
+}
+
 impl<'de> Deserialize<'de> for Msrv {
     fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
     where
diff --git a/src/tools/clippy/clippy_lints/Cargo.toml b/src/tools/clippy/clippy_lints/Cargo.toml
index 416e9a680dd..6e6e315bb65 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.77"
+version = "0.1.78"
 description = "A bunch of helpful lints to avoid common pitfalls in Rust"
 repository = "https://github.com/rust-lang/rust-clippy"
 readme = "README.md"
diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs
index e11f83f2260..2d1c250ace9 100644
--- a/src/tools/clippy/clippy_lints/src/booleans.rs
+++ b/src/tools/clippy/clippy_lints/src/booleans.rs
@@ -499,6 +499,7 @@ struct NotSimplificationVisitor<'a, 'tcx> {
 impl<'a, 'tcx> Visitor<'tcx> for NotSimplificationVisitor<'a, 'tcx> {
     fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
         if let ExprKind::Unary(UnOp::Not, inner) = &expr.kind
+            && !expr.span.from_expansion()
             && !inner.span.from_expansion()
             && let Some(suggestion) = simplify_not(self.cx, inner)
             && self.cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
diff --git a/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs
new file mode 100644
index 00000000000..a39b972b56a
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/cargo/lint_groups_priority.rs
@@ -0,0 +1,168 @@
+use super::LINT_GROUPS_PRIORITY;
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_errors::Applicability;
+use rustc_lint::{unerased_lint_store, LateContext};
+use rustc_span::{BytePos, Pos, SourceFile, Span, SyntaxContext};
+use serde::{Deserialize, Serialize};
+use std::collections::BTreeMap;
+use std::ops::Range;
+use std::path::Path;
+use toml::Spanned;
+
+#[derive(Deserialize, Serialize, Debug)]
+struct LintConfigTable {
+    level: String,
+    priority: Option<i64>,
+}
+
+#[derive(Deserialize, Debug)]
+#[serde(untagged)]
+enum LintConfig {
+    Level(String),
+    Table(LintConfigTable),
+}
+
+impl LintConfig {
+    fn level(&self) -> &str {
+        match self {
+            LintConfig::Level(level) => level,
+            LintConfig::Table(table) => &table.level,
+        }
+    }
+
+    fn priority(&self) -> i64 {
+        match self {
+            LintConfig::Level(_) => 0,
+            LintConfig::Table(table) => table.priority.unwrap_or(0),
+        }
+    }
+
+    fn is_implicit(&self) -> bool {
+        if let LintConfig::Table(table) = self {
+            table.priority.is_none()
+        } else {
+            true
+        }
+    }
+}
+
+type LintTable = BTreeMap<Spanned<String>, Spanned<LintConfig>>;
+
+#[derive(Deserialize, Debug)]
+struct Lints {
+    #[serde(default)]
+    rust: LintTable,
+    #[serde(default)]
+    clippy: LintTable,
+}
+
+#[derive(Deserialize, Debug)]
+struct CargoToml {
+    lints: Lints,
+}
+
+#[derive(Default, Debug)]
+struct LintsAndGroups {
+    lints: Vec<Spanned<String>>,
+    groups: Vec<(Spanned<String>, Spanned<LintConfig>)>,
+}
+
+fn toml_span(range: Range<usize>, file: &SourceFile) -> Span {
+    Span::new(
+        file.start_pos + BytePos::from_usize(range.start),
+        file.start_pos + BytePos::from_usize(range.end),
+        SyntaxContext::root(),
+        None,
+    )
+}
+
+fn check_table(cx: &LateContext<'_>, table: LintTable, groups: &FxHashSet<&str>, file: &SourceFile) {
+    let mut by_priority = BTreeMap::<_, LintsAndGroups>::new();
+    for (name, config) in table {
+        let lints_and_groups = by_priority.entry(config.as_ref().priority()).or_default();
+        if groups.contains(name.get_ref().as_str()) {
+            lints_and_groups.groups.push((name, config));
+        } else {
+            lints_and_groups.lints.push(name);
+        }
+    }
+    let low_priority = by_priority
+        .iter()
+        .find(|(_, lints_and_groups)| !lints_and_groups.lints.is_empty())
+        .map_or(-1, |(&lowest_lint_priority, _)| lowest_lint_priority - 1);
+
+    for (priority, LintsAndGroups { lints, groups }) in by_priority {
+        let Some(last_lint_alphabetically) = lints.last() else {
+            continue;
+        };
+
+        for (group, config) in groups {
+            span_lint_and_then(
+                cx,
+                LINT_GROUPS_PRIORITY,
+                toml_span(group.span(), file),
+                &format!(
+                    "lint group `{}` has the same priority ({priority}) as a lint",
+                    group.as_ref()
+                ),
+                |diag| {
+                    let config_span = toml_span(config.span(), file);
+                    if config.as_ref().is_implicit() {
+                        diag.span_label(config_span, "has an implicit priority of 0");
+                    }
+                    // add the label to next lint after this group that has the same priority
+                    let lint = lints
+                        .iter()
+                        .filter(|lint| lint.span().start > group.span().start)
+                        .min_by_key(|lint| lint.span().start)
+                        .unwrap_or(last_lint_alphabetically);
+                    diag.span_label(toml_span(lint.span(), file), "has the same priority as this lint");
+                    diag.note("the order of the lints in the table is ignored by Cargo");
+                    let mut suggestion = String::new();
+                    Serialize::serialize(
+                        &LintConfigTable {
+                            level: config.as_ref().level().into(),
+                            priority: Some(low_priority),
+                        },
+                        toml::ser::ValueSerializer::new(&mut suggestion),
+                    )
+                    .unwrap();
+                    diag.span_suggestion_verbose(
+                        config_span,
+                        format!(
+                            "to have lints override the group set `{}` to a lower priority",
+                            group.as_ref()
+                        ),
+                        suggestion,
+                        Applicability::MaybeIncorrect,
+                    );
+                },
+            );
+        }
+    }
+}
+
+pub fn check(cx: &LateContext<'_>) {
+    if let Ok(file) = cx.tcx.sess.source_map().load_file(Path::new("Cargo.toml"))
+        && let Some(src) = file.src.as_deref()
+        && let Ok(cargo_toml) = toml::from_str::<CargoToml>(src)
+    {
+        let mut rustc_groups = FxHashSet::default();
+        let mut clippy_groups = FxHashSet::default();
+        for (group, ..) in unerased_lint_store(cx.tcx.sess).get_lint_groups() {
+            match group.split_once("::") {
+                None => {
+                    rustc_groups.insert(group);
+                },
+                Some(("clippy", group)) => {
+                    clippy_groups.insert(group);
+                },
+                _ => {},
+            }
+        }
+
+        check_table(cx, cargo_toml.lints.rust, &rustc_groups, &file);
+        check_table(cx, cargo_toml.lints.clippy, &clippy_groups, &file);
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/cargo/mod.rs b/src/tools/clippy/clippy_lints/src/cargo/mod.rs
index d8107f61f37..95d5449781b 100644
--- a/src/tools/clippy/clippy_lints/src/cargo/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/cargo/mod.rs
@@ -1,5 +1,6 @@
 mod common_metadata;
 mod feature_name;
+mod lint_groups_priority;
 mod multiple_crate_versions;
 mod wildcard_dependencies;
 
@@ -165,6 +166,43 @@ declare_clippy_lint! {
     "wildcard dependencies being used"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for lint groups with the same priority as lints in the `Cargo.toml`
+    /// [`[lints]` table](https://doc.rust-lang.org/cargo/reference/manifest.html#the-lints-section).
+    ///
+    /// This lint will be removed once [cargo#12918](https://github.com/rust-lang/cargo/issues/12918)
+    /// is resolved.
+    ///
+    /// ### Why is this bad?
+    /// The order of lints in the `[lints]` is ignored, to have a lint override a group the
+    /// `priority` field needs to be used, otherwise the sort order is undefined.
+    ///
+    /// ### Known problems
+    /// Does not check lints inherited using `lints.workspace = true`
+    ///
+    /// ### Example
+    /// ```toml
+    /// # Passed as `--allow=clippy::similar_names --warn=clippy::pedantic`
+    /// # which results in `similar_names` being `warn`
+    /// [lints.clippy]
+    /// pedantic = "warn"
+    /// similar_names = "allow"
+    /// ```
+    /// Use instead:
+    /// ```toml
+    /// # Passed as `--warn=clippy::pedantic --allow=clippy::similar_names`
+    /// # which results in `similar_names` being `allow`
+    /// [lints.clippy]
+    /// pedantic = { level = "warn", priority = -1 }
+    /// similar_names = "allow"
+    /// ```
+    #[clippy::version = "1.76.0"]
+    pub LINT_GROUPS_PRIORITY,
+    correctness,
+    "a lint group in `Cargo.toml` at the same priority as a lint"
+}
+
 pub struct Cargo {
     pub allowed_duplicate_crates: FxHashSet<String>,
     pub ignore_publish: bool,
@@ -175,7 +213,8 @@ impl_lint_pass!(Cargo => [
     REDUNDANT_FEATURE_NAMES,
     NEGATIVE_FEATURE_NAMES,
     MULTIPLE_CRATE_VERSIONS,
-    WILDCARD_DEPENDENCIES
+    WILDCARD_DEPENDENCIES,
+    LINT_GROUPS_PRIORITY,
 ]);
 
 impl LateLintPass<'_> for Cargo {
@@ -188,6 +227,8 @@ impl LateLintPass<'_> for Cargo {
         ];
         static WITH_DEPS_LINTS: &[&Lint] = &[MULTIPLE_CRATE_VERSIONS];
 
+        lint_groups_priority::check(cx);
+
         if !NO_DEPS_LINTS
             .iter()
             .all(|&lint| is_lint_allowed(cx, lint, CRATE_HIR_ID))
diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs
index e05b8f66d86..14f2f4a7f59 100644
--- a/src/tools/clippy/clippy_lints/src/casts/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs
@@ -18,6 +18,7 @@ mod fn_to_numeric_cast_any;
 mod fn_to_numeric_cast_with_truncation;
 mod ptr_as_ptr;
 mod ptr_cast_constness;
+mod ref_as_ptr;
 mod unnecessary_cast;
 mod utils;
 mod zero_ptr;
@@ -689,6 +690,30 @@ declare_clippy_lint! {
     "using `0 as *{const, mut} T`"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for casts of references to pointer using `as`
+    /// and suggests `std::ptr::from_ref` and `std::ptr::from_mut` instead.
+    ///
+    /// ### Why is this bad?
+    /// Using `as` casts may result in silently changing mutability or type.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// let a_ref = &1;
+    /// let a_ptr = a_ref as *const _;
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// let a_ref = &1;
+    /// let a_ptr = std::ptr::from_ref(a_ref);
+    /// ```
+    #[clippy::version = "1.77.0"]
+    pub REF_AS_PTR,
+    pedantic,
+    "using `as` to cast a reference to pointer"
+}
+
 pub struct Casts {
     msrv: Msrv,
 }
@@ -724,6 +749,7 @@ impl_lint_pass!(Casts => [
     AS_PTR_CAST_MUT,
     CAST_NAN_TO_INT,
     ZERO_PTR,
+    REF_AS_PTR,
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Casts {
@@ -771,7 +797,9 @@ impl<'tcx> LateLintPass<'tcx> for Casts {
 
             as_underscore::check(cx, expr, cast_to_hir);
 
-            if self.msrv.meets(msrvs::BORROW_AS_PTR) {
+            if self.msrv.meets(msrvs::PTR_FROM_REF) {
+                ref_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
+            } else if self.msrv.meets(msrvs::BORROW_AS_PTR) {
                 borrow_as_ptr::check(cx, expr, cast_expr, cast_to_hir);
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs
new file mode 100644
index 00000000000..d600d2aec1b
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/casts/ref_as_ptr.rs
@@ -0,0 +1,55 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::is_no_std_crate;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::sugg::Sugg;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, Mutability, Ty, TyKind};
+use rustc_lint::LateContext;
+use rustc_middle::ty::{self, TypeAndMut};
+
+use super::REF_AS_PTR;
+
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, cast_expr: &Expr<'_>, cast_to_hir_ty: &Ty<'_>) {
+    let (cast_from, cast_to) = (
+        cx.typeck_results().expr_ty(cast_expr),
+        cx.typeck_results().expr_ty(expr),
+    );
+
+    if matches!(cast_from.kind(), ty::Ref(..))
+        && let ty::RawPtr(TypeAndMut { mutbl: to_mutbl, .. }) = cast_to.kind()
+    {
+        let core_or_std = if is_no_std_crate(cx) { "core" } else { "std" };
+        let fn_name = match to_mutbl {
+            Mutability::Not => "from_ref",
+            Mutability::Mut => "from_mut",
+        };
+
+        let mut app = Applicability::MachineApplicable;
+        let turbofish = match &cast_to_hir_ty.kind {
+            TyKind::Infer => String::new(),
+            TyKind::Ptr(mut_ty) => {
+                if matches!(mut_ty.ty.kind, TyKind::Infer) {
+                    String::new()
+                } else {
+                    format!(
+                        "::<{}>",
+                        snippet_with_applicability(cx, mut_ty.ty.span, "/* type */", &mut app)
+                    )
+                }
+            },
+            _ => return,
+        };
+
+        let cast_expr_sugg = Sugg::hir_with_applicability(cx, cast_expr, "_", &mut app);
+
+        span_lint_and_sugg(
+            cx,
+            REF_AS_PTR,
+            expr.span,
+            "reference as raw pointer",
+            "try",
+            format!("{core_or_std}::ptr::{fn_name}{turbofish}({cast_expr_sugg})"),
+            app,
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index 639edd8da30..0a5baabd973 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -71,6 +71,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::borrow_deref_ref::BORROW_DEREF_REF_INFO,
     crate::box_default::BOX_DEFAULT_INFO,
     crate::cargo::CARGO_COMMON_METADATA_INFO,
+    crate::cargo::LINT_GROUPS_PRIORITY_INFO,
     crate::cargo::MULTIPLE_CRATE_VERSIONS_INFO,
     crate::cargo::NEGATIVE_FEATURE_NAMES_INFO,
     crate::cargo::REDUNDANT_FEATURE_NAMES_INFO,
@@ -96,6 +97,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::casts::FN_TO_NUMERIC_CAST_WITH_TRUNCATION_INFO,
     crate::casts::PTR_AS_PTR_INFO,
     crate::casts::PTR_CAST_CONSTNESS_INFO,
+    crate::casts::REF_AS_PTR_INFO,
     crate::casts::UNNECESSARY_CAST_INFO,
     crate::casts::ZERO_PTR_INFO,
     crate::checked_conversions::CHECKED_CONVERSIONS_INFO,
@@ -212,6 +214,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::implicit_saturating_add::IMPLICIT_SATURATING_ADD_INFO,
     crate::implicit_saturating_sub::IMPLICIT_SATURATING_SUB_INFO,
     crate::implied_bounds_in_impls::IMPLIED_BOUNDS_IN_IMPLS_INFO,
+    crate::incompatible_msrv::INCOMPATIBLE_MSRV_INFO,
     crate::inconsistent_struct_constructor::INCONSISTENT_STRUCT_CONSTRUCTOR_INFO,
     crate::index_refutable_slice::INDEX_REFUTABLE_SLICE_INFO,
     crate::indexing_slicing::INDEXING_SLICING_INFO,
@@ -384,6 +387,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::methods::ITER_SKIP_ZERO_INFO,
     crate::methods::ITER_WITH_DRAIN_INFO,
     crate::methods::JOIN_ABSOLUTE_PATHS_INFO,
+    crate::methods::MANUAL_C_STR_LITERALS_INFO,
     crate::methods::MANUAL_FILTER_MAP_INFO,
     crate::methods::MANUAL_FIND_MAP_INFO,
     crate::methods::MANUAL_IS_VARIANT_AND_INFO,
@@ -452,6 +456,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::methods::UNNECESSARY_JOIN_INFO,
     crate::methods::UNNECESSARY_LAZY_EVALUATIONS_INFO,
     crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO,
+    crate::methods::UNNECESSARY_RESULT_MAP_OR_ELSE_INFO,
     crate::methods::UNNECESSARY_SORT_BY_INFO,
     crate::methods::UNNECESSARY_TO_OWNED_INFO,
     crate::methods::UNWRAP_OR_DEFAULT_INFO,
@@ -656,6 +661,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::tests_outside_test_module::TESTS_OUTSIDE_TEST_MODULE_INFO,
     crate::thread_local_initializer_can_be_made_const::THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST_INFO,
     crate::to_digit_is_some::TO_DIGIT_IS_SOME_INFO,
+    crate::to_string_trait_impl::TO_STRING_TRAIT_IMPL_INFO,
     crate::trailing_empty_array::TRAILING_EMPTY_ARRAY_INFO,
     crate::trait_bounds::TRAIT_DUPLICATION_IN_BOUNDS_INFO,
     crate::trait_bounds::TYPE_REPETITION_IN_BOUNDS_INFO,
diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs
index ba452775015..2b4ce6ddfaa 100644
--- a/src/tools/clippy/clippy_lints/src/doc/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs
@@ -226,7 +226,7 @@ declare_clippy_lint! {
     ///     unimplemented!();
     /// }
     /// ```
-    #[clippy::version = "1.40.0"]
+    #[clippy::version = "1.76.0"]
     pub TEST_ATTR_IN_DOCTEST,
     suspicious,
     "presence of `#[test]` in code examples"
diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
index 450cee4007c..40be71a0e5d 100644
--- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs
+++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
@@ -3,15 +3,14 @@ use clippy_utils::higher::VecArgs;
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::type_diagnostic_name;
 use clippy_utils::usage::{local_used_after_expr, local_used_in};
-use clippy_utils::{higher, is_adjusted, path_to_local, path_to_local_id};
+use clippy_utils::{get_path_from_caller_to_method_type, higher, is_adjusted, path_to_local, path_to_local_id};
 use rustc_errors::Applicability;
-use rustc_hir::def_id::DefId;
 use rustc_hir::{BindingAnnotation, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, TyKind, Unsafety};
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{
-    self, Binder, ClosureArgs, ClosureKind, EarlyBinder, FnSig, GenericArg, GenericArgKind, GenericArgsRef,
-    ImplPolarity, List, Region, RegionKind, Ty, TypeVisitableExt, TypeckResults,
+    self, Binder, ClosureArgs, ClosureKind, FnSig, GenericArg, GenericArgKind, ImplPolarity, List, Region, RegionKind,
+    Ty, TypeVisitableExt, TypeckResults,
 };
 use rustc_session::declare_lint_pass;
 use rustc_span::symbol::sym;
@@ -21,8 +20,8 @@ use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for closures which just call another function where
-    /// the function can be called directly. `unsafe` functions or calls where types
-    /// get adjusted are ignored.
+    /// the function can be called directly. `unsafe` functions, calls where types
+    /// get adjusted or where the callee is marked `#[track_caller]` are ignored.
     ///
     /// ### Why is this bad?
     /// Needlessly creating a closure adds code for no benefit
@@ -136,7 +135,14 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
                     .map_or(callee_ty, |a| a.target.peel_refs());
 
                 let sig = match callee_ty_adjusted.kind() {
-                    ty::FnDef(def, _) => cx.tcx.fn_sig(def).skip_binder().skip_binder(),
+                    ty::FnDef(def, _) => {
+                        // Rewriting `x(|| f())` to `x(f)` where f is marked `#[track_caller]` moves the `Location`
+                        if cx.tcx.has_attr(*def, sym::track_caller) {
+                            return;
+                        }
+
+                        cx.tcx.fn_sig(def).skip_binder().skip_binder()
+                    },
                     ty::FnPtr(sig) => sig.skip_binder(),
                     ty::Closure(_, subs) => cx
                         .tcx
@@ -186,6 +192,7 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
             },
             ExprKind::MethodCall(path, self_, args, _) if check_inputs(typeck, body.params, Some(self_), args) => {
                 if let Some(method_def_id) = typeck.type_dependent_def_id(body.value.hir_id)
+                    && !cx.tcx.has_attr(method_def_id, sym::track_caller)
                     && check_sig(cx, closure, cx.tcx.fn_sig(method_def_id).skip_binder().skip_binder())
                 {
                     span_lint_and_then(
@@ -195,11 +202,12 @@ impl<'tcx> LateLintPass<'tcx> for EtaReduction {
                         "redundant closure",
                         |diag| {
                             let args = typeck.node_args(body.value.hir_id);
-                            let name = get_ufcs_type_name(cx, method_def_id, args);
+                            let caller = self_.hir_id.owner.def_id;
+                            let type_name = get_path_from_caller_to_method_type(cx.tcx, caller, method_def_id, args);
                             diag.span_suggestion(
                                 expr.span,
                                 "replace the closure with the method itself",
-                                format!("{}::{}", name, path.ident.name),
+                                format!("{}::{}", type_name, path.ident.name),
                                 Applicability::MachineApplicable,
                             );
                         },
@@ -301,27 +309,3 @@ fn has_late_bound_to_non_late_bound_regions(from_sig: FnSig<'_>, to_sig: FnSig<'
         .zip(to_sig.inputs_and_output)
         .any(|(from_ty, to_ty)| check_ty(from_ty, to_ty))
 }
-
-fn get_ufcs_type_name<'tcx>(cx: &LateContext<'tcx>, method_def_id: DefId, args: GenericArgsRef<'tcx>) -> String {
-    let assoc_item = cx.tcx.associated_item(method_def_id);
-    let def_id = assoc_item.container_id(cx.tcx);
-    match assoc_item.container {
-        ty::TraitContainer => cx.tcx.def_path_str(def_id),
-        ty::ImplContainer => {
-            let ty = cx.tcx.type_of(def_id).instantiate_identity();
-            match ty.kind() {
-                ty::Adt(adt, _) => cx.tcx.def_path_str(adt.did()),
-                ty::Array(..)
-                | ty::Dynamic(..)
-                | ty::Never
-                | ty::RawPtr(_)
-                | ty::Ref(..)
-                | ty::Slice(_)
-                | ty::Tuple(_) => {
-                    format!("<{}>", EarlyBinder::bind(ty).instantiate(cx.tcx, args))
-                },
-                _ => ty.to_string(),
-            }
-        },
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs
new file mode 100644
index 00000000000..f2f0e7d4266
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs
@@ -0,0 +1,133 @@
+use clippy_config::msrvs::Msrv;
+use clippy_utils::diagnostics::span_lint;
+use rustc_attr::{StabilityLevel, StableSince};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty::TyCtxt;
+use rustc_semver::RustcVersion;
+use rustc_session::impl_lint_pass;
+use rustc_span::def_id::DefId;
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// This lint checks that no function newer than the defined MSRV (minimum
+    /// supported rust version) is used in the crate.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// It would prevent the crate to be actually used with the specified MSRV.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// // MSRV of 1.3.0
+    /// use std::thread::sleep;
+    /// use std::time::Duration;
+    ///
+    /// // Sleep was defined in `1.4.0`.
+    /// sleep(Duration::new(1, 0));
+    /// ```
+    ///
+    /// To fix this problem, either increase your MSRV or use another item
+    /// available in your current MSRV.
+    #[clippy::version = "1.77.0"]
+    pub INCOMPATIBLE_MSRV,
+    suspicious,
+    "ensures that all items used in the crate are available for the current MSRV"
+}
+
+pub struct IncompatibleMsrv {
+    msrv: Msrv,
+    is_above_msrv: FxHashMap<DefId, RustcVersion>,
+}
+
+impl_lint_pass!(IncompatibleMsrv => [INCOMPATIBLE_MSRV]);
+
+impl IncompatibleMsrv {
+    pub fn new(msrv: Msrv) -> Self {
+        Self {
+            msrv,
+            is_above_msrv: FxHashMap::default(),
+        }
+    }
+
+    #[allow(clippy::cast_lossless)]
+    fn get_def_id_version(&mut self, tcx: TyCtxt<'_>, def_id: DefId) -> RustcVersion {
+        if let Some(version) = self.is_above_msrv.get(&def_id) {
+            return *version;
+        }
+        let version = if let Some(version) = tcx
+            .lookup_stability(def_id)
+            .and_then(|stability| match stability.level {
+                StabilityLevel::Stable {
+                    since: StableSince::Version(version),
+                    ..
+                } => Some(RustcVersion::new(
+                    version.major as _,
+                    version.minor as _,
+                    version.patch as _,
+                )),
+                _ => None,
+            }) {
+            version
+        } else if let Some(parent_def_id) = tcx.opt_parent(def_id) {
+            self.get_def_id_version(tcx, parent_def_id)
+        } else {
+            RustcVersion::new(1, 0, 0)
+        };
+        self.is_above_msrv.insert(def_id, version);
+        version
+    }
+
+    fn emit_lint_if_under_msrv(&mut self, cx: &LateContext<'_>, def_id: DefId, span: Span) {
+        if def_id.is_local() {
+            // We don't check local items since their MSRV is supposed to always be valid.
+            return;
+        }
+        let version = self.get_def_id_version(cx.tcx, def_id);
+        if self.msrv.meets(version) {
+            return;
+        }
+        self.emit_lint_for(cx, span, version);
+    }
+
+    fn emit_lint_for(&self, cx: &LateContext<'_>, span: Span, version: RustcVersion) {
+        span_lint(
+            cx,
+            INCOMPATIBLE_MSRV,
+            span,
+            &format!(
+                "current MSRV (Minimum Supported Rust Version) is `{}` but this item is stable since `{version}`",
+                self.msrv
+            ),
+        );
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for IncompatibleMsrv {
+    extract_msrv_attr!(LateContext);
+
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+        if self.msrv.current().is_none() {
+            // If there is no MSRV, then no need to check anything...
+            return;
+        }
+        match expr.kind {
+            ExprKind::MethodCall(_, _, _, span) => {
+                if let Some(method_did) = cx.typeck_results().type_dependent_def_id(expr.hir_id) {
+                    self.emit_lint_if_under_msrv(cx, method_did, span);
+                }
+            },
+            ExprKind::Call(call, [_]) => {
+                if let ExprKind::Path(qpath) = call.kind
+                    && let Some(path_def_id) = cx.qpath_res(&qpath, call.hir_id).opt_def_id()
+                {
+                    self.emit_lint_if_under_msrv(cx, path_def_id, call.span);
+                }
+            },
+            _ => {},
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs
index 8110c1970d9..6c6eff9ba48 100644
--- a/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs
+++ b/src/tools/clippy/clippy_lints/src/iter_over_hash_type.rs
@@ -34,7 +34,7 @@ declare_clippy_lint! {
     ///         let value = &my_map[key];
     ///     }
     /// ```
-    #[clippy::version = "1.75.0"]
+    #[clippy::version = "1.76.0"]
     pub ITER_OVER_HASH_TYPE,
     restriction,
     "iterating over unordered hash-based types (`HashMap` and `HashSet`)"
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index f6608b22953..14bd82f9c97 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -31,6 +31,7 @@ extern crate rustc_abi;
 extern crate rustc_arena;
 extern crate rustc_ast;
 extern crate rustc_ast_pretty;
+extern crate rustc_attr;
 extern crate rustc_data_structures;
 extern crate rustc_driver;
 extern crate rustc_errors;
@@ -158,6 +159,7 @@ mod implicit_return;
 mod implicit_saturating_add;
 mod implicit_saturating_sub;
 mod implied_bounds_in_impls;
+mod incompatible_msrv;
 mod inconsistent_struct_constructor;
 mod index_refutable_slice;
 mod indexing_slicing;
@@ -330,6 +332,7 @@ mod temporary_assignment;
 mod tests_outside_test_module;
 mod thread_local_initializer_can_be_made_const;
 mod to_digit_is_some;
+mod to_string_trait_impl;
 mod trailing_empty_array;
 mod trait_bounds;
 mod transmute;
@@ -526,6 +529,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
         ref allowed_dotfiles,
         ref allowed_idents_below_min_chars,
         ref allowed_scripts,
+        ref allowed_wildcard_imports,
         ref arithmetic_side_effects_allowed_binary,
         ref arithmetic_side_effects_allowed_unary,
         ref arithmetic_side_effects_allowed,
@@ -580,6 +584,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
         check_private_items,
         pub_underscore_fields_behavior,
         ref allowed_duplicate_crates,
+        allow_comparison_to_zero,
 
         blacklisted_names: _,
         cyclomatic_complexity_threshold: _,
@@ -877,7 +882,12 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
         ))
     });
     store.register_early_pass(|| Box::new(option_env_unwrap::OptionEnvUnwrap));
-    store.register_late_pass(move |_| Box::new(wildcard_imports::WildcardImports::new(warn_on_all_wildcard_imports)));
+    store.register_late_pass(move |_| {
+        Box::new(wildcard_imports::WildcardImports::new(
+            warn_on_all_wildcard_imports,
+            allowed_wildcard_imports.clone(),
+        ))
+    });
     store.register_late_pass(|_| Box::<redundant_pub_crate::RedundantPubCrate>::default());
     store.register_late_pass(|_| Box::new(unnamed_address::UnnamedAddress));
     store.register_late_pass(|_| Box::<dereference::Dereferencing<'_>>::default());
@@ -973,7 +983,12 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     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())));
-    store.register_late_pass(move |_| Box::new(operators::Operators::new(verbose_bit_mask_threshold)));
+    store.register_late_pass(move |_| {
+        Box::new(operators::Operators::new(
+            verbose_bit_mask_threshold,
+            allow_comparison_to_zero,
+        ))
+    });
     store.register_late_pass(|_| Box::<std_instead_of_core::StdReexports>::default());
     store.register_late_pass(move |_| Box::new(instant_subtraction::InstantSubtraction::new(msrv())));
     store.register_late_pass(|_| Box::new(partialeq_to_none::PartialeqToNone));
@@ -1099,6 +1114,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(move |_| {
         Box::new(thread_local_initializer_can_be_made_const::ThreadLocalInitializerCanBeMadeConst::new(msrv()))
     });
+    store.register_late_pass(move |_| Box::new(incompatible_msrv::IncompatibleMsrv::new(msrv())));
+    store.register_late_pass(|_| Box::new(to_string_trait_impl::ToStringTraitImpl));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/loops/mod.rs b/src/tools/clippy/clippy_lints/src/loops/mod.rs
index 3c9bde86bb6..b5e39b33c6a 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mod.rs
@@ -672,7 +672,7 @@ declare_clippy_lint! {
     ///     }
     /// }
     /// ```
-    #[clippy::version = "1.75.0"]
+    #[clippy::version = "1.76.0"]
     pub INFINITE_LOOP,
     restriction,
     "possibly unintended infinite loop"
diff --git a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
index 62bc663191f..245a903f998 100644
--- a/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/never_loop.rs
@@ -201,12 +201,12 @@ fn never_loop_expr<'tcx>(
                 })
             })
         },
-        ExprKind::Block(b, l) => {
-            if l.is_some() {
+        ExprKind::Block(b, _) => {
+            if b.targeted_by_break {
                 local_labels.push((b.hir_id, false));
             }
             let ret = never_loop_block(cx, b, local_labels, main_loop_id);
-            let jumped_to = l.is_some() && local_labels.pop().unwrap().1;
+            let jumped_to = b.targeted_by_break && local_labels.pop().unwrap().1;
             match ret {
                 NeverLoopResult::Diverging if jumped_to => NeverLoopResult::Normal,
                 _ => ret,
diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs
index 1fe247dacb9..6f15fca089e 100644
--- a/src/tools/clippy/clippy_lints/src/manual_retain.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs
@@ -11,6 +11,7 @@ use rustc_lint::{LateContext, LateLintPass};
 use rustc_semver::RustcVersion;
 use rustc_session::impl_lint_pass;
 use rustc_span::symbol::sym;
+use rustc_span::Span;
 
 const ACCEPTABLE_METHODS: [&[&str]; 5] = [
     &paths::BINARYHEAP_ITER,
@@ -28,6 +29,7 @@ const ACCEPTABLE_TYPES: [(rustc_span::Symbol, Option<RustcVersion>); 7] = [
     (sym::Vec, None),
     (sym::VecDeque, None),
 ];
+const MAP_TYPES: [rustc_span::Symbol; 2] = [sym::BTreeMap, sym::HashMap];
 
 declare_clippy_lint! {
     /// ### What it does
@@ -44,6 +46,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// let mut vec = vec![0, 1, 2];
     /// vec.retain(|x| x % 2 == 0);
+    /// vec.retain(|x| x % 2 == 0);
     /// ```
     #[clippy::version = "1.64.0"]
     pub MANUAL_RETAIN,
@@ -74,9 +77,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualRetain {
             && let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id)
             && cx.tcx.is_diagnostic_item(sym::iterator_collect_fn, collect_def_id)
         {
-            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);
+            check_into_iter(cx, left_expr, target_expr, parent_expr.span, &self.msrv);
+            check_iter(cx, left_expr, target_expr, parent_expr.span, &self.msrv);
+            check_to_owned(cx, left_expr, target_expr, parent_expr.span, &self.msrv);
         }
     }
 
@@ -85,9 +88,9 @@ impl<'tcx> LateLintPass<'tcx> for ManualRetain {
 
 fn check_into_iter(
     cx: &LateContext<'_>,
-    parent_expr: &hir::Expr<'_>,
     left_expr: &hir::Expr<'_>,
     target_expr: &hir::Expr<'_>,
+    parent_expr_span: Span,
     msrv: &Msrv,
 ) {
     if let hir::ExprKind::MethodCall(_, into_iter_expr, [_], _) = &target_expr.kind
@@ -98,16 +101,39 @@ fn check_into_iter(
         && Some(into_iter_def_id) == cx.tcx.lang_items().into_iter_fn()
         && match_acceptable_type(cx, left_expr, msrv)
         && SpanlessEq::new(cx).eq_expr(left_expr, struct_expr)
+        && let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = target_expr.kind
+        && let hir::ExprKind::Closure(closure) = closure_expr.kind
+        && let filter_body = cx.tcx.hir().body(closure.body)
+        && let [filter_params] = filter_body.params
     {
-        suggest(cx, parent_expr, left_expr, target_expr);
+        if match_map_type(cx, left_expr) {
+            if let hir::PatKind::Tuple([key_pat, value_pat], _) = filter_params.pat.kind {
+                if let Some(sugg) = make_sugg(cx, key_pat, value_pat, left_expr, filter_body) {
+                    make_span_lint_and_sugg(cx, parent_expr_span, sugg);
+                }
+            }
+            // Cannot lint other cases because `retain` requires two parameters
+        } else {
+            // Can always move because `retain` and `filter` have the same bound on the predicate
+            // for other types
+            make_span_lint_and_sugg(
+                cx,
+                parent_expr_span,
+                format!(
+                    "{}.retain({})",
+                    snippet(cx, left_expr.span, ".."),
+                    snippet(cx, closure_expr.span, "..")
+                ),
+            );
+        }
     }
 }
 
 fn check_iter(
     cx: &LateContext<'_>,
-    parent_expr: &hir::Expr<'_>,
     left_expr: &hir::Expr<'_>,
     target_expr: &hir::Expr<'_>,
+    parent_expr_span: Span,
     msrv: &Msrv,
 ) {
     if let hir::ExprKind::MethodCall(_, filter_expr, [], _) = &target_expr.kind
@@ -122,16 +148,50 @@ fn check_iter(
         && 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)
+        && let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = filter_expr.kind
+        && let hir::ExprKind::Closure(closure) = closure_expr.kind
+        && let filter_body = cx.tcx.hir().body(closure.body)
+        && let [filter_params] = filter_body.params
     {
-        suggest(cx, parent_expr, left_expr, filter_expr);
+        match filter_params.pat.kind {
+            // hir::PatKind::Binding(_, _, _, None) => {
+            //     // Be conservative now. Do nothing here.
+            //     // TODO: Ideally, we can rewrite the lambda by stripping one level of reference
+            // },
+            hir::PatKind::Tuple([_, _], _) => {
+                // the `&&` reference for the `filter` method will be auto derefed to `ref`
+                // so, we can directly use the lambda
+                // https://doc.rust-lang.org/reference/patterns.html#binding-modes
+                make_span_lint_and_sugg(
+                    cx,
+                    parent_expr_span,
+                    format!(
+                        "{}.retain({})",
+                        snippet(cx, left_expr.span, ".."),
+                        snippet(cx, closure_expr.span, "..")
+                    ),
+                );
+            },
+            hir::PatKind::Ref(pat, _) => make_span_lint_and_sugg(
+                cx,
+                parent_expr_span,
+                format!(
+                    "{}.retain(|{}| {})",
+                    snippet(cx, left_expr.span, ".."),
+                    snippet(cx, pat.span, ".."),
+                    snippet(cx, filter_body.value.span, "..")
+                ),
+            ),
+            _ => {},
+        }
     }
 }
 
 fn check_to_owned(
     cx: &LateContext<'_>,
-    parent_expr: &hir::Expr<'_>,
     left_expr: &hir::Expr<'_>,
     target_expr: &hir::Expr<'_>,
+    parent_expr_span: Span,
     msrv: &Msrv,
 ) {
     if msrv.meets(msrvs::STRING_RETAIN)
@@ -147,43 +207,25 @@ fn check_to_owned(
         && let ty = cx.typeck_results().expr_ty(str_expr).peel_refs()
         && is_type_lang_item(cx, ty, hir::LangItem::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(&hir::Closure { body, .. }) = closure.kind
-        && let filter_body = cx.tcx.hir().body(body)
+        && let hir::ExprKind::MethodCall(_, _, [closure_expr], _) = filter_expr.kind
+        && let hir::ExprKind::Closure(closure) = closure_expr.kind
+        && let filter_body = cx.tcx.hir().body(closure.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(|{filter_param_ident}| {})",
-                snippet(cx, left_expr.span, ".."),
-                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(|{filter_param_ident}| {})",
+    {
+        if let hir::PatKind::Ref(pat, _) = filter_params.pat.kind {
+            make_span_lint_and_sugg(
+                cx,
+                parent_expr_span,
+                format!(
+                    "{}.retain(|{}| {})",
                     snippet(cx, left_expr.span, ".."),
+                    snippet(cx, pat.span, ".."),
                     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,
-        );
+        // Be conservative now. Do nothing for the `Binding` case.
+        // TODO: Ideally, we can rewrite the lambda by stripping one level of reference
     }
 }
 
@@ -229,3 +271,20 @@ fn match_acceptable_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>, msrv: &Msrv
             && acceptable_msrv.map_or(true, |acceptable_msrv| msrv.meets(acceptable_msrv))
     })
 }
+
+fn match_map_type(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
+    let expr_ty = cx.typeck_results().expr_ty(expr).peel_refs();
+    MAP_TYPES.iter().any(|ty| is_type_diagnostic_item(cx, expr_ty, *ty))
+}
+
+fn make_span_lint_and_sugg(cx: &LateContext<'_>, span: Span, sugg: String) {
+    span_lint_and_sugg(
+        cx,
+        MANUAL_RETAIN,
+        span,
+        "this expression can be written more simply using `.retain()`",
+        "consider calling `.retain()` instead",
+        sugg,
+        Applicability::MachineApplicable,
+    );
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs
new file mode 100644
index 00000000000..cb9fb373c10
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_c_str_literals.rs
@@ -0,0 +1,197 @@
+use clippy_config::msrvs::{self, Msrv};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::get_parent_expr;
+use clippy_utils::source::snippet;
+use rustc_ast::{LitKind, StrStyle};
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind, Node, QPath, TyKind};
+use rustc_lint::LateContext;
+use rustc_span::{sym, Span, Symbol};
+
+use super::MANUAL_C_STR_LITERALS;
+
+/// Checks:
+/// - `b"...".as_ptr()`
+/// - `b"...".as_ptr().cast()`
+/// - `"...".as_ptr()`
+/// - `"...".as_ptr().cast()`
+///
+/// Iff the parent call of `.cast()` isn't `CStr::from_ptr`, to avoid linting twice.
+pub(super) fn check_as_ptr<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    receiver: &'tcx Expr<'tcx>,
+    msrv: &Msrv,
+) {
+    if let ExprKind::Lit(lit) = receiver.kind
+        && let LitKind::ByteStr(_, StrStyle::Cooked) | LitKind::Str(_, StrStyle::Cooked) = lit.node
+        && let casts_removed = peel_ptr_cast_ancestors(cx, expr)
+        && !get_parent_expr(cx, casts_removed).is_some_and(
+            |parent| matches!(parent.kind, ExprKind::Call(func, _) if is_c_str_function(cx, func).is_some()),
+        )
+        && let Some(sugg) = rewrite_as_cstr(cx, lit.span)
+        && msrv.meets(msrvs::C_STR_LITERALS)
+    {
+        span_lint_and_sugg(
+            cx,
+            MANUAL_C_STR_LITERALS,
+            receiver.span,
+            "manually constructing a nul-terminated string",
+            r#"use a `c""` literal"#,
+            sugg,
+            // an additional cast may be needed, since the type of `CStr::as_ptr` and
+            // `"".as_ptr()` can differ and is platform dependent
+            Applicability::HasPlaceholders,
+        );
+    }
+}
+
+/// Checks if the callee is a "relevant" `CStr` function considered by this lint.
+/// Returns the function name.
+fn is_c_str_function(cx: &LateContext<'_>, func: &Expr<'_>) -> Option<Symbol> {
+    if let ExprKind::Path(QPath::TypeRelative(cstr, fn_name)) = &func.kind
+        && let TyKind::Path(QPath::Resolved(_, ty_path)) = &cstr.kind
+        && cx.tcx.lang_items().c_str() == ty_path.res.opt_def_id()
+    {
+        Some(fn_name.ident.name)
+    } else {
+        None
+    }
+}
+
+/// Checks calls to the `CStr` constructor functions:
+/// - `CStr::from_bytes_with_nul(..)`
+/// - `CStr::from_bytes_with_nul_unchecked(..)`
+/// - `CStr::from_ptr(..)`
+pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, func: &Expr<'_>, args: &[Expr<'_>], msrv: &Msrv) {
+    if let Some(fn_name) = is_c_str_function(cx, func)
+        && let [arg] = args
+        && msrv.meets(msrvs::C_STR_LITERALS)
+    {
+        match fn_name.as_str() {
+            name @ ("from_bytes_with_nul" | "from_bytes_with_nul_unchecked")
+                if !arg.span.from_expansion()
+                    && let ExprKind::Lit(lit) = arg.kind
+                    && let LitKind::ByteStr(_, StrStyle::Cooked) | LitKind::Str(_, StrStyle::Cooked) = lit.node =>
+            {
+                check_from_bytes(cx, expr, arg, name);
+            },
+            "from_ptr" => check_from_ptr(cx, expr, arg),
+            _ => {},
+        }
+    }
+}
+
+/// Checks `CStr::from_ptr(b"foo\0".as_ptr().cast())`
+fn check_from_ptr(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>) {
+    if let ExprKind::MethodCall(method, lit, ..) = peel_ptr_cast(arg).kind
+        && method.ident.name == sym::as_ptr
+        && !lit.span.from_expansion()
+        && let ExprKind::Lit(lit) = lit.kind
+        && let LitKind::ByteStr(_, StrStyle::Cooked) = lit.node
+        && let Some(sugg) = rewrite_as_cstr(cx, lit.span)
+    {
+        span_lint_and_sugg(
+            cx,
+            MANUAL_C_STR_LITERALS,
+            expr.span,
+            "calling `CStr::from_ptr` with a byte string literal",
+            r#"use a `c""` literal"#,
+            sugg,
+            Applicability::MachineApplicable,
+        );
+    }
+}
+/// Checks `CStr::from_bytes_with_nul(b"foo\0")`
+fn check_from_bytes(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, method: &str) {
+    let (span, applicability) = if let Some(parent) = get_parent_expr(cx, expr)
+        && let ExprKind::MethodCall(method, ..) = parent.kind
+        && [sym::unwrap, sym::expect].contains(&method.ident.name)
+    {
+        (parent.span, Applicability::MachineApplicable)
+    } else if method == "from_bytes_with_nul_unchecked" {
+        // `*_unchecked` returns `&CStr` directly, nothing needs to be changed
+        (expr.span, Applicability::MachineApplicable)
+    } else {
+        // User needs to remove error handling, can't be machine applicable
+        (expr.span, Applicability::HasPlaceholders)
+    };
+
+    let Some(sugg) = rewrite_as_cstr(cx, arg.span) else {
+        return;
+    };
+
+    span_lint_and_sugg(
+        cx,
+        MANUAL_C_STR_LITERALS,
+        span,
+        "calling `CStr::new` with a byte string literal",
+        r#"use a `c""` literal"#,
+        sugg,
+        applicability,
+    );
+}
+
+/// Rewrites a byte string literal to a c-str literal.
+/// `b"foo\0"` -> `c"foo"`
+///
+/// Returns `None` if it doesn't end in a NUL byte.
+fn rewrite_as_cstr(cx: &LateContext<'_>, span: Span) -> Option<String> {
+    let mut sugg = String::from("c") + snippet(cx, span.source_callsite(), "..").trim_start_matches('b');
+
+    // NUL byte should always be right before the closing quote.
+    if let Some(quote_pos) = sugg.rfind('"') {
+        // Possible values right before the quote:
+        // - literal NUL value
+        if sugg.as_bytes()[quote_pos - 1] == b'\0' {
+            sugg.remove(quote_pos - 1);
+        }
+        // - \x00
+        else if sugg[..quote_pos].ends_with("\\x00") {
+            sugg.replace_range(quote_pos - 4..quote_pos, "");
+        }
+        // - \0
+        else if sugg[..quote_pos].ends_with("\\0") {
+            sugg.replace_range(quote_pos - 2..quote_pos, "");
+        }
+        // No known suffix, so assume it's not a C-string.
+        else {
+            return None;
+        }
+    }
+
+    Some(sugg)
+}
+
+fn get_cast_target<'tcx>(e: &'tcx Expr<'tcx>) -> Option<&'tcx Expr<'tcx>> {
+    match &e.kind {
+        ExprKind::MethodCall(method, receiver, [], _) if method.ident.as_str() == "cast" => Some(receiver),
+        ExprKind::Cast(expr, _) => Some(expr),
+        _ => None,
+    }
+}
+
+/// `x.cast()` -> `x`
+/// `x as *const _` -> `x`
+/// `x` -> `x` (returns the same expression for non-cast exprs)
+fn peel_ptr_cast<'tcx>(e: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
+    get_cast_target(e).map_or(e, peel_ptr_cast)
+}
+
+/// Same as `peel_ptr_cast`, but the other way around, by walking up the ancestor cast expressions:
+///
+/// `foo(x.cast() as *const _)`
+///      ^ given this `x` expression, returns the `foo(...)` expression
+fn peel_ptr_cast_ancestors<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> &'tcx Expr<'tcx> {
+    let mut prev = e;
+    for (_, node) in cx.tcx.hir().parent_iter(e.hir_id) {
+        if let Node::Expr(e) = node
+            && get_cast_target(e).is_some()
+        {
+            prev = e;
+        } else {
+            break;
+        }
+    }
+    prev
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
index 52ea584a2c8..3226fa9cd3f 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_unwrap_or.rs
@@ -21,7 +21,7 @@ pub(super) fn check<'tcx>(
     unwrap_arg: &'tcx hir::Expr<'_>,
     msrv: &Msrv,
 ) -> bool {
-    // lint if the caller of `map()` is an `Option`
+    // lint if the caller of `map()` is an `Option` or a `Result`.
     let is_option = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Option);
     let is_result = is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result);
 
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 03bcf108914..e8a7a321bf4 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -51,6 +51,7 @@ mod iter_skip_zero;
 mod iter_with_drain;
 mod iterator_step_by_zero;
 mod join_absolute_paths;
+mod manual_c_str_literals;
 mod manual_is_variant_and;
 mod manual_next_back;
 mod manual_ok_or;
@@ -113,6 +114,7 @@ mod unnecessary_iter_cloned;
 mod unnecessary_join;
 mod unnecessary_lazy_eval;
 mod unnecessary_literal_unwrap;
+mod unnecessary_result_map_or_else;
 mod unnecessary_sort_by;
 mod unnecessary_to_owned;
 mod unwrap_expect_used;
@@ -3951,6 +3953,64 @@ declare_clippy_lint! {
     "cloning an `Option` via `as_ref().cloned()`"
 }
 
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for usage of `.map_or_else()` "map closure" for `Result` type.
+    ///
+    /// ### Why is this bad?
+    /// This can be written more concisely by using `unwrap_or_else()`.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// # fn handle_error(_: ()) -> u32 { 0 }
+    /// let x: Result<u32, ()> = Ok(0);
+    /// let y = x.map_or_else(|err| handle_error(err), |n| n);
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// # fn handle_error(_: ()) -> u32 { 0 }
+    /// let x: Result<u32, ()> = Ok(0);
+    /// let y = x.unwrap_or_else(|err| handle_error(err));
+    /// ```
+    #[clippy::version = "1.77.0"]
+    pub UNNECESSARY_RESULT_MAP_OR_ELSE,
+    suspicious,
+    "making no use of the \"map closure\" when calling `.map_or_else(|err| handle_error(err), |n| n)`"
+}
+
+declare_clippy_lint! {
+    /// Checks for the manual creation of C strings (a string with a `NUL` byte at the end), either
+    /// through one of the `CStr` constructor functions, or more plainly by calling `.as_ptr()`
+    /// on a (byte) string literal with a hardcoded `\0` byte at the end.
+    ///
+    /// ### Why is this bad?
+    /// This can be written more concisely using `c"str"` literals and is also less error-prone,
+    /// because the compiler checks for interior `NUL` bytes and the terminating `NUL` byte is inserted automatically.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// # use std::ffi::CStr;
+    /// # mod libc { pub unsafe fn puts(_: *const i8) {} }
+    /// fn needs_cstr(_: &CStr) {}
+    ///
+    /// needs_cstr(CStr::from_bytes_with_nul(b"Hello\0").unwrap());
+    /// unsafe { libc::puts("World\0".as_ptr().cast()) }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// # use std::ffi::CStr;
+    /// # mod libc { pub unsafe fn puts(_: *const i8) {} }
+    /// fn needs_cstr(_: &CStr) {}
+    ///
+    /// needs_cstr(c"Hello");
+    /// unsafe { libc::puts(c"World".as_ptr()) }
+    /// ```
+    #[clippy::version = "1.76.0"]
+    pub MANUAL_C_STR_LITERALS,
+    pedantic,
+    r#"creating a `CStr` through functions when `c""` literals can be used"#
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Msrv,
@@ -4109,6 +4169,8 @@ impl_lint_pass!(Methods => [
     MANUAL_IS_VARIANT_AND,
     STR_SPLIT_AT_NEWLINE,
     OPTION_AS_REF_CLONED,
+    UNNECESSARY_RESULT_MAP_OR_ELSE,
+    MANUAL_C_STR_LITERALS,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -4136,6 +4198,7 @@ impl<'tcx> LateLintPass<'tcx> for Methods {
             hir::ExprKind::Call(func, args) => {
                 from_iter_instead_of_collect::check(cx, expr, args, func);
                 unnecessary_fallible_conversions::check_function(cx, expr, func);
+                manual_c_str_literals::check(cx, expr, func, args, &self.msrv);
             },
             hir::ExprKind::MethodCall(method_call, receiver, args, _) => {
                 let method_span = method_call.ident.span;
@@ -4354,6 +4417,7 @@ impl Methods {
                     }
                 },
                 ("as_mut", []) => useless_asref::check(cx, expr, "as_mut", recv),
+                ("as_ptr", []) => manual_c_str_literals::check_as_ptr(cx, expr, recv, &self.msrv),
                 ("as_ref", []) => useless_asref::check(cx, expr, "as_ref", recv),
                 ("assume_init", []) => uninit_assumed_init::check(cx, expr, recv),
                 ("cloned", []) => {
@@ -4592,6 +4656,7 @@ impl Methods {
                 },
                 ("map_or_else", [def, map]) => {
                     result_map_or_else_none::check(cx, expr, recv, def, map);
+                    unnecessary_result_map_or_else::check(cx, expr, recv, def, map);
                 },
                 ("next", []) => {
                     if let Some((name2, recv2, args2, _, _)) = method_call(recv) {
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs
index f3577ef6082..2046692bbd0 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_fold.rs
@@ -99,7 +99,6 @@ fn check_fold_with_op(
             cx,
             UNNECESSARY_FOLD,
             fold_span.with_hi(expr.span.hi()),
-            // TODO #2371 don't suggest e.g., .any(|x| f(x)) if we can suggest .any(f)
             "this `.fold` can be written more succinctly using another method",
             "try",
             sugg,
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs
new file mode 100644
index 00000000000..7b0cf48ac43
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_result_map_or_else.rs
@@ -0,0 +1,95 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::peel_blocks;
+use clippy_utils::source::snippet;
+use clippy_utils::ty::is_type_diagnostic_item;
+use rustc_errors::Applicability;
+use rustc_hir as hir;
+use rustc_hir::{Closure, Expr, ExprKind, HirId, QPath, Stmt, StmtKind};
+use rustc_lint::LateContext;
+use rustc_span::symbol::sym;
+
+use super::UNNECESSARY_RESULT_MAP_OR_ELSE;
+
+fn emit_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, def_arg: &Expr<'_>) {
+    let msg = "unused \"map closure\" when calling `Result::map_or_else` value";
+    let self_snippet = snippet(cx, recv.span, "..");
+    let err_snippet = snippet(cx, def_arg.span, "..");
+    span_lint_and_sugg(
+        cx,
+        UNNECESSARY_RESULT_MAP_OR_ELSE,
+        expr.span,
+        msg,
+        "consider using `unwrap_or_else`",
+        format!("{self_snippet}.unwrap_or_else({err_snippet})"),
+        Applicability::MachineApplicable,
+    );
+}
+
+fn get_last_chain_binding_hir_id(mut hir_id: HirId, statements: &[Stmt<'_>]) -> Option<HirId> {
+    for stmt in statements {
+        if let StmtKind::Local(local) = stmt.kind
+            && let Some(init) = local.init
+            && let ExprKind::Path(QPath::Resolved(_, path)) = init.kind
+            && let hir::def::Res::Local(local_hir_id) = path.res
+            && local_hir_id == hir_id
+        {
+            hir_id = local.pat.hir_id;
+        } else {
+            return None;
+        }
+    }
+    Some(hir_id)
+}
+
+fn handle_qpath(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    recv: &Expr<'_>,
+    def_arg: &Expr<'_>,
+    expected_hir_id: HirId,
+    qpath: QPath<'_>,
+) {
+    if let QPath::Resolved(_, path) = qpath
+        && let hir::def::Res::Local(hir_id) = path.res
+        && expected_hir_id == hir_id
+    {
+        emit_lint(cx, expr, recv, def_arg);
+    }
+}
+
+/// lint use of `_.map_or_else(|err| err, |n| n)` for `Result`s.
+pub(super) fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'_>,
+    recv: &'tcx Expr<'_>,
+    def_arg: &'tcx Expr<'_>,
+    map_arg: &'tcx Expr<'_>,
+) {
+    // lint if the caller of `map_or_else()` is a `Result`
+    if is_type_diagnostic_item(cx, cx.typeck_results().expr_ty(recv), sym::Result)
+        && let ExprKind::Closure(&Closure { body, .. }) = map_arg.kind
+        && let body = cx.tcx.hir().body(body)
+        && let Some(first_param) = body.params.first()
+    {
+        let body_expr = peel_blocks(body.value);
+
+        match body_expr.kind {
+            ExprKind::Path(qpath) => {
+                handle_qpath(cx, expr, recv, def_arg, first_param.pat.hir_id, qpath);
+            },
+            // If this is a block (that wasn't peeled off), then it means there are statements.
+            ExprKind::Block(block, _) => {
+                if let Some(block_expr) = block.expr
+                    // First we ensure that this is a "binding chain" (each statement is a binding
+                    // of the previous one) and that it is a binding of the closure argument.
+                    && let Some(last_chain_binding_id) =
+                        get_last_chain_binding_hir_id(first_param.pat.hir_id, block.stmts)
+                    && let ExprKind::Path(qpath) = block_expr.kind
+                {
+                    handle_qpath(cx, expr, recv, def_arg, last_chain_binding_id, qpath);
+                }
+            },
+            _ => {},
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index 0d234f7f9b5..580160efeb7 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -357,7 +357,7 @@ fn reduce_expression<'a>(cx: &LateContext<'_>, expr: &'a Expr<'a>) -> Option<Vec
             }
         },
         ExprKind::Block(block, _) => {
-            if block.stmts.is_empty() {
+            if block.stmts.is_empty() && !block.targeted_by_break {
                 block.expr.as_ref().and_then(|e| {
                     match block.rules {
                         BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) => None,
diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
index d621051ef16..ae14016f482 100644
--- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
+++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
@@ -252,7 +252,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
                 {
                     (
                         trait_item_id,
-                        FnKind::ImplTraitFn(cx.tcx.erase_regions(trait_ref.args) as *const _ as usize),
+                        FnKind::ImplTraitFn(std::ptr::from_ref(cx.tcx.erase_regions(trait_ref.args)) as usize),
                         usize::from(sig.decl.implicit_self.has_implicit_self()),
                     )
                 } else {
@@ -390,7 +390,6 @@ fn has_matching_args(kind: FnKind, args: GenericArgsRef<'_>) -> bool {
             GenericArgKind::Type(ty) => matches!(*ty.kind(), ty::Param(ty) if ty.index as usize == idx),
             GenericArgKind::Const(c) => matches!(c.kind(), ConstKind::Param(c) if c.index as usize == idx),
         }),
-        #[allow(trivial_casts)]
-        FnKind::ImplTraitFn(expected_args) => args as *const _ as usize == expected_args,
+        FnKind::ImplTraitFn(expected_args) => std::ptr::from_ref(args) as usize == expected_args,
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs
index 4c09c4eea58..e002429e3a4 100644
--- a/src/tools/clippy/clippy_lints/src/operators/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs
@@ -771,6 +771,7 @@ declare_clippy_lint! {
 pub struct Operators {
     arithmetic_context: numeric_arithmetic::Context,
     verbose_bit_mask_threshold: u64,
+    modulo_arithmetic_allow_comparison_to_zero: bool,
 }
 impl_lint_pass!(Operators => [
     ABSURD_EXTREME_COMPARISONS,
@@ -801,10 +802,11 @@ impl_lint_pass!(Operators => [
     SELF_ASSIGNMENT,
 ]);
 impl Operators {
-    pub fn new(verbose_bit_mask_threshold: u64) -> Self {
+    pub fn new(verbose_bit_mask_threshold: u64, modulo_arithmetic_allow_comparison_to_zero: bool) -> Self {
         Self {
             arithmetic_context: numeric_arithmetic::Context::default(),
             verbose_bit_mask_threshold,
+            modulo_arithmetic_allow_comparison_to_zero,
         }
     }
 }
@@ -835,12 +837,19 @@ impl<'tcx> LateLintPass<'tcx> for Operators {
                 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);
+                modulo_arithmetic::check(
+                    cx,
+                    e,
+                    op.node,
+                    lhs,
+                    rhs,
+                    self.modulo_arithmetic_allow_comparison_to_zero,
+                );
             },
             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);
+                modulo_arithmetic::check(cx, e, op.node, lhs, rhs, false);
             },
             ExprKind::Assign(lhs, rhs, _) => {
                 assign_op_pattern::check(cx, e, lhs, rhs);
diff --git a/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs
index cb3916484c1..40d4a842bef 100644
--- a/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/modulo_arithmetic.rs
@@ -1,7 +1,7 @@
 use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::sext;
-use rustc_hir::{BinOpKind, Expr};
+use rustc_hir::{BinOpKind, Expr, ExprKind, Node};
 use rustc_lint::LateContext;
 use rustc_middle::ty::{self, Ty};
 use std::fmt::Display;
@@ -14,8 +14,13 @@ pub(super) fn check<'tcx>(
     op: BinOpKind,
     lhs: &'tcx Expr<'_>,
     rhs: &'tcx Expr<'_>,
+    allow_comparison_to_zero: bool,
 ) {
     if op == BinOpKind::Rem {
+        if allow_comparison_to_zero && used_in_comparison_with_zero(cx, e) {
+            return;
+        }
+
         let lhs_operand = analyze_operand(lhs, cx, e);
         let rhs_operand = analyze_operand(rhs, cx, e);
         if let Some(lhs_operand) = lhs_operand
@@ -28,6 +33,26 @@ pub(super) fn check<'tcx>(
     };
 }
 
+fn used_in_comparison_with_zero(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
+    let Some(Node::Expr(parent_expr)) = cx.tcx.hir().find_parent(expr.hir_id) else {
+        return false;
+    };
+    let ExprKind::Binary(op, lhs, rhs) = parent_expr.kind else {
+        return false;
+    };
+
+    if op.node == BinOpKind::Eq || op.node == BinOpKind::Ne {
+        if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), rhs) {
+            return true;
+        }
+        if let Some(Constant::Int(0)) = constant(cx, cx.typeck_results(), lhs) {
+            return true;
+        }
+    }
+
+    false
+}
+
 struct OperandInfo {
     string_representation: Option<String>,
     is_negative: bool,
diff --git a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
index 334e6770ae4..915da18aafe 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_closure_call.rs
@@ -2,6 +2,7 @@ use crate::rustc_lint::LintContext;
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
 use clippy_utils::get_parent_expr;
 use clippy_utils::sugg::Sugg;
+use hir::Param;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::intravisit::{Visitor as HirVisitor, Visitor};
@@ -13,6 +14,7 @@ use rustc_middle::hir::nested_filter;
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty;
 use rustc_session::declare_lint_pass;
+use rustc_span::ExpnKind;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -89,7 +91,12 @@ fn find_innermost_closure<'tcx>(
     cx: &LateContext<'tcx>,
     mut expr: &'tcx hir::Expr<'tcx>,
     mut steps: usize,
-) -> Option<(&'tcx hir::Expr<'tcx>, &'tcx hir::FnDecl<'tcx>, ty::Asyncness)> {
+) -> Option<(
+    &'tcx hir::Expr<'tcx>,
+    &'tcx hir::FnDecl<'tcx>,
+    ty::Asyncness,
+    &'tcx [Param<'tcx>],
+)> {
     let mut data = None;
 
     while let hir::ExprKind::Closure(closure) = expr.kind
@@ -110,6 +117,7 @@ fn find_innermost_closure<'tcx>(
             } else {
                 ty::Asyncness::No
             },
+            body.params,
         ));
         steps -= 1;
     }
@@ -152,7 +160,9 @@ impl<'tcx> LateLintPass<'tcx> for RedundantClosureCall {
             // without this check, we'd end up linting twice.
             && !matches!(recv.kind, hir::ExprKind::Call(..))
             && let (full_expr, call_depth) = get_parent_call_exprs(cx, expr)
-            && let Some((body, fn_decl, coroutine_kind)) = find_innermost_closure(cx, recv, call_depth)
+            && let Some((body, fn_decl, coroutine_kind, params)) = find_innermost_closure(cx, recv, call_depth)
+            // outside macros we lint properly. Inside macros, we lint only ||() style closures.
+            && (!matches!(expr.span.ctxt().outer_expn_data().kind, ExpnKind::Macro(_, _)) || params.is_empty())
         {
             span_lint_and_then(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/redundant_locals.rs b/src/tools/clippy/clippy_lints/src/redundant_locals.rs
index 2c511ee0bc0..700a5dd4a85 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_locals.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_locals.rs
@@ -4,8 +4,10 @@ use clippy_utils::ty::needs_ordered_drop;
 use rustc_ast::Mutability;
 use rustc_hir::def::Res;
 use rustc_hir::{BindingAnnotation, ByRef, ExprKind, HirId, Local, Node, Pat, PatKind, QPath};
+use rustc_hir_typeck::expr_use_visitor::PlaceBase;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::UpvarCapture;
 use rustc_session::declare_lint_pass;
 use rustc_span::symbol::Ident;
 use rustc_span::DesugaringKind;
@@ -69,6 +71,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals {
             // the local is user-controlled
             && !in_external_macro(cx.sess(), local.span)
             && !is_from_proc_macro(cx, expr)
+            && !is_by_value_closure_capture(cx, local.hir_id, binding_id)
         {
             span_lint_and_help(
                 cx,
@@ -82,6 +85,29 @@ impl<'tcx> LateLintPass<'tcx> for RedundantLocals {
     }
 }
 
+/// Checks if the enclosing body is a closure and if the given local is captured by value.
+///
+/// In those cases, the redefinition may be necessary to force a move:
+/// ```
+/// fn assert_static<T: 'static>(_: T) {}
+///
+/// let v = String::new();
+/// let closure = || {
+///   let v = v; // <- removing this redefinition makes `closure` no longer `'static`
+///   dbg!(&v);
+/// };
+/// assert_static(closure);
+/// ```
+fn is_by_value_closure_capture(cx: &LateContext<'_>, redefinition: HirId, root_variable: HirId) -> bool {
+    let closure_def_id = cx.tcx.hir().enclosing_body_owner(redefinition);
+
+    cx.tcx.is_closure_or_coroutine(closure_def_id.to_def_id())
+        && cx.tcx.closure_captures(closure_def_id).iter().any(|c| {
+            matches!(c.info.capture_kind, UpvarCapture::ByValue)
+                && matches!(c.place.base, PlaceBase::Upvar(upvar) if upvar.var_path.hir_id == root_variable)
+        })
+}
+
 /// Find the annotation of a binding introduced by a pattern, or `None` if it's not introduced.
 fn find_binding(pat: &Pat<'_>, name: Ident) -> Option<BindingAnnotation> {
     let mut ret = None;
diff --git a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs
index 07fcb69afbc..c8352c05265 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_type_annotations.rs
@@ -188,7 +188,6 @@ impl LateLintPass<'_> for RedundantTypeAnnotations {
                     match init_lit.node {
                         // In these cases the annotation is redundant
                         LitKind::Str(..)
-                        | LitKind::ByteStr(..)
                         | LitKind::Byte(..)
                         | LitKind::Char(..)
                         | LitKind::Bool(..)
@@ -202,6 +201,16 @@ impl LateLintPass<'_> for RedundantTypeAnnotations {
                             }
                         },
                         LitKind::Err => (),
+                        LitKind::ByteStr(..) => {
+                            // We only lint if the type annotation is an array type (e.g. &[u8; 4]).
+                            // If instead it is a slice (e.g. &[u8]) it may not be redundant, so we
+                            // don't lint.
+                            if let hir::TyKind::Ref(_, mut_ty) = ty.kind
+                                && matches!(mut_ty.ty.kind, hir::TyKind::Array(..))
+                            {
+                                span_lint(cx, REDUNDANT_TYPE_ANNOTATIONS, local.span, "redundant type annotation");
+                            }
+                        },
                     }
                 },
                 _ => (),
diff --git a/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs
index 5a4933a3fce..fcb79f6d694 100644
--- a/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs
+++ b/src/tools/clippy/clippy_lints/src/repeat_vec_with_capacity.rs
@@ -42,7 +42,7 @@ declare_clippy_lint! {
     /// //                                      ^^^ this closure executes 123 times
     /// //                                          and the vecs will have the expected capacity
     /// ```
-    #[clippy::version = "1.74.0"]
+    #[clippy::version = "1.76.0"]
     pub REPEAT_VEC_WITH_CAPACITY,
     suspicious,
     "repeating a `Vec::with_capacity` expression which does not retain capacity"
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index e0175046587..2af466d3f51 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -2,10 +2,14 @@ use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then, span_lin
 use clippy_utils::source::{snippet_opt, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
 use clippy_utils::visitors::{for_each_expr_with_closures, Descend};
-use clippy_utils::{fn_def_id, is_from_proc_macro, is_inside_let_else, path_to_local_id, span_find_starting_semi};
+use clippy_utils::{
+    fn_def_id, is_from_proc_macro, is_inside_let_else, is_res_lang_ctor, path_res, path_to_local_id,
+    span_find_starting_semi,
+};
 use core::ops::ControlFlow;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
+use rustc_hir::LangItem::ResultErr;
 use rustc_hir::{
     Block, Body, Expr, ExprKind, FnDecl, HirId, ItemKind, LangItem, MatchSource, Node, OwnerNode, PatKind, QPath, Stmt,
     StmtKind,
@@ -18,6 +22,7 @@ use rustc_session::declare_lint_pass;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::{BytePos, Pos, Span};
 use std::borrow::Cow;
+use std::fmt::Display;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -146,14 +151,14 @@ impl<'tcx> RetReplacement<'tcx> {
     }
 }
 
-impl<'tcx> ToString for RetReplacement<'tcx> {
-    fn to_string(&self) -> String {
+impl<'tcx> Display for RetReplacement<'tcx> {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
         match self {
-            Self::Empty => String::new(),
-            Self::Block => "{}".to_string(),
-            Self::Unit => "()".to_string(),
-            Self::IfSequence(inner, _) => format!("({inner})"),
-            Self::Expr(inner, _) => inner.to_string(),
+            Self::Empty => write!(f, ""),
+            Self::Block => write!(f, "{{}}"),
+            Self::Unit => write!(f, "()"),
+            Self::IfSequence(inner, _) => write!(f, "({inner})"),
+            Self::Expr(inner, _) => write!(f, "{inner}"),
         }
     }
 }
@@ -181,7 +186,15 @@ impl<'tcx> LateLintPass<'tcx> for Return {
         if !in_external_macro(cx.sess(), stmt.span)
             && let StmtKind::Semi(expr) = stmt.kind
             && let ExprKind::Ret(Some(ret)) = expr.kind
-            && let ExprKind::Match(.., MatchSource::TryDesugar(_)) = ret.kind
+            // return Err(...)? desugars to a match
+            // over a Err(...).branch()
+            // which breaks down to a branch call, with the callee being
+            // the constructor of the Err variant
+            && let ExprKind::Match(maybe_cons, _, MatchSource::TryDesugar(_)) = ret.kind
+            && let ExprKind::Call(_, [maybe_result_err]) = maybe_cons.kind
+            && let ExprKind::Call(maybe_constr, _) = maybe_result_err.kind
+            && is_res_lang_ctor(cx, path_res(cx, maybe_constr), ResultErr)
+
             // Ensure this is not the final stmt, otherwise removing it would cause a compile error
             && let OwnerNode::Item(item) = cx.tcx.hir_owner_node(cx.tcx.hir().get_parent_item(expr.hir_id))
             && let ItemKind::Fn(_, _, body) = item.kind
diff --git a/src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs b/src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs
new file mode 100644
index 00000000000..e1cea99085f
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/to_string_trait_impl.rs
@@ -0,0 +1,67 @@
+use clippy_utils::diagnostics::span_lint_and_help;
+use rustc_hir::{Impl, Item, ItemKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::declare_lint_pass;
+use rustc_span::sym;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for direct implementations of `ToString`.
+    /// ### Why is this bad?
+    /// This trait is automatically implemented for any type which implements the `Display` trait.
+    /// As such, `ToString` shouldnโ€™t be implemented directly: `Display` should be implemented instead,
+    /// and you get the `ToString` implementation for free.
+    /// ### Example
+    /// ```no_run
+    /// struct Point {
+    ///   x: usize,
+    ///   y: usize,
+    /// }
+    ///
+    /// impl ToString for Point {
+    ///   fn to_string(&self) -> String {
+    ///     format!("({}, {})", self.x, self.y)
+    ///   }
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// struct Point {
+    ///   x: usize,
+    ///   y: usize,
+    /// }
+    ///
+    /// impl std::fmt::Display for Point {
+    ///   fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+    ///     write!(f, "({}, {})", self.x, self.y)
+    ///   }
+    /// }
+    /// ```
+    #[clippy::version = "1.77.0"]
+    pub TO_STRING_TRAIT_IMPL,
+    style,
+    "check for direct implementations of `ToString`"
+}
+
+declare_lint_pass!(ToStringTraitImpl => [TO_STRING_TRAIT_IMPL]);
+
+impl<'tcx> LateLintPass<'tcx> for ToStringTraitImpl {
+    fn check_item(&mut self, cx: &LateContext<'tcx>, it: &'tcx Item<'tcx>) {
+        if let ItemKind::Impl(Impl {
+            of_trait: Some(trait_ref),
+            ..
+        }) = it.kind
+            && let Some(trait_did) = trait_ref.trait_def_id()
+            && cx.tcx.is_diagnostic_item(sym::ToString, trait_did)
+        {
+            span_lint_and_help(
+                cx,
+                TO_STRING_TRAIT_IMPL,
+                it.span,
+                "direct implementation of `ToString`",
+                None,
+                "prefer implementing `Display` instead",
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs
index 209035804e4..224ec475c51 100644
--- a/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs
+++ b/src/tools/clippy/clippy_lints/src/unconditional_recursion.rs
@@ -69,14 +69,6 @@ fn span_error(cx: &LateContext<'_>, method_span: Span, expr: &Expr<'_>) {
     );
 }
 
-fn get_ty_def_id(ty: Ty<'_>) -> Option<DefId> {
-    match ty.peel_refs().kind() {
-        ty::Adt(adt, _) => Some(adt.did()),
-        ty::Foreign(def_id) => Some(*def_id),
-        _ => None,
-    }
-}
-
 fn get_hir_ty_def_id<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: rustc_hir::Ty<'tcx>) -> Option<DefId> {
     let TyKind::Path(qpath) = hir_ty.kind else { return None };
     match qpath {
@@ -131,21 +123,49 @@ fn get_impl_trait_def_id(cx: &LateContext<'_>, method_def_id: LocalDefId) -> Opt
     }
 }
 
-#[allow(clippy::unnecessary_def_path)]
+/// When we have `x == y` where `x = &T` and `y = &T`, then that resolves to
+/// `<&T as PartialEq<&T>>::eq`, which is not the same as `<T as PartialEq<T>>::eq`,
+/// however we still would want to treat it the same, because we know that it's a blanket impl
+/// that simply delegates to the `PartialEq` impl with one reference removed.
+///
+/// Still, we can't just do `lty.peel_refs() == rty.peel_refs()` because when we have `x = &T` and
+/// `y = &&T`, this is not necessarily the same as `<T as PartialEq<T>>::eq`
+///
+/// So to avoid these FNs and FPs, we keep removing a layer of references from *both* sides
+/// until both sides match the expected LHS and RHS type (or they don't).
+fn matches_ty<'tcx>(
+    mut left: Ty<'tcx>,
+    mut right: Ty<'tcx>,
+    expected_left: Ty<'tcx>,
+    expected_right: Ty<'tcx>,
+) -> bool {
+    while let (&ty::Ref(_, lty, _), &ty::Ref(_, rty, _)) = (left.kind(), right.kind()) {
+        if lty == expected_left && rty == expected_right {
+            return true;
+        }
+        left = lty;
+        right = rty;
+    }
+    false
+}
+
 fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: LocalDefId, name: Ident, expr: &Expr<'_>) {
-    let args = cx
-        .tcx
-        .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(method_def_id).skip_binder())
-        .inputs();
+    let Some(sig) = cx
+        .typeck_results()
+        .liberated_fn_sigs()
+        .get(cx.tcx.local_def_id_to_hir_id(method_def_id))
+    else {
+        return;
+    };
+
     // That has two arguments.
-    if let [self_arg, other_arg] = args
-        && let Some(self_arg) = get_ty_def_id(*self_arg)
-        && let Some(other_arg) = get_ty_def_id(*other_arg)
+    if let [self_arg, other_arg] = sig.inputs()
+        && let &ty::Ref(_, self_arg, _) = self_arg.kind()
+        && let &ty::Ref(_, other_arg, _) = other_arg.kind()
         // The two arguments are of the same type.
-        && self_arg == other_arg
         && let Some(trait_def_id) = get_impl_trait_def_id(cx, method_def_id)
         // The trait is `PartialEq`.
-        && Some(trait_def_id) == get_trait_def_id(cx, &["core", "cmp", "PartialEq"])
+        && cx.tcx.is_diagnostic_item(sym::PartialEq, trait_def_id)
     {
         let to_check_op = if name.name == sym::eq {
             BinOpKind::Eq
@@ -154,31 +174,19 @@ fn check_partial_eq(cx: &LateContext<'_>, method_span: Span, method_def_id: Loca
         };
         let is_bad = match expr.kind {
             ExprKind::Binary(op, left, right) if op.node == to_check_op => {
-                // Then we check if the left-hand element is of the same type as `self`.
-                if let Some(left_ty) = cx.typeck_results().expr_ty_opt(left)
-                    && let Some(left_id) = get_ty_def_id(left_ty)
-                    && self_arg == left_id
-                    && let Some(right_ty) = cx.typeck_results().expr_ty_opt(right)
-                    && let Some(right_id) = get_ty_def_id(right_ty)
-                    && other_arg == right_id
-                {
-                    true
-                } else {
-                    false
-                }
+                // Then we check if the LHS matches self_arg and RHS matches other_arg
+                let left_ty = cx.typeck_results().expr_ty_adjusted(left);
+                let right_ty = cx.typeck_results().expr_ty_adjusted(right);
+                matches_ty(left_ty, right_ty, self_arg, other_arg)
             },
-            ExprKind::MethodCall(segment, receiver, &[_arg], _) if segment.ident.name == name.name => {
-                if let Some(ty) = cx.typeck_results().expr_ty_opt(receiver)
-                    && let Some(ty_id) = get_ty_def_id(ty)
-                    && self_arg != ty_id
-                {
-                    // Since this called on a different type, the lint should not be
-                    // triggered here.
-                    return;
-                }
+            ExprKind::MethodCall(segment, receiver, [arg], _) if segment.ident.name == name.name => {
+                let receiver_ty = cx.typeck_results().expr_ty_adjusted(receiver);
+                let arg_ty = cx.typeck_results().expr_ty_adjusted(arg);
+
                 if let Some(fn_id) = cx.typeck_results().type_dependent_def_id(expr.hir_id)
                     && let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
                     && trait_id == trait_def_id
+                    && matches_ty(receiver_ty, arg_ty, self_arg, other_arg)
                 {
                     true
                 } else {
diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
index adc66e15ff5..6b3ea7700b7 100644
--- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
@@ -1,5 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::{is_res_lang_ctor, is_trait_method, match_trait_method, paths};
+use clippy_utils::macros::{is_panic, root_macro_call_first_node};
+use clippy_utils::{is_res_lang_ctor, is_trait_method, match_trait_method, paths, peel_blocks};
 use hir::{ExprKind, PatKind};
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
@@ -82,37 +83,72 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
         }
 
         if let Some(exp) = block.expr
-            && matches!(exp.kind, hir::ExprKind::If(_, _, _) | hir::ExprKind::Match(_, _, _))
+            && matches!(
+                exp.kind,
+                hir::ExprKind::If(_, _, _) | hir::ExprKind::Match(_, _, hir::MatchSource::Normal)
+            )
         {
             check_expr(cx, exp);
         }
     }
 }
 
+fn non_consuming_err_arm<'a>(cx: &LateContext<'a>, arm: &hir::Arm<'a>) -> bool {
+    // if there is a guard, we consider the result to be consumed
+    if arm.guard.is_some() {
+        return false;
+    }
+    if is_unreachable_or_panic(cx, arm.body) {
+        // if the body is unreachable or there is a panic,
+        // we consider the result to be consumed
+        return false;
+    }
+
+    if let PatKind::TupleStruct(ref path, [inner_pat], _) = arm.pat.kind {
+        return is_res_lang_ctor(cx, cx.qpath_res(path, inner_pat.hir_id), hir::LangItem::ResultErr);
+    }
+
+    false
+}
+
+fn non_consuming_ok_arm<'a>(cx: &LateContext<'a>, arm: &hir::Arm<'a>) -> bool {
+    // if there is a guard, we consider the result to be consumed
+    if arm.guard.is_some() {
+        return false;
+    }
+    if is_unreachable_or_panic(cx, arm.body) {
+        // if the body is unreachable or there is a panic,
+        // we consider the result to be consumed
+        return false;
+    }
+
+    if is_ok_wild_or_dotdot_pattern(cx, arm.pat) {
+        return true;
+    }
+    false
+}
+
 fn check_expr<'a>(cx: &LateContext<'a>, expr: &'a hir::Expr<'a>) {
     match expr.kind {
         hir::ExprKind::If(cond, _, _)
             if let ExprKind::Let(hir::Let { pat, init, .. }) = cond.kind
-                && pattern_is_ignored_ok(cx, pat)
+                && is_ok_wild_or_dotdot_pattern(cx, pat)
                 && let Some(op) = should_lint(cx, init) =>
         {
             emit_lint(cx, cond.span, op, &[pat.span]);
         },
-        hir::ExprKind::Match(expr, arms, hir::MatchSource::Normal) if let Some(op) = should_lint(cx, expr) => {
-            let found_arms: Vec<_> = arms
-                .iter()
-                .filter_map(|arm| {
-                    if pattern_is_ignored_ok(cx, arm.pat) {
-                        Some(arm.span)
-                    } else {
-                        None
-                    }
-                })
-                .collect();
-            if !found_arms.is_empty() {
-                emit_lint(cx, expr.span, op, found_arms.as_slice());
+        // we will capture only the case where the match is Ok( ) or Err( )
+        // prefer to match the minimum possible, and expand later if needed
+        // to avoid false positives on something as used as this
+        hir::ExprKind::Match(expr, [arm1, arm2], hir::MatchSource::Normal) if let Some(op) = should_lint(cx, expr) => {
+            if non_consuming_ok_arm(cx, arm1) && non_consuming_err_arm(cx, arm2) {
+                emit_lint(cx, expr.span, op, &[arm1.pat.span]);
+            }
+            if non_consuming_ok_arm(cx, arm2) && non_consuming_err_arm(cx, arm1) {
+                emit_lint(cx, expr.span, op, &[arm2.pat.span]);
             }
         },
+        hir::ExprKind::Match(_, _, hir::MatchSource::Normal) => {},
         _ if let Some(op) = should_lint(cx, expr) => {
             emit_lint(cx, expr.span, op, &[]);
         },
@@ -130,25 +166,40 @@ fn should_lint<'a>(cx: &LateContext<'a>, mut inner: &'a hir::Expr<'a>) -> Option
     check_io_mode(cx, inner)
 }
 
-fn pattern_is_ignored_ok(cx: &LateContext<'_>, pat: &hir::Pat<'_>) -> bool {
+fn is_ok_wild_or_dotdot_pattern<'a>(cx: &LateContext<'a>, pat: &hir::Pat<'a>) -> bool {
     // the if checks whether we are in a result Ok( ) pattern
     // and the return checks whether it is unhandled
 
-    if let PatKind::TupleStruct(ref path, inner_pat, ddp) = pat.kind
+    if let PatKind::TupleStruct(ref path, inner_pat, _) = pat.kind
         // we check against Result::Ok to avoid linting on Err(_) or something else.
         && is_res_lang_ctor(cx, cx.qpath_res(path, pat.hir_id), hir::LangItem::ResultOk)
     {
-        return match (inner_pat, ddp.as_opt_usize()) {
-            // Ok(_) pattern
-            ([inner_pat], None) if matches!(inner_pat.kind, PatKind::Wild) => true,
-            // Ok(..) pattern
-            ([], Some(0)) => true,
-            _ => false,
-        };
+        if matches!(inner_pat, []) {
+            return true;
+        }
+
+        if let [cons_pat] = inner_pat
+            && matches!(cons_pat.kind, PatKind::Wild)
+        {
+            return true;
+        }
+        return false;
     }
     false
 }
 
+// this is partially taken from panic_unimplemented
+fn is_unreachable_or_panic(cx: &LateContext<'_>, expr: &hir::Expr<'_>) -> bool {
+    let expr = peel_blocks(expr);
+    let Some(macro_call) = root_macro_call_first_node(cx, expr) else {
+        return false;
+    };
+    if is_panic(cx, macro_call.def_id) {
+        return !cx.tcx.hir().is_inside_const_context(expr.hir_id);
+    }
+    matches!(cx.tcx.item_name(macro_call.def_id).as_str(), "unreachable")
+}
+
 fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
     while let hir::ExprKind::MethodCall(path, receiver, ..) = expr.kind {
         if matches!(
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index 288df0fd663..29c67341a46 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -490,9 +490,9 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
                             format!("ClosureKind::Coroutine(CoroutineKind::Coroutine(Movability::{movability:?})")
                         },
                     },
-                    ClosureKind::CoroutineClosure(desugaring) => format!(
-                        "ClosureKind::CoroutineClosure(CoroutineDesugaring::{desugaring:?})"
-                    ),
+                    ClosureKind::CoroutineClosure(desugaring) => {
+                        format!("ClosureKind::CoroutineClosure(CoroutineDesugaring::{desugaring:?})")
+                    },
                 };
 
                 let ret_ty = match fn_decl.output {
diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
index b82bd1d7e7c..5410e8ac117 100644
--- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
@@ -1,6 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::is_test_module_or_function;
 use clippy_utils::source::{snippet, snippet_with_applicability};
+use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Item, ItemKind, PathSegment, UseKind};
@@ -100,13 +101,15 @@ declare_clippy_lint! {
 pub struct WildcardImports {
     warn_on_all: bool,
     test_modules_deep: u32,
+    allowed_segments: FxHashSet<String>,
 }
 
 impl WildcardImports {
-    pub fn new(warn_on_all: bool) -> Self {
+    pub fn new(warn_on_all: bool, allowed_wildcard_imports: FxHashSet<String>) -> Self {
         Self {
             warn_on_all,
             test_modules_deep: 0,
+            allowed_segments: allowed_wildcard_imports,
         }
     }
 }
@@ -190,6 +193,7 @@ impl WildcardImports {
         item.span.from_expansion()
             || is_prelude_import(segments)
             || (is_super_only_import(segments) && self.test_modules_deep > 0)
+            || is_allowed_via_config(segments, &self.allowed_segments)
     }
 }
 
@@ -198,10 +202,18 @@ impl WildcardImports {
 fn is_prelude_import(segments: &[PathSegment<'_>]) -> bool {
     segments
         .iter()
-        .any(|ps| ps.ident.name.as_str().contains(sym::prelude.as_str()))
+        .any(|ps| ps.ident.as_str().contains(sym::prelude.as_str()))
 }
 
 // Allow "super::*" imports in tests.
 fn is_super_only_import(segments: &[PathSegment<'_>]) -> bool {
     segments.len() == 1 && segments[0].ident.name == kw::Super
 }
+
+// Allow skipping imports containing user configured segments,
+// i.e. "...::utils::...::*" if user put `allowed-wildcard-imports = ["utils"]` in `Clippy.toml`
+fn is_allowed_via_config(segments: &[PathSegment<'_>], allowed_segments: &FxHashSet<String>) -> bool {
+    // segment matching need to be exact instead of using 'contains', in case user unintentionaly put
+    // a single character in the config thus skipping most of the warnings.
+    segments.iter().any(|seg| allowed_segments.contains(seg.ident.as_str()))
+}
diff --git a/src/tools/clippy/clippy_utils/Cargo.toml b/src/tools/clippy/clippy_utils/Cargo.toml
index b8869eedf52..c7454fa3328 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.77"
+version = "0.1.78"
 edition = "2021"
 publish = false
 
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index d2bc49d1e29..6a33e11be46 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -1,5 +1,6 @@
 #![feature(array_chunks)]
 #![feature(box_patterns)]
+#![feature(control_flow_enum)]
 #![feature(if_let_guard)]
 #![feature(let_chains)]
 #![feature(lint_reasons)]
@@ -81,6 +82,7 @@ use core::mem;
 use core::ops::ControlFlow;
 use std::collections::hash_map::Entry;
 use std::hash::BuildHasherDefault;
+use std::iter::{once, repeat};
 use std::sync::{Mutex, MutexGuard, OnceLock};
 
 use itertools::Itertools;
@@ -90,6 +92,7 @@ use rustc_data_structures::packed::Pu128;
 use rustc_data_structures::unhash::UnhashMap;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalModDefId, LOCAL_CRATE};
+use rustc_hir::definitions::{DefPath, DefPathData};
 use rustc_hir::hir_id::{HirIdMap, HirIdSet};
 use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
@@ -108,8 +111,8 @@ use rustc_middle::ty::binding::BindingMode;
 use rustc_middle::ty::fast_reject::SimplifiedType;
 use rustc_middle::ty::layout::IntegerExt;
 use rustc_middle::ty::{
-    self as rustc_ty, Binder, BorrowKind, ClosureKind, FloatTy, IntTy, ParamEnv, ParamEnvAnd, Ty, TyCtxt, TypeAndMut,
-    TypeVisitableExt, UintTy, UpvarCapture,
+    self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgsRef, IntTy, ParamEnv,
+    ParamEnvAnd, Ty, TyCtxt, TypeAndMut, TypeVisitableExt, UintTy, UpvarCapture,
 };
 use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::source_map::SourceMap;
@@ -120,10 +123,7 @@ use visitors::Visitable;
 
 use crate::consts::{constant, mir_to_const, Constant};
 use crate::higher::Range;
-use crate::ty::{
-    adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type,
-    ty_is_fn_once_param,
-};
+use crate::ty::{adt_and_variant_of_res, can_partially_move_ty, expr_sig, is_copy, is_recursively_primitive_type};
 use crate::visitors::for_each_expr;
 
 use rustc_middle::hir::nested_filter;
@@ -1359,46 +1359,12 @@ pub fn get_enclosing_loop_or_multi_call_closure<'tcx>(
     for (_, node) in cx.tcx.hir().parent_iter(expr.hir_id) {
         match node {
             Node::Expr(e) => match e.kind {
-                ExprKind::Closure { .. } => {
+                ExprKind::Closure { .. }
                     if let rustc_ty::Closure(_, subs) = cx.typeck_results().expr_ty(e).kind()
-                        && subs.as_closure().kind() == ClosureKind::FnOnce
-                    {
-                        continue;
-                    }
-                    let is_once = walk_to_expr_usage(cx, e, |node, id| {
-                        let Node::Expr(e) = node else {
-                            return None;
-                        };
-                        match e.kind {
-                            ExprKind::Call(f, _) if f.hir_id == id => Some(()),
-                            ExprKind::Call(f, args) => {
-                                let i = args.iter().position(|arg| arg.hir_id == id)?;
-                                let sig = expr_sig(cx, f)?;
-                                let predicates = sig
-                                    .predicates_id()
-                                    .map_or(cx.param_env, |id| cx.tcx.param_env(id))
-                                    .caller_bounds();
-                                sig.input(i).and_then(|ty| {
-                                    ty_is_fn_once_param(cx.tcx, ty.skip_binder(), predicates).then_some(())
-                                })
-                            },
-                            ExprKind::MethodCall(_, receiver, args, _) => {
-                                let i = std::iter::once(receiver)
-                                    .chain(args.iter())
-                                    .position(|arg| arg.hir_id == id)?;
-                                let id = cx.typeck_results().type_dependent_def_id(e.hir_id)?;
-                                let ty = cx.tcx.fn_sig(id).instantiate_identity().skip_binder().inputs()[i];
-                                ty_is_fn_once_param(cx.tcx, ty, cx.tcx.param_env(id).caller_bounds()).then_some(())
-                            },
-                            _ => None,
-                        }
-                    })
-                    .is_some();
-                    if !is_once {
-                        return Some(e);
-                    }
-                },
-                ExprKind::Loop(..) => return Some(e),
+                        && subs.as_closure().kind() == ClosureKind::FnOnce => {},
+
+                // Note: A closure's kind is determined by how it's used, not it's captures.
+                ExprKind::Closure { .. } | ExprKind::Loop(..) => return Some(e),
                 _ => (),
             },
             Node::Stmt(_) | Node::Block(_) | Node::Local(_) | Node::Arm(_) => (),
@@ -2596,26 +2562,30 @@ 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`.
+/// Walks up the HIR tree from the given expression in an attempt to find where the value is
+/// consumed.
+///
+/// Termination has three conditions:
+/// - The given function returns `Break`. This function will return the value.
+/// - The consuming node is found. This function will return `Continue(use_node, child_id)`.
+/// - No further parent nodes are found. This will trigger a debug assert or return `None`.
 ///
-/// This allows walking through `if`, `match`, `break`, block expressions to find where the value
-/// produced by the expression is consumed.
+/// This allows walking through `if`, `match`, `break`, and 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> {
+    mut f: impl FnMut(HirId, Node<'tcx>, HirId) -> ControlFlow<T>,
+) -> Option<ControlFlow<T, (Node<'tcx>, HirId)>> {
     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);
+        if let ControlFlow::Break(x) = f(parent_id, parent, child_id) {
+            return Some(ControlFlow::Break(x));
         }
-        let parent = match parent {
+        let parent_expr = 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;
@@ -2625,18 +2595,19 @@ pub fn walk_to_expr_usage<'tcx, T>(
                 child_id = parent_id;
                 continue;
             },
-            _ => return None,
+            _ => return Some(ControlFlow::Continue((parent, child_id))),
         };
-        match parent.kind {
+        match parent_expr.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,
+            ExprKind::Block(..) | ExprKind::DropTemps(_) => child_id = parent_id,
+            _ => return Some(ControlFlow::Continue((parent, child_id))),
         }
     }
+    debug_assert!(false, "no parent node found for `{child_id:?}`");
     None
 }
 
@@ -2680,6 +2651,8 @@ pub enum ExprUseNode<'tcx> {
     Callee,
     /// Access of a field.
     FieldAccess(Ident),
+    Expr,
+    Other,
 }
 impl<'tcx> ExprUseNode<'tcx> {
     /// Checks if the value is returned from the function.
@@ -2756,144 +2729,104 @@ impl<'tcx> ExprUseNode<'tcx> {
                 let sig = cx.tcx.fn_sig(id).skip_binder();
                 Some(DefinedTy::Mir(cx.tcx.param_env(id).and(sig.input(i))))
             },
-            Self::Local(_) | Self::FieldAccess(..) | Self::Callee => None,
+            Self::Local(_) | Self::FieldAccess(..) | Self::Callee | Self::Expr | Self::Other => None,
         }
     }
 }
 
 /// Gets the context an expression's value is used in.
-#[expect(clippy::too_many_lines)]
 pub fn expr_use_ctxt<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> Option<ExprUseCtxt<'tcx>> {
     let mut adjustments = [].as_slice();
     let mut is_ty_unified = false;
     let mut moved_before_use = false;
     let ctxt = e.span.ctxt();
-    walk_to_expr_usage(cx, e, &mut |parent, child_id| {
-        // LocalTableInContext returns the wrong lifetime, so go use `expr_adjustments` instead.
+    walk_to_expr_usage(cx, e, &mut |parent_id, parent, child_id| {
         if adjustments.is_empty()
             && let Node::Expr(e) = cx.tcx.hir_node(child_id)
         {
             adjustments = cx.typeck_results().expr_adjustments(e);
         }
-        match parent {
-            Node::Local(l) if l.span.ctxt() == ctxt => Some(ExprUseCtxt {
-                node: ExprUseNode::Local(l),
-                adjustments,
-                is_ty_unified,
-                moved_before_use,
-            }),
+        if cx.tcx.hir().span(parent_id).ctxt() != ctxt {
+            return ControlFlow::Break(());
+        }
+        if let Node::Expr(e) = parent {
+            match e.kind {
+                ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
+                    is_ty_unified = true;
+                    moved_before_use = true;
+                },
+                ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
+                    is_ty_unified = true;
+                    moved_before_use = true;
+                },
+                ExprKind::Block(..) => moved_before_use = true,
+                _ => {},
+            }
+        }
+        ControlFlow::Continue(())
+    })?
+    .continue_value()
+    .map(|(use_node, child_id)| {
+        let node = match use_node {
+            Node::Local(l) => ExprUseNode::Local(l),
+            Node::ExprField(field) => ExprUseNode::Field(field),
+
             Node::Item(&Item {
                 kind: ItemKind::Static(..) | ItemKind::Const(..),
                 owner_id,
-                span,
                 ..
             })
             | Node::TraitItem(&TraitItem {
                 kind: TraitItemKind::Const(..),
                 owner_id,
-                span,
                 ..
             })
             | Node::ImplItem(&ImplItem {
                 kind: ImplItemKind::Const(..),
                 owner_id,
-                span,
                 ..
-            }) if span.ctxt() == ctxt => Some(ExprUseCtxt {
-                node: ExprUseNode::ConstStatic(owner_id),
-                adjustments,
-                is_ty_unified,
-                moved_before_use,
-            }),
+            }) => ExprUseNode::ConstStatic(owner_id),
 
             Node::Item(&Item {
                 kind: ItemKind::Fn(..),
                 owner_id,
-                span,
                 ..
             })
             | Node::TraitItem(&TraitItem {
                 kind: TraitItemKind::Fn(..),
                 owner_id,
-                span,
                 ..
             })
             | Node::ImplItem(&ImplItem {
                 kind: ImplItemKind::Fn(..),
                 owner_id,
-                span,
                 ..
-            }) if span.ctxt() == ctxt => Some(ExprUseCtxt {
-                node: ExprUseNode::Return(owner_id),
-                adjustments,
-                is_ty_unified,
-                moved_before_use,
-            }),
-
-            Node::ExprField(field) if field.span.ctxt() == ctxt => Some(ExprUseCtxt {
-                node: ExprUseNode::Field(field),
-                adjustments,
-                is_ty_unified,
-                moved_before_use,
-            }),
+            }) => ExprUseNode::Return(owner_id),
 
-            Node::Expr(parent) if parent.span.ctxt() == ctxt => match parent.kind {
-                ExprKind::Ret(_) => Some(ExprUseCtxt {
-                    node: ExprUseNode::Return(OwnerId {
-                        def_id: cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()),
-                    }),
-                    adjustments,
-                    is_ty_unified,
-                    moved_before_use,
-                }),
-                ExprKind::Closure(closure) => Some(ExprUseCtxt {
-                    node: ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
-                    adjustments,
-                    is_ty_unified,
-                    moved_before_use,
-                }),
-                ExprKind::Call(func, args) => Some(ExprUseCtxt {
-                    node: match args.iter().position(|arg| arg.hir_id == child_id) {
-                        Some(i) => ExprUseNode::FnArg(func, i),
-                        None => ExprUseNode::Callee,
-                    },
-                    adjustments,
-                    is_ty_unified,
-                    moved_before_use,
-                }),
-                ExprKind::MethodCall(name, _, args, _) => Some(ExprUseCtxt {
-                    node: ExprUseNode::MethodArg(
-                        parent.hir_id,
-                        name.args,
-                        args.iter().position(|arg| arg.hir_id == child_id).map_or(0, |i| i + 1),
-                    ),
-                    adjustments,
-                    is_ty_unified,
-                    moved_before_use,
-                }),
-                ExprKind::Field(child, name) if child.hir_id == e.hir_id => Some(ExprUseCtxt {
-                    node: ExprUseNode::FieldAccess(name),
-                    adjustments,
-                    is_ty_unified,
-                    moved_before_use,
+            Node::Expr(use_expr) => match use_expr.kind {
+                ExprKind::Ret(_) => ExprUseNode::Return(OwnerId {
+                    def_id: cx.tcx.hir().body_owner_def_id(cx.enclosing_body.unwrap()),
                 }),
-                ExprKind::If(e, _, _) | ExprKind::Match(e, _, _) if e.hir_id != child_id => {
-                    is_ty_unified = true;
-                    moved_before_use = true;
-                    None
-                },
-                ExprKind::Block(_, Some(_)) | ExprKind::Break(..) => {
-                    is_ty_unified = true;
-                    moved_before_use = true;
-                    None
+                ExprKind::Closure(closure) => ExprUseNode::Return(OwnerId { def_id: closure.def_id }),
+                ExprKind::Call(func, args) => match args.iter().position(|arg| arg.hir_id == child_id) {
+                    Some(i) => ExprUseNode::FnArg(func, i),
+                    None => ExprUseNode::Callee,
                 },
-                ExprKind::Block(..) => {
-                    moved_before_use = true;
-                    None
-                },
-                _ => None,
+                ExprKind::MethodCall(name, _, args, _) => ExprUseNode::MethodArg(
+                    use_expr.hir_id,
+                    name.args,
+                    args.iter().position(|arg| arg.hir_id == child_id).map_or(0, |i| i + 1),
+                ),
+                ExprKind::Field(child, name) if child.hir_id == e.hir_id => ExprUseNode::FieldAccess(name),
+                _ => ExprUseNode::Expr,
             },
-            _ => None,
+            _ => ExprUseNode::Other,
+        };
+        ExprUseCtxt {
+            node,
+            adjustments,
+            is_ty_unified,
+            moved_before_use,
         }
     })
 }
@@ -3270,3 +3203,131 @@ pub fn is_never_expr<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> Option<
             })
     }
 }
+
+/// Produces a path from a local caller to the type of the called method. Suitable for user
+/// output/suggestions.
+///
+/// Returned path can be either absolute (for methods defined non-locally), or relative (for local
+/// methods).
+pub fn get_path_from_caller_to_method_type<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    from: LocalDefId,
+    method: DefId,
+    args: GenericArgsRef<'tcx>,
+) -> String {
+    let assoc_item = tcx.associated_item(method);
+    let def_id = assoc_item.container_id(tcx);
+    match assoc_item.container {
+        rustc_ty::TraitContainer => get_path_to_callee(tcx, from, def_id),
+        rustc_ty::ImplContainer => {
+            let ty = tcx.type_of(def_id).instantiate_identity();
+            get_path_to_ty(tcx, from, ty, args)
+        },
+    }
+}
+
+fn get_path_to_ty<'tcx>(tcx: TyCtxt<'tcx>, from: LocalDefId, ty: Ty<'tcx>, args: GenericArgsRef<'tcx>) -> String {
+    match ty.kind() {
+        rustc_ty::Adt(adt, _) => get_path_to_callee(tcx, from, adt.did()),
+        // TODO these types need to be recursively resolved as well
+        rustc_ty::Array(..)
+        | rustc_ty::Dynamic(..)
+        | rustc_ty::Never
+        | rustc_ty::RawPtr(_)
+        | rustc_ty::Ref(..)
+        | rustc_ty::Slice(_)
+        | rustc_ty::Tuple(_) => format!("<{}>", EarlyBinder::bind(ty).instantiate(tcx, args)),
+        _ => ty.to_string(),
+    }
+}
+
+/// Produce a path from some local caller to the callee. Suitable for user output/suggestions.
+fn get_path_to_callee(tcx: TyCtxt<'_>, from: LocalDefId, callee: DefId) -> String {
+    // only search for a relative path if the call is fully local
+    if callee.is_local() {
+        let callee_path = tcx.def_path(callee);
+        let caller_path = tcx.def_path(from.to_def_id());
+        maybe_get_relative_path(&caller_path, &callee_path, 2)
+    } else {
+        tcx.def_path_str(callee)
+    }
+}
+
+/// Tries to produce a relative path from `from` to `to`; if such a path would contain more than
+/// `max_super` `super` items, produces an absolute path instead. Both `from` and `to` should be in
+/// the local crate.
+///
+/// Suitable for user output/suggestions.
+///
+/// This ignores use items, and assumes that the target path is visible from the source
+/// path (which _should_ be a reasonable assumption since we in order to be able to use an object of
+/// certain type T, T is required to be visible).
+///
+/// TODO make use of `use` items. Maybe we should have something more sophisticated like
+/// rust-analyzer does? <https://docs.rs/ra_ap_hir_def/0.0.169/src/ra_ap_hir_def/find_path.rs.html#19-27>
+fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> String {
+    use itertools::EitherOrBoth::{Both, Left, Right};
+
+    // 1. skip the segments common for both paths (regardless of their type)
+    let unique_parts = to
+        .data
+        .iter()
+        .zip_longest(from.data.iter())
+        .skip_while(|el| matches!(el, Both(l, r) if l == r))
+        .map(|el| match el {
+            Both(l, r) => Both(l.data, r.data),
+            Left(l) => Left(l.data),
+            Right(r) => Right(r.data),
+        });
+
+    // 2. for the remaning segments, construct relative path using only mod names and `super`
+    let mut go_up_by = 0;
+    let mut path = Vec::new();
+    for el in unique_parts {
+        match el {
+            Both(l, r) => {
+                // consider:
+                // a::b::sym:: ::    refers to
+                // c::d::e  ::f::sym
+                // result should be super::super::c::d::e::f
+                //
+                // alternatively:
+                // a::b::c  ::d::sym refers to
+                // e::f::sym:: ::
+                // result should be super::super::super::super::e::f
+                if let DefPathData::TypeNs(s) = l {
+                    path.push(s.to_string());
+                }
+                if let DefPathData::TypeNs(_) = r {
+                    go_up_by += 1;
+                }
+            },
+            // consider:
+            // a::b::sym:: ::    refers to
+            // c::d::e  ::f::sym
+            // when looking at `f`
+            Left(DefPathData::TypeNs(sym)) => path.push(sym.to_string()),
+            // consider:
+            // a::b::c  ::d::sym refers to
+            // e::f::sym:: ::
+            // when looking at `d`
+            Right(DefPathData::TypeNs(_)) => go_up_by += 1,
+            _ => {},
+        }
+    }
+
+    if go_up_by > max_super {
+        // `super` chain would be too long, just use the absolute path instead
+        once(String::from("crate"))
+            .chain(to.data.iter().filter_map(|el| {
+                if let DefPathData::TypeNs(sym) = el.data {
+                    Some(sym.to_string())
+                } else {
+                    None
+                }
+            }))
+            .join("::")
+    } else {
+        repeat(String::from("super")).take(go_up_by).chain(path).join("::")
+    }
+}
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 11bb16ff6c5..6762c883005 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -1000,35 +1000,6 @@ pub fn adt_and_variant_of_res<'tcx>(cx: &LateContext<'tcx>, res: Res) -> Option<
     }
 }
 
-/// Checks if the type is a type parameter implementing `FnOnce`, but not `FnMut`.
-pub fn ty_is_fn_once_param<'tcx>(tcx: TyCtxt<'_>, ty: Ty<'tcx>, predicates: &'tcx [ty::Clause<'_>]) -> bool {
-    let ty::Param(ty) = *ty.kind() else {
-        return false;
-    };
-    let lang = tcx.lang_items();
-    let (Some(fn_once_id), Some(fn_mut_id), Some(fn_id)) = (lang.fn_once_trait(), lang.fn_mut_trait(), lang.fn_trait())
-    else {
-        return false;
-    };
-    predicates
-        .iter()
-        .try_fold(false, |found, p| {
-            if let ty::ClauseKind::Trait(p) = p.kind().skip_binder()
-                && let ty::Param(self_ty) = p.trait_ref.self_ty().kind()
-                && ty.index == self_ty.index
-            {
-                // This should use `super_traits_of`, but that's a private function.
-                if p.trait_ref.def_id == fn_once_id {
-                    return Some(true);
-                } else if p.trait_ref.def_id == fn_mut_id || p.trait_ref.def_id == fn_id {
-                    return None;
-                }
-            }
-            Some(found)
-        })
-        .unwrap_or(false)
-}
-
 /// Comes up with an "at least" guesstimate for the type's size, not taking into
 /// account the layout of type parameters.
 pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 {
diff --git a/src/tools/clippy/declare_clippy_lint/Cargo.toml b/src/tools/clippy/declare_clippy_lint/Cargo.toml
index 5aaafb41721..0f90cef5cdd 100644
--- a/src/tools/clippy/declare_clippy_lint/Cargo.toml
+++ b/src/tools/clippy/declare_clippy_lint/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "declare_clippy_lint"
-version = "0.1.77"
+version = "0.1.78"
 edition = "2021"
 publish = false
 
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index d2d56e59ee3..fcf5c456270 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,3 @@
 [toolchain]
-channel = "nightly-2024-01-25"
+channel = "nightly-2024-02-08"
 components = ["cargo", "llvm-tools", "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 1b159f5937a..bdf1cf0a224 100644
--- a/src/tools/clippy/src/driver.rs
+++ b/src/tools/clippy/src/driver.rs
@@ -24,9 +24,11 @@ use rustc_session::EarlyDiagCtxt;
 use rustc_span::symbol::Symbol;
 
 use std::env;
+use std::fs::read_to_string;
 use std::ops::Deref;
 use std::path::Path;
 use std::process::exit;
+use std::string::ToString;
 
 use anstream::println;
 
@@ -190,12 +192,31 @@ pub fn main() {
 
     exit(rustc_driver::catch_with_exit_code(move || {
         let mut orig_args: Vec<String> = env::args().collect();
-        let has_sysroot_arg = arg_value(&orig_args, "--sysroot", |_| true).is_some();
+
+        let has_sysroot_arg = |args: &mut [String]| -> bool {
+            if arg_value(args, "--sysroot", |_| true).is_some() {
+                return true;
+            }
+            // https://doc.rust-lang.org/rustc/command-line-arguments.html#path-load-command-line-flags-from-a-path
+            // Beside checking for existence of `--sysroot` on the command line, we need to
+            // check for the arg files that are prefixed with @ as well to be consistent with rustc
+            for arg in args.iter() {
+                if let Some(arg_file_path) = arg.strip_prefix('@') {
+                    if let Ok(arg_file) = read_to_string(arg_file_path) {
+                        let split_arg_file: Vec<String> = arg_file.lines().map(ToString::to_string).collect();
+                        if arg_value(&split_arg_file, "--sysroot", |_| true).is_some() {
+                            return true;
+                        }
+                    }
+                }
+            }
+            false
+        };
 
         let sys_root_env = std::env::var("SYSROOT").ok();
         let pass_sysroot_env_if_given = |args: &mut Vec<String>, sys_root_env| {
             if let Some(sys_root) = sys_root_env {
-                if !has_sysroot_arg {
+                if !has_sysroot_arg(args) {
                     args.extend(vec!["--sysroot".into(), sys_root]);
                 }
             };
diff --git a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr
new file mode 100644
index 00000000000..103e60d8484
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.stderr
@@ -0,0 +1,45 @@
+error: lint group `rust_2018_idioms` has the same priority (0) as a lint
+ --> Cargo.toml:7:1
+  |
+7 | rust_2018_idioms = "warn"
+  | ^^^^^^^^^^^^^^^^   ------ has an implicit priority of 0
+8 | bare_trait_objects = "allow"
+  | ------------------ has the same priority as this lint
+  |
+  = note: the order of the lints in the table is ignored by Cargo
+  = note: `#[deny(clippy::lint_groups_priority)]` on by default
+help: to have lints override the group set `rust_2018_idioms` to a lower priority
+  |
+7 | rust_2018_idioms = { level = "warn", priority = -1 }
+  |                    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: lint group `unused` has the same priority (0) as a lint
+  --> Cargo.toml:10:1
+   |
+10 | unused = { level = "deny" }
+   | ^^^^^^   ------------------ has an implicit priority of 0
+11 | unused_braces = { level = "allow", priority = 1 }
+12 | unused_attributes = { level = "allow" }
+   | ----------------- has the same priority as this lint
+   |
+   = note: the order of the lints in the table is ignored by Cargo
+help: to have lints override the group set `unused` to a lower priority
+   |
+10 | unused = { level = "deny", priority = -1 }
+   |          ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: lint group `pedantic` has the same priority (-1) as a lint
+  --> Cargo.toml:19:1
+   |
+19 | pedantic = { level = "warn", priority = -1 }
+   | ^^^^^^^^
+20 | similar_names = { level = "allow", priority = -1 }
+   | ------------- has the same priority as this lint
+   |
+   = note: the order of the lints in the table is ignored by Cargo
+help: to have lints override the group set `pedantic` to a lower priority
+   |
+19 | pedantic = { level = "warn", priority = -2 }
+   |            ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: could not compile `fail` (lib) due to 3 previous errors
diff --git a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml
new file mode 100644
index 00000000000..4ce41f78171
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "fail"
+version = "0.1.0"
+publish = false
+
+[lints.rust]
+rust_2018_idioms = "warn"
+bare_trait_objects = "allow"
+
+unused = { level = "deny" }
+unused_braces = { level = "allow", priority = 1 }
+unused_attributes = { level = "allow" }
+
+# `warnings` is not a group so the order it is passed does not matter
+warnings = "deny"
+deprecated  = "allow"
+
+[lints.clippy]
+pedantic = { level = "warn", priority = -1 }
+similar_names = { level = "allow", priority = -1 }
diff --git a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/src/lib.rs b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/src/lib.rs
new file mode 100644
index 00000000000..8b137891791
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/fail/src/lib.rs
@@ -0,0 +1 @@
+
diff --git a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/pass/Cargo.toml b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/pass/Cargo.toml
new file mode 100644
index 00000000000..e9fcf803d93
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/pass/Cargo.toml
@@ -0,0 +1,10 @@
+[package]
+name = "pass"
+version = "0.1.0"
+publish = false
+
+[lints.clippy]
+pedantic = { level = "warn", priority = -1 }
+style = { level = "warn", priority = 1 }
+similar_names = "allow"
+dbg_macro = { level = "warn", priority = 2 }
diff --git a/src/tools/clippy/tests/ui-cargo/lint_groups_priority/pass/src/lib.rs b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/pass/src/lib.rs
new file mode 100644
index 00000000000..8b137891791
--- /dev/null
+++ b/src/tools/clippy/tests/ui-cargo/lint_groups_priority/pass/src/lib.rs
@@ -0,0 +1 @@
+
diff --git a/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.fixed b/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.fixed
index 6c58e07d846..497f783087a 100644
--- a/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.fixed
+++ b/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.fixed
@@ -1,4 +1,4 @@
-#![allow(clippy::redundant_clone, clippy::unnecessary_operation)]
+#![allow(clippy::redundant_clone, clippy::unnecessary_operation, clippy::incompatible_msrv)]
 #![warn(clippy::manual_non_exhaustive, clippy::borrow_as_ptr, clippy::manual_bits)]
 
 use std::mem::{size_of, size_of_val};
diff --git a/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs b/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs
index e1dc3f4389c..6e7874108a3 100644
--- a/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs
+++ b/src/tools/clippy/tests/ui-toml/min_rust_version/min_rust_version.rs
@@ -1,4 +1,4 @@
-#![allow(clippy::redundant_clone, clippy::unnecessary_operation)]
+#![allow(clippy::redundant_clone, clippy::unnecessary_operation, clippy::incompatible_msrv)]
 #![warn(clippy::manual_non_exhaustive, clippy::borrow_as_ptr, clippy::manual_bits)]
 
 use std::mem::{size_of, size_of_val};
diff --git a/src/tools/clippy/tests/ui-toml/modulo_arithmetic/clippy.toml b/src/tools/clippy/tests/ui-toml/modulo_arithmetic/clippy.toml
new file mode 100644
index 00000000000..de80f4ae598
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/modulo_arithmetic/clippy.toml
@@ -0,0 +1 @@
+allow-comparison-to-zero = false
diff --git a/src/tools/clippy/tests/ui-toml/modulo_arithmetic/modulo_arithmetic.rs b/src/tools/clippy/tests/ui-toml/modulo_arithmetic/modulo_arithmetic.rs
new file mode 100644
index 00000000000..27d27564baf
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/modulo_arithmetic/modulo_arithmetic.rs
@@ -0,0 +1,10 @@
+#![warn(clippy::modulo_arithmetic)]
+
+fn main() {
+    let a = -1;
+    let b = 2;
+    let c = a % b == 0;
+    let c = a % b != 0;
+    let c = 0 == a % b;
+    let c = 0 != a % b;
+}
diff --git a/src/tools/clippy/tests/ui-toml/modulo_arithmetic/modulo_arithmetic.stderr b/src/tools/clippy/tests/ui-toml/modulo_arithmetic/modulo_arithmetic.stderr
new file mode 100644
index 00000000000..da644b05a11
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/modulo_arithmetic/modulo_arithmetic.stderr
@@ -0,0 +1,40 @@
+error: you are using modulo operator on types that might have different signs
+  --> $DIR/modulo_arithmetic.rs:6:13
+   |
+LL |     let c = a % b == 0;
+   |             ^^^^^
+   |
+   = note: double check for expected result especially when interoperating with different languages
+   = note: or consider using `rem_euclid` or similar function
+   = note: `-D clippy::modulo-arithmetic` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::modulo_arithmetic)]`
+
+error: you are using modulo operator on types that might have different signs
+  --> $DIR/modulo_arithmetic.rs:7:13
+   |
+LL |     let c = a % b != 0;
+   |             ^^^^^
+   |
+   = note: double check for expected result especially when interoperating with different languages
+   = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
+  --> $DIR/modulo_arithmetic.rs:8:18
+   |
+LL |     let c = 0 == a % b;
+   |                  ^^^^^
+   |
+   = note: double check for expected result especially when interoperating with different languages
+   = note: or consider using `rem_euclid` or similar function
+
+error: you are using modulo operator on types that might have different signs
+  --> $DIR/modulo_arithmetic.rs:9:18
+   |
+LL |     let c = 0 != a % b;
+   |                  ^^^^^
+   |
+   = note: double check for expected result especially when interoperating with different languages
+   = note: or consider using `rem_euclid` or similar function
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
index fc683e514ba..f097d2503e1 100644
--- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
+++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
@@ -3,6 +3,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
            absolute-paths-max-segments
            accept-comment-above-attributes
            accept-comment-above-statement
+           allow-comparison-to-zero
            allow-dbg-in-tests
            allow-expect-in-tests
            allow-mixed-uninlined-format-args
@@ -14,6 +15,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
            allowed-duplicate-crates
            allowed-idents-below-min-chars
            allowed-scripts
+           allowed-wildcard-imports
            arithmetic-side-effects-allowed
            arithmetic-side-effects-allowed-binary
            arithmetic-side-effects-allowed-unary
@@ -80,6 +82,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
            absolute-paths-max-segments
            accept-comment-above-attributes
            accept-comment-above-statement
+           allow-comparison-to-zero
            allow-dbg-in-tests
            allow-expect-in-tests
            allow-mixed-uninlined-format-args
@@ -91,6 +94,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
            allowed-duplicate-crates
            allowed-idents-below-min-chars
            allowed-scripts
+           allowed-wildcard-imports
            arithmetic-side-effects-allowed
            arithmetic-side-effects-allowed-binary
            arithmetic-side-effects-allowed-unary
@@ -157,6 +161,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
            absolute-paths-max-segments
            accept-comment-above-attributes
            accept-comment-above-statement
+           allow-comparison-to-zero
            allow-dbg-in-tests
            allow-expect-in-tests
            allow-mixed-uninlined-format-args
@@ -168,6 +173,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
            allowed-duplicate-crates
            allowed-idents-below-min-chars
            allowed-scripts
+           allowed-wildcard-imports
            arithmetic-side-effects-allowed
            arithmetic-side-effects-allowed-binary
            arithmetic-side-effects-allowed-unary
diff --git a/src/tools/clippy/tests/ui-toml/wildcard_imports/clippy.toml b/src/tools/clippy/tests/ui-toml/wildcard_imports/clippy.toml
index 875aaeef6c9..68815c1756a 100644
--- a/src/tools/clippy/tests/ui-toml/wildcard_imports/clippy.toml
+++ b/src/tools/clippy/tests/ui-toml/wildcard_imports/clippy.toml
@@ -1 +1,4 @@
 warn-on-all-wildcard-imports = true
+
+# This should be ignored since `warn-on-all-wildcard-imports` has higher precedence
+allowed-wildcard-imports = ["utils"]
diff --git a/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.fixed b/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.fixed
index 1752f48856c..af72d6be0e0 100644
--- a/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.fixed
+++ b/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.fixed
@@ -3,9 +3,28 @@
 mod prelude {
     pub const FOO: u8 = 1;
 }
+
+mod utils {
+    pub const BAR: u8 = 1;
+    pub fn print() {}
+}
+
+mod my_crate {
+    pub mod utils {
+        pub fn my_util_fn() {}
+    }
+}
+
+use utils::{BAR, print};
+//~^ ERROR: usage of wildcard import
+use my_crate::utils::my_util_fn;
+//~^ ERROR: usage of wildcard import
 use prelude::FOO;
 //~^ ERROR: usage of wildcard import
 
 fn main() {
     let _ = FOO;
+    let _ = BAR;
+    print();
+    my_util_fn();
 }
diff --git a/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.rs b/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.rs
index 331c2c59c22..91009dd8835 100644
--- a/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.rs
+++ b/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.rs
@@ -3,9 +3,28 @@
 mod prelude {
     pub const FOO: u8 = 1;
 }
+
+mod utils {
+    pub const BAR: u8 = 1;
+    pub fn print() {}
+}
+
+mod my_crate {
+    pub mod utils {
+        pub fn my_util_fn() {}
+    }
+}
+
+use utils::*;
+//~^ ERROR: usage of wildcard import
+use my_crate::utils::*;
+//~^ ERROR: usage of wildcard import
 use prelude::*;
 //~^ ERROR: usage of wildcard import
 
 fn main() {
     let _ = FOO;
+    let _ = BAR;
+    print();
+    my_util_fn();
 }
diff --git a/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.stderr b/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.stderr
index f11fda6a0c6..a733d786d0e 100644
--- a/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.stderr
+++ b/src/tools/clippy/tests/ui-toml/wildcard_imports/wildcard_imports.stderr
@@ -1,11 +1,23 @@
 error: usage of wildcard import
-  --> $DIR/wildcard_imports.rs:6:5
+  --> $DIR/wildcard_imports.rs:18:5
    |
-LL | use prelude::*;
-   |     ^^^^^^^^^^ help: try: `prelude::FOO`
+LL | use utils::*;
+   |     ^^^^^^^^ help: try: `utils::{BAR, print}`
    |
    = note: `-D clippy::wildcard-imports` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::wildcard_imports)]`
 
-error: aborting due to 1 previous error
+error: usage of wildcard import
+  --> $DIR/wildcard_imports.rs:20:5
+   |
+LL | use my_crate::utils::*;
+   |     ^^^^^^^^^^^^^^^^^^ help: try: `my_crate::utils::my_util_fn`
+
+error: usage of wildcard import
+  --> $DIR/wildcard_imports.rs:22:5
+   |
+LL | use prelude::*;
+   |     ^^^^^^^^^^ help: try: `prelude::FOO`
+
+error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui-toml/wildcard_imports_whitelist/clippy.toml b/src/tools/clippy/tests/ui-toml/wildcard_imports_whitelist/clippy.toml
new file mode 100644
index 00000000000..6b7882e64a8
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/wildcard_imports_whitelist/clippy.toml
@@ -0,0 +1 @@
+allowed-wildcard-imports = ["utils"]
diff --git a/src/tools/clippy/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.fixed b/src/tools/clippy/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.fixed
new file mode 100644
index 00000000000..d539525c45e
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.fixed
@@ -0,0 +1,26 @@
+#![warn(clippy::wildcard_imports)]
+
+mod utils {
+    pub fn print() {}
+}
+
+mod utils_plus {
+    pub fn do_something() {}
+}
+
+mod my_crate {
+    pub mod utils {
+        pub fn my_util_fn() {}
+    }
+}
+
+use my_crate::utils::*;
+use utils::*;
+use utils_plus::do_something;
+//~^ ERROR: usage of wildcard import
+
+fn main() {
+    print();
+    my_util_fn();
+    do_something();
+}
diff --git a/src/tools/clippy/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.rs b/src/tools/clippy/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.rs
new file mode 100644
index 00000000000..9169d16a08b
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.rs
@@ -0,0 +1,26 @@
+#![warn(clippy::wildcard_imports)]
+
+mod utils {
+    pub fn print() {}
+}
+
+mod utils_plus {
+    pub fn do_something() {}
+}
+
+mod my_crate {
+    pub mod utils {
+        pub fn my_util_fn() {}
+    }
+}
+
+use my_crate::utils::*;
+use utils::*;
+use utils_plus::*;
+//~^ ERROR: usage of wildcard import
+
+fn main() {
+    print();
+    my_util_fn();
+    do_something();
+}
diff --git a/src/tools/clippy/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.stderr b/src/tools/clippy/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.stderr
new file mode 100644
index 00000000000..12c2f9cbada
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/wildcard_imports_whitelist/wildcard_imports.stderr
@@ -0,0 +1,11 @@
+error: usage of wildcard import
+  --> $DIR/wildcard_imports.rs:19:5
+   |
+LL | use utils_plus::*;
+   |     ^^^^^^^^^^^^^ help: try: `utils_plus::do_something`
+   |
+   = note: `-D clippy::wildcard-imports` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::wildcard_imports)]`
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed
index 6c0de96d65e..289a5ef38b8 100644
--- a/src/tools/clippy/tests/ui/borrow_as_ptr.fixed
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr.fixed
@@ -5,6 +5,7 @@ fn a() -> i32 {
     0
 }
 
+#[clippy::msrv = "1.75"]
 fn main() {
     let val = 1;
     let _p = std::ptr::addr_of!(val);
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.rs b/src/tools/clippy/tests/ui/borrow_as_ptr.rs
index c37c5357c82..b5328cb22dc 100644
--- a/src/tools/clippy/tests/ui/borrow_as_ptr.rs
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr.rs
@@ -5,6 +5,7 @@ fn a() -> i32 {
     0
 }
 
+#[clippy::msrv = "1.75"]
 fn main() {
     let val = 1;
     let _p = &val as *const i32;
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr.stderr b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr
index 43a7a6bf5b5..b9861805905 100644
--- a/src/tools/clippy/tests/ui/borrow_as_ptr.stderr
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr.stderr
@@ -1,5 +1,5 @@
 error: borrow as raw pointer
-  --> $DIR/borrow_as_ptr.rs:10:14
+  --> $DIR/borrow_as_ptr.rs:11:14
    |
 LL |     let _p = &val as *const i32;
    |              ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of!(val)`
@@ -8,7 +8,7 @@ LL |     let _p = &val as *const i32;
    = help: to override `-D warnings` add `#[allow(clippy::borrow_as_ptr)]`
 
 error: borrow as raw pointer
-  --> $DIR/borrow_as_ptr.rs:17:18
+  --> $DIR/borrow_as_ptr.rs:18:18
    |
 LL |     let _p_mut = &mut val_mut as *mut i32;
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::addr_of_mut!(val_mut)`
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.fixed b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.fixed
index a361a36474d..f66554de300 100644
--- a/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.fixed
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.fixed
@@ -2,6 +2,7 @@
 #![feature(lang_items, start, libc)]
 #![no_std]
 
+#[clippy::msrv = "1.75"]
 #[start]
 fn main(_argc: isize, _argv: *const *const u8) -> isize {
     let val = 1;
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.rs b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.rs
index b3fe01442b7..1fc254aafa7 100644
--- a/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.rs
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.rs
@@ -2,6 +2,7 @@
 #![feature(lang_items, start, libc)]
 #![no_std]
 
+#[clippy::msrv = "1.75"]
 #[start]
 fn main(_argc: isize, _argv: *const *const u8) -> isize {
     let val = 1;
diff --git a/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.stderr b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.stderr
index 2f258bcf966..1ef0a948a32 100644
--- a/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.stderr
+++ b/src/tools/clippy/tests/ui/borrow_as_ptr_no_std.stderr
@@ -1,5 +1,5 @@
 error: borrow as raw pointer
-  --> $DIR/borrow_as_ptr_no_std.rs:8:14
+  --> $DIR/borrow_as_ptr_no_std.rs:9:14
    |
 LL |     let _p = &val as *const i32;
    |              ^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::addr_of!(val)`
@@ -8,7 +8,7 @@ LL |     let _p = &val as *const i32;
    = help: to override `-D warnings` add `#[allow(clippy::borrow_as_ptr)]`
 
 error: borrow as raw pointer
-  --> $DIR/borrow_as_ptr_no_std.rs:11:18
+  --> $DIR/borrow_as_ptr_no_std.rs:12:18
    |
 LL |     let _p_mut = &mut val_mut as *mut i32;
    |                  ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `core::ptr::addr_of_mut!(val_mut)`
diff --git a/src/tools/clippy/tests/ui/eta.fixed b/src/tools/clippy/tests/ui/eta.fixed
index 32c7499bf73..da28ec2e653 100644
--- a/src/tools/clippy/tests/ui/eta.fixed
+++ b/src/tools/clippy/tests/ui/eta.fixed
@@ -346,6 +346,23 @@ fn angle_brackets_and_args() {
     dyn_opt.map(<dyn TestTrait>::method_on_dyn);
 }
 
+// https://github.com/rust-lang/rust-clippy/issues/12199
+fn track_caller_fp() {
+    struct S;
+    impl S {
+        #[track_caller]
+        fn add_location(self) {}
+    }
+
+    #[track_caller]
+    fn add_location() {}
+
+    fn foo(_: fn()) {}
+    fn foo2(_: fn(S)) {}
+    foo(|| add_location());
+    foo2(|s| s.add_location());
+}
+
 fn _late_bound_to_early_bound_regions() {
     struct Foo<'a>(&'a u32);
     impl<'a> Foo<'a> {
@@ -400,3 +417,57 @@ fn _closure_with_types() {
     let _ = f2(|x: u32| f(x));
     let _ = f2(|x| -> u32 { f(x) });
 }
+
+/// https://github.com/rust-lang/rust-clippy/issues/10854
+/// This is to verify that redundant_closure_for_method_calls resolves suggested paths to relative.
+mod issue_10854 {
+    pub mod test_mod {
+        pub struct Test;
+
+        impl Test {
+            pub fn method(self) -> i32 {
+                0
+            }
+        }
+
+        pub fn calls_test(test: Option<Test>) -> Option<i32> {
+            test.map(Test::method)
+        }
+
+        pub fn calls_outer(test: Option<super::Outer>) -> Option<i32> {
+            test.map(super::Outer::method)
+        }
+    }
+
+    pub struct Outer;
+
+    impl Outer {
+        pub fn method(self) -> i32 {
+            0
+        }
+    }
+
+    pub fn calls_into_mod(test: Option<test_mod::Test>) -> Option<i32> {
+        test.map(test_mod::Test::method)
+    }
+
+    mod a {
+        pub mod b {
+            pub mod c {
+                pub fn extreme_nesting(test: Option<super::super::super::d::Test>) -> Option<i32> {
+                    test.map(crate::issue_10854::d::Test::method)
+                }
+            }
+        }
+    }
+
+    mod d {
+        pub struct Test;
+
+        impl Test {
+            pub fn method(self) -> i32 {
+                0
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/eta.rs b/src/tools/clippy/tests/ui/eta.rs
index 25b7431ba8c..f924100f8f4 100644
--- a/src/tools/clippy/tests/ui/eta.rs
+++ b/src/tools/clippy/tests/ui/eta.rs
@@ -346,6 +346,23 @@ fn angle_brackets_and_args() {
     dyn_opt.map(|d| d.method_on_dyn());
 }
 
+// https://github.com/rust-lang/rust-clippy/issues/12199
+fn track_caller_fp() {
+    struct S;
+    impl S {
+        #[track_caller]
+        fn add_location(self) {}
+    }
+
+    #[track_caller]
+    fn add_location() {}
+
+    fn foo(_: fn()) {}
+    fn foo2(_: fn(S)) {}
+    foo(|| add_location());
+    foo2(|s| s.add_location());
+}
+
 fn _late_bound_to_early_bound_regions() {
     struct Foo<'a>(&'a u32);
     impl<'a> Foo<'a> {
@@ -400,3 +417,57 @@ fn _closure_with_types() {
     let _ = f2(|x: u32| f(x));
     let _ = f2(|x| -> u32 { f(x) });
 }
+
+/// https://github.com/rust-lang/rust-clippy/issues/10854
+/// This is to verify that redundant_closure_for_method_calls resolves suggested paths to relative.
+mod issue_10854 {
+    pub mod test_mod {
+        pub struct Test;
+
+        impl Test {
+            pub fn method(self) -> i32 {
+                0
+            }
+        }
+
+        pub fn calls_test(test: Option<Test>) -> Option<i32> {
+            test.map(|t| t.method())
+        }
+
+        pub fn calls_outer(test: Option<super::Outer>) -> Option<i32> {
+            test.map(|t| t.method())
+        }
+    }
+
+    pub struct Outer;
+
+    impl Outer {
+        pub fn method(self) -> i32 {
+            0
+        }
+    }
+
+    pub fn calls_into_mod(test: Option<test_mod::Test>) -> Option<i32> {
+        test.map(|t| t.method())
+    }
+
+    mod a {
+        pub mod b {
+            pub mod c {
+                pub fn extreme_nesting(test: Option<super::super::super::d::Test>) -> Option<i32> {
+                    test.map(|t| t.method())
+                }
+            }
+        }
+    }
+
+    mod d {
+        pub struct Test;
+
+        impl Test {
+            pub fn method(self) -> i32 {
+                0
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/eta.stderr b/src/tools/clippy/tests/ui/eta.stderr
index 951e4ac749c..945de466d83 100644
--- a/src/tools/clippy/tests/ui/eta.stderr
+++ b/src/tools/clippy/tests/ui/eta.stderr
@@ -161,10 +161,34 @@ LL |     dyn_opt.map(|d| d.method_on_dyn());
    |                 ^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the method itself: `<dyn TestTrait>::method_on_dyn`
 
 error: redundant closure
-  --> $DIR/eta.rs:389:19
+  --> $DIR/eta.rs:406:19
    |
 LL |     let _ = f(&0, |x, y| f2(x, y));
    |                   ^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `f2`
 
-error: aborting due to 27 previous errors
+error: redundant closure
+  --> $DIR/eta.rs:434:22
+   |
+LL |             test.map(|t| t.method())
+   |                      ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `Test::method`
+
+error: redundant closure
+  --> $DIR/eta.rs:438:22
+   |
+LL |             test.map(|t| t.method())
+   |                      ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `super::Outer::method`
+
+error: redundant closure
+  --> $DIR/eta.rs:451:18
+   |
+LL |         test.map(|t| t.method())
+   |                  ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `test_mod::Test::method`
+
+error: redundant closure
+  --> $DIR/eta.rs:458:30
+   |
+LL |                     test.map(|t| t.method())
+   |                              ^^^^^^^^^^^^^^ help: replace the closure with the method itself: `crate::issue_10854::d::Test::method`
+
+error: aborting due to 31 previous errors
 
diff --git a/src/tools/clippy/tests/ui/format_args.fixed b/src/tools/clippy/tests/ui/format_args.fixed
index ddd5976c408..cab20b11e07 100644
--- a/src/tools/clippy/tests/ui/format_args.fixed
+++ b/src/tools/clippy/tests/ui/format_args.fixed
@@ -14,6 +14,7 @@ use std::panic::Location;
 
 struct Somewhere;
 
+#[allow(clippy::to_string_trait_impl)]
 impl ToString for Somewhere {
     fn to_string(&self) -> String {
         String::from("somewhere")
diff --git a/src/tools/clippy/tests/ui/format_args.rs b/src/tools/clippy/tests/ui/format_args.rs
index 18e1bc1af67..bc3645cb2c2 100644
--- a/src/tools/clippy/tests/ui/format_args.rs
+++ b/src/tools/clippy/tests/ui/format_args.rs
@@ -14,6 +14,7 @@ use std::panic::Location;
 
 struct Somewhere;
 
+#[allow(clippy::to_string_trait_impl)]
 impl ToString for Somewhere {
     fn to_string(&self) -> String {
         String::from("somewhere")
diff --git a/src/tools/clippy/tests/ui/format_args.stderr b/src/tools/clippy/tests/ui/format_args.stderr
index dcdfa668aff..2f1714296d6 100644
--- a/src/tools/clippy/tests/ui/format_args.stderr
+++ b/src/tools/clippy/tests/ui/format_args.stderr
@@ -1,5 +1,5 @@
 error: `to_string` applied to a type that implements `Display` in `format!` args
-  --> $DIR/format_args.rs:76:72
+  --> $DIR/format_args.rs:77:72
    |
 LL |     let _ = format!("error: something failed at {}", Location::caller().to_string());
    |                                                                        ^^^^^^^^^^^^ help: remove this
@@ -8,145 +8,145 @@ LL |     let _ = format!("error: something failed at {}", Location::caller().to_
    = help: to override `-D warnings` add `#[allow(clippy::to_string_in_format_args)]`
 
 error: `to_string` applied to a type that implements `Display` in `write!` args
-  --> $DIR/format_args.rs:80:27
+  --> $DIR/format_args.rs:81:27
    |
 LL |         Location::caller().to_string()
    |                           ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `writeln!` args
-  --> $DIR/format_args.rs:85:27
+  --> $DIR/format_args.rs:86:27
    |
 LL |         Location::caller().to_string()
    |                           ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `print!` args
-  --> $DIR/format_args.rs:87:63
+  --> $DIR/format_args.rs:88:63
    |
 LL |     print!("error: something failed at {}", Location::caller().to_string());
    |                                                               ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:88:65
+  --> $DIR/format_args.rs:89:65
    |
 LL |     println!("error: something failed at {}", Location::caller().to_string());
    |                                                                 ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `eprint!` args
-  --> $DIR/format_args.rs:89:64
+  --> $DIR/format_args.rs:90:64
    |
 LL |     eprint!("error: something failed at {}", Location::caller().to_string());
    |                                                                ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `eprintln!` args
-  --> $DIR/format_args.rs:90:66
+  --> $DIR/format_args.rs:91:66
    |
 LL |     eprintln!("error: something failed at {}", Location::caller().to_string());
    |                                                                  ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `format_args!` args
-  --> $DIR/format_args.rs:91:77
+  --> $DIR/format_args.rs:92:77
    |
 LL |     let _ = format_args!("error: something failed at {}", Location::caller().to_string());
    |                                                                             ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `assert!` args
-  --> $DIR/format_args.rs:92:70
+  --> $DIR/format_args.rs:93:70
    |
 LL |     assert!(true, "error: something failed at {}", Location::caller().to_string());
    |                                                                      ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `assert_eq!` args
-  --> $DIR/format_args.rs:93:73
+  --> $DIR/format_args.rs:94:73
    |
 LL |     assert_eq!(0, 0, "error: something failed at {}", Location::caller().to_string());
    |                                                                         ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `assert_ne!` args
-  --> $DIR/format_args.rs:94:73
+  --> $DIR/format_args.rs:95:73
    |
 LL |     assert_ne!(0, 0, "error: something failed at {}", Location::caller().to_string());
    |                                                                         ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `panic!` args
-  --> $DIR/format_args.rs:95:63
+  --> $DIR/format_args.rs:96:63
    |
 LL |     panic!("error: something failed at {}", Location::caller().to_string());
    |                                                               ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:96:20
+  --> $DIR/format_args.rs:97:20
    |
 LL |     println!("{}", X(1).to_string());
    |                    ^^^^^^^^^^^^^^^^ help: use this: `*X(1)`
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:97:20
+  --> $DIR/format_args.rs:98:20
    |
 LL |     println!("{}", Y(&X(1)).to_string());
    |                    ^^^^^^^^^^^^^^^^^^^^ help: use this: `***Y(&X(1))`
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:98:24
+  --> $DIR/format_args.rs:99:24
    |
 LL |     println!("{}", Z(1).to_string());
    |                        ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:99:20
+  --> $DIR/format_args.rs:100:20
    |
 LL |     println!("{}", x.to_string());
    |                    ^^^^^^^^^^^^^ help: use this: `**x`
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:100:20
+  --> $DIR/format_args.rs:101:20
    |
 LL |     println!("{}", x_ref.to_string());
    |                    ^^^^^^^^^^^^^^^^^ help: use this: `***x_ref`
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:102:39
+  --> $DIR/format_args.rs:103:39
    |
 LL |     println!("{foo}{bar}", foo = "foo".to_string(), bar = "bar");
    |                                       ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:103:52
+  --> $DIR/format_args.rs:104:52
    |
 LL |     println!("{foo}{bar}", foo = "foo", bar = "bar".to_string());
    |                                                    ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:104:39
+  --> $DIR/format_args.rs:105:39
    |
 LL |     println!("{foo}{bar}", bar = "bar".to_string(), foo = "foo");
    |                                       ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:105:52
+  --> $DIR/format_args.rs:106:52
    |
 LL |     println!("{foo}{bar}", bar = "bar", foo = "foo".to_string());
    |                                                    ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `print!` args
-  --> $DIR/format_args.rs:117:37
+  --> $DIR/format_args.rs:118:37
    |
 LL |     print!("{}", (Location::caller().to_string()));
    |                                     ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `print!` args
-  --> $DIR/format_args.rs:118:39
+  --> $DIR/format_args.rs:119:39
    |
 LL |     print!("{}", ((Location::caller()).to_string()));
    |                                       ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `format!` args
-  --> $DIR/format_args.rs:146:38
+  --> $DIR/format_args.rs:147:38
    |
 LL |         let x = format!("{} {}", a, b.to_string());
    |                                      ^^^^^^^^^^^^ help: remove this
 
 error: `to_string` applied to a type that implements `Display` in `println!` args
-  --> $DIR/format_args.rs:160:24
+  --> $DIR/format_args.rs:161:24
    |
 LL |         println!("{}", original[..10].to_string());
    |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use this: `&original[..10]`
diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed
index 707a0e76e4e..118f0b48895 100644
--- a/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed
+++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.fixed
@@ -1,6 +1,11 @@
 //@aux-build:proc_macro_derive.rs
 #![warn(clippy::ignored_unit_patterns)]
-#![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)]
+#![allow(
+    clippy::let_unit_value,
+    clippy::redundant_pattern_matching,
+    clippy::single_match,
+    clippy::needless_borrow
+)]
 
 fn foo() -> Result<(), ()> {
     unimplemented!()
diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.rs b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs
index 544f2b8f692..92feb9e6c28 100644
--- a/src/tools/clippy/tests/ui/ignored_unit_patterns.rs
+++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.rs
@@ -1,6 +1,11 @@
 //@aux-build:proc_macro_derive.rs
 #![warn(clippy::ignored_unit_patterns)]
-#![allow(clippy::let_unit_value, clippy::redundant_pattern_matching, clippy::single_match)]
+#![allow(
+    clippy::let_unit_value,
+    clippy::redundant_pattern_matching,
+    clippy::single_match,
+    clippy::needless_borrow
+)]
 
 fn foo() -> Result<(), ()> {
     unimplemented!()
diff --git a/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr
index 05c8f281e55..18ca7ebbcf2 100644
--- a/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr
+++ b/src/tools/clippy/tests/ui/ignored_unit_patterns.stderr
@@ -1,5 +1,5 @@
 error: matching over `()` is more explicit
-  --> $DIR/ignored_unit_patterns.rs:11:12
+  --> $DIR/ignored_unit_patterns.rs:16:12
    |
 LL |         Ok(_) => {},
    |            ^ help: use `()` instead of `_`: `()`
@@ -8,49 +8,49 @@ LL |         Ok(_) => {},
    = help: to override `-D warnings` add `#[allow(clippy::ignored_unit_patterns)]`
 
 error: matching over `()` is more explicit
-  --> $DIR/ignored_unit_patterns.rs:12:13
+  --> $DIR/ignored_unit_patterns.rs:17:13
    |
 LL |         Err(_) => {},
    |             ^ help: use `()` instead of `_`: `()`
 
 error: matching over `()` is more explicit
-  --> $DIR/ignored_unit_patterns.rs:14:15
+  --> $DIR/ignored_unit_patterns.rs:19:15
    |
 LL |     if let Ok(_) = foo() {}
    |               ^ help: use `()` instead of `_`: `()`
 
 error: matching over `()` is more explicit
-  --> $DIR/ignored_unit_patterns.rs:16:28
+  --> $DIR/ignored_unit_patterns.rs:21:28
    |
 LL |     let _ = foo().map_err(|_| todo!());
    |                            ^ help: use `()` instead of `_`: `()`
 
 error: matching over `()` is more explicit
-  --> $DIR/ignored_unit_patterns.rs:22:16
+  --> $DIR/ignored_unit_patterns.rs:27:16
    |
 LL |             Ok(_) => {},
    |                ^ help: use `()` instead of `_`: `()`
 
 error: matching over `()` is more explicit
-  --> $DIR/ignored_unit_patterns.rs:24:17
+  --> $DIR/ignored_unit_patterns.rs:29:17
    |
 LL |             Err(_) => {},
    |                 ^ help: use `()` instead of `_`: `()`
 
 error: matching over `()` is more explicit
-  --> $DIR/ignored_unit_patterns.rs:36:9
+  --> $DIR/ignored_unit_patterns.rs:41:9
    |
 LL |     let _ = foo().unwrap();
    |         ^ help: use `()` instead of `_`: `()`
 
 error: matching over `()` is more explicit
-  --> $DIR/ignored_unit_patterns.rs:45:13
+  --> $DIR/ignored_unit_patterns.rs:50:13
    |
 LL |         (1, _) => unimplemented!(),
    |             ^ help: use `()` instead of `_`: `()`
 
 error: matching over `()` is more explicit
-  --> $DIR/ignored_unit_patterns.rs:52:13
+  --> $DIR/ignored_unit_patterns.rs:57:13
    |
 LL |     for (x, _) in v {
    |             ^ help: use `()` instead of `_`: `()`
diff --git a/src/tools/clippy/tests/ui/incompatible_msrv.rs b/src/tools/clippy/tests/ui/incompatible_msrv.rs
new file mode 100644
index 00000000000..a92017fb0f6
--- /dev/null
+++ b/src/tools/clippy/tests/ui/incompatible_msrv.rs
@@ -0,0 +1,23 @@
+#![warn(clippy::incompatible_msrv)]
+#![feature(custom_inner_attributes)]
+#![clippy::msrv = "1.3.0"]
+
+use std::collections::hash_map::Entry;
+use std::collections::HashMap;
+use std::thread::sleep;
+use std::time::Duration;
+
+fn foo() {
+    let mut map: HashMap<&str, u32> = HashMap::new();
+    assert_eq!(map.entry("poneyland").key(), &"poneyland");
+    //~^ ERROR: is `1.3.0` but this item is stable since `1.10.0`
+    if let Entry::Vacant(v) = map.entry("poneyland") {
+        v.into_key();
+        //~^ ERROR: is `1.3.0` but this item is stable since `1.12.0`
+    }
+    // Should warn for `sleep` but not for `Duration` (which was added in `1.3.0`).
+    sleep(Duration::new(1, 0));
+    //~^ ERROR: is `1.3.0` but this item is stable since `1.4.0`
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui/incompatible_msrv.stderr b/src/tools/clippy/tests/ui/incompatible_msrv.stderr
new file mode 100644
index 00000000000..bd5ecd6ed2f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/incompatible_msrv.stderr
@@ -0,0 +1,23 @@
+error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.10.0`
+  --> $DIR/incompatible_msrv.rs:12:39
+   |
+LL |     assert_eq!(map.entry("poneyland").key(), &"poneyland");
+   |                                       ^^^^^
+   |
+   = note: `-D clippy::incompatible-msrv` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::incompatible_msrv)]`
+
+error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.12.0`
+  --> $DIR/incompatible_msrv.rs:15:11
+   |
+LL |         v.into_key();
+   |           ^^^^^^^^^^
+
+error: current MSRV (Minimum Supported Rust Version) is `1.3.0` but this item is stable since `1.4.0`
+  --> $DIR/incompatible_msrv.rs:19:5
+   |
+LL |     sleep(Duration::new(1, 0));
+   |     ^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_c_str_literals.fixed b/src/tools/clippy/tests/ui/manual_c_str_literals.fixed
new file mode 100644
index 00000000000..a24d7088c88
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_c_str_literals.fixed
@@ -0,0 +1,60 @@
+#![warn(clippy::manual_c_str_literals)]
+#![allow(clippy::no_effect)]
+
+use std::ffi::CStr;
+
+macro_rules! cstr {
+    ($s:literal) => {
+        CStr::from_bytes_with_nul(concat!($s, "\0").as_bytes()).unwrap()
+    };
+}
+
+macro_rules! macro_returns_c_str {
+    () => {
+        CStr::from_bytes_with_nul(b"foo\0").unwrap();
+    };
+}
+
+macro_rules! macro_returns_byte_string {
+    () => {
+        b"foo\0"
+    };
+}
+
+#[clippy::msrv = "1.76.0"]
+fn pre_stabilization() {
+    CStr::from_bytes_with_nul(b"foo\0");
+}
+
+#[clippy::msrv = "1.77.0"]
+fn post_stabilization() {
+    c"foo";
+}
+
+fn main() {
+    c"foo";
+    c"foo";
+    c"foo";
+    c"foo\\0sdsd";
+    CStr::from_bytes_with_nul(br"foo\\0sdsd\0").unwrap();
+    CStr::from_bytes_with_nul(br"foo\x00").unwrap();
+    CStr::from_bytes_with_nul(br##"foo#a\0"##).unwrap();
+
+    unsafe { c"foo" };
+    unsafe { c"foo" };
+    let _: *const _ = c"foo".as_ptr();
+    let _: *const _ = c"foo".as_ptr();
+    let _: *const _ = "foo".as_ptr(); // not a C-string
+    let _: *const _ = "".as_ptr();
+    let _: *const _ = c"foo".as_ptr().cast::<i8>();
+    let _ = "็”ต่„‘".as_ptr();
+    let _ = "็”ต่„‘\\".as_ptr();
+    let _ = c"็”ต่„‘\\".as_ptr();
+    let _ = c"็”ต่„‘".as_ptr();
+    let _ = c"็”ต่„‘".as_ptr();
+
+    // Macro cases, don't lint:
+    cstr!("foo");
+    macro_returns_c_str!();
+    CStr::from_bytes_with_nul(macro_returns_byte_string!()).unwrap();
+}
diff --git a/src/tools/clippy/tests/ui/manual_c_str_literals.rs b/src/tools/clippy/tests/ui/manual_c_str_literals.rs
new file mode 100644
index 00000000000..0a007786720
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_c_str_literals.rs
@@ -0,0 +1,60 @@
+#![warn(clippy::manual_c_str_literals)]
+#![allow(clippy::no_effect)]
+
+use std::ffi::CStr;
+
+macro_rules! cstr {
+    ($s:literal) => {
+        CStr::from_bytes_with_nul(concat!($s, "\0").as_bytes()).unwrap()
+    };
+}
+
+macro_rules! macro_returns_c_str {
+    () => {
+        CStr::from_bytes_with_nul(b"foo\0").unwrap();
+    };
+}
+
+macro_rules! macro_returns_byte_string {
+    () => {
+        b"foo\0"
+    };
+}
+
+#[clippy::msrv = "1.76.0"]
+fn pre_stabilization() {
+    CStr::from_bytes_with_nul(b"foo\0");
+}
+
+#[clippy::msrv = "1.77.0"]
+fn post_stabilization() {
+    CStr::from_bytes_with_nul(b"foo\0");
+}
+
+fn main() {
+    CStr::from_bytes_with_nul(b"foo\0");
+    CStr::from_bytes_with_nul(b"foo\x00");
+    CStr::from_bytes_with_nul(b"foo\0").unwrap();
+    CStr::from_bytes_with_nul(b"foo\\0sdsd\0").unwrap();
+    CStr::from_bytes_with_nul(br"foo\\0sdsd\0").unwrap();
+    CStr::from_bytes_with_nul(br"foo\x00").unwrap();
+    CStr::from_bytes_with_nul(br##"foo#a\0"##).unwrap();
+
+    unsafe { CStr::from_ptr(b"foo\0".as_ptr().cast()) };
+    unsafe { CStr::from_ptr(b"foo\0".as_ptr() as *const _) };
+    let _: *const _ = b"foo\0".as_ptr();
+    let _: *const _ = "foo\0".as_ptr();
+    let _: *const _ = "foo".as_ptr(); // not a C-string
+    let _: *const _ = "".as_ptr();
+    let _: *const _ = b"foo\0".as_ptr().cast::<i8>();
+    let _ = "็”ต่„‘".as_ptr();
+    let _ = "็”ต่„‘\\".as_ptr();
+    let _ = "็”ต่„‘\\\0".as_ptr();
+    let _ = "็”ต่„‘\0".as_ptr();
+    let _ = "็”ต่„‘\x00".as_ptr();
+
+    // Macro cases, don't lint:
+    cstr!("foo");
+    macro_returns_c_str!();
+    CStr::from_bytes_with_nul(macro_returns_byte_string!()).unwrap();
+}
diff --git a/src/tools/clippy/tests/ui/manual_c_str_literals.stderr b/src/tools/clippy/tests/ui/manual_c_str_literals.stderr
new file mode 100644
index 00000000000..8de4e16f010
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_c_str_literals.stderr
@@ -0,0 +1,83 @@
+error: calling `CStr::new` with a byte string literal
+  --> $DIR/manual_c_str_literals.rs:31:5
+   |
+LL |     CStr::from_bytes_with_nul(b"foo\0");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"`
+   |
+   = note: `-D clippy::manual-c-str-literals` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::manual_c_str_literals)]`
+
+error: calling `CStr::new` with a byte string literal
+  --> $DIR/manual_c_str_literals.rs:35:5
+   |
+LL |     CStr::from_bytes_with_nul(b"foo\0");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"`
+
+error: calling `CStr::new` with a byte string literal
+  --> $DIR/manual_c_str_literals.rs:36:5
+   |
+LL |     CStr::from_bytes_with_nul(b"foo\x00");
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"`
+
+error: calling `CStr::new` with a byte string literal
+  --> $DIR/manual_c_str_literals.rs:37:5
+   |
+LL |     CStr::from_bytes_with_nul(b"foo\0").unwrap();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"`
+
+error: calling `CStr::new` with a byte string literal
+  --> $DIR/manual_c_str_literals.rs:38:5
+   |
+LL |     CStr::from_bytes_with_nul(b"foo\\0sdsd\0").unwrap();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo\\0sdsd"`
+
+error: calling `CStr::from_ptr` with a byte string literal
+  --> $DIR/manual_c_str_literals.rs:43:14
+   |
+LL |     unsafe { CStr::from_ptr(b"foo\0".as_ptr().cast()) };
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"`
+
+error: calling `CStr::from_ptr` with a byte string literal
+  --> $DIR/manual_c_str_literals.rs:44:14
+   |
+LL |     unsafe { CStr::from_ptr(b"foo\0".as_ptr() as *const _) };
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a `c""` literal: `c"foo"`
+
+error: manually constructing a nul-terminated string
+  --> $DIR/manual_c_str_literals.rs:45:23
+   |
+LL |     let _: *const _ = b"foo\0".as_ptr();
+   |                       ^^^^^^^^ help: use a `c""` literal: `c"foo"`
+
+error: manually constructing a nul-terminated string
+  --> $DIR/manual_c_str_literals.rs:46:23
+   |
+LL |     let _: *const _ = "foo\0".as_ptr();
+   |                       ^^^^^^^ help: use a `c""` literal: `c"foo"`
+
+error: manually constructing a nul-terminated string
+  --> $DIR/manual_c_str_literals.rs:49:23
+   |
+LL |     let _: *const _ = b"foo\0".as_ptr().cast::<i8>();
+   |                       ^^^^^^^^ help: use a `c""` literal: `c"foo"`
+
+error: manually constructing a nul-terminated string
+  --> $DIR/manual_c_str_literals.rs:52:13
+   |
+LL |     let _ = "็”ต่„‘\\\0".as_ptr();
+   |             ^^^^^^^^^^ help: use a `c""` literal: `c"็”ต่„‘\\"`
+
+error: manually constructing a nul-terminated string
+  --> $DIR/manual_c_str_literals.rs:53:13
+   |
+LL |     let _ = "็”ต่„‘\0".as_ptr();
+   |             ^^^^^^^^ help: use a `c""` literal: `c"็”ต่„‘"`
+
+error: manually constructing a nul-terminated string
+  --> $DIR/manual_c_str_literals.rs:54:13
+   |
+LL |     let _ = "็”ต่„‘\x00".as_ptr();
+   |             ^^^^^^^^^^ help: use a `c""` literal: `c"็”ต่„‘"`
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/manual_retain.fixed b/src/tools/clippy/tests/ui/manual_retain.fixed
index 4dea3e8bfe6..5540029bf6b 100644
--- a/src/tools/clippy/tests/ui/manual_retain.fixed
+++ b/src/tools/clippy/tests/ui/manual_retain.fixed
@@ -14,6 +14,9 @@ fn main() {
     _msrv_153();
     _msrv_126();
     _msrv_118();
+
+    issue_10393();
+    issue_12081();
 }
 
 fn binary_heap_retain() {
@@ -23,6 +26,11 @@ fn binary_heap_retain() {
     binary_heap.retain(|x| x % 2 == 0);
     binary_heap.retain(|x| x % 2 == 0);
 
+    // Do lint, because we use pattern matching
+    let mut tuples = BinaryHeap::from([(0, 1), (1, 2), (2, 3)]);
+    tuples.retain(|(ref x, ref y)| *x == 0);
+    tuples.retain(|(x, y)| *x == 0);
+
     // Do not lint, because type conversion is performed
     binary_heap = binary_heap
         .into_iter()
@@ -55,6 +63,9 @@ fn btree_map_retain() {
     btree_map.retain(|_, &mut v| v % 2 == 0);
     btree_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0));
 
+    // Do not lint, because the parameters are not matched in tuple pattern
+    btree_map = btree_map.into_iter().filter(|t| t.0 % 2 == 0).collect();
+
     // Do not lint.
     btree_map = btree_map
         .into_iter()
@@ -76,6 +87,11 @@ fn btree_set_retain() {
     btree_set.retain(|x| x % 2 == 0);
     btree_set.retain(|x| x % 2 == 0);
 
+    // Do lint, because we use pattern matching
+    let mut tuples = BTreeSet::from([(0, 1), (1, 2), (2, 3)]);
+    tuples.retain(|(ref x, ref y)| *x == 0);
+    tuples.retain(|(x, y)| *x == 0);
+
     // Do not lint, because type conversion is performed
     btree_set = btree_set
         .iter()
@@ -108,6 +124,9 @@ fn hash_map_retain() {
     hash_map.retain(|_, &mut v| v % 2 == 0);
     hash_map.retain(|k, &mut v| (k % 2 == 0) && (v % 2 == 0));
 
+    // Do not lint, because the parameters are not matched in tuple pattern
+    hash_map = hash_map.into_iter().filter(|t| t.0 % 2 == 0).collect();
+
     // Do not lint.
     hash_map = hash_map
         .into_iter()
@@ -128,6 +147,11 @@ fn hash_set_retain() {
     hash_set.retain(|x| x % 2 == 0);
     hash_set.retain(|x| x % 2 == 0);
 
+    // Do lint, because we use pattern matching
+    let mut tuples = HashSet::from([(0, 1), (1, 2), (2, 3)]);
+    tuples.retain(|(ref x, ref y)| *x == 0);
+    tuples.retain(|(x, y)| *x == 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
@@ -171,6 +195,11 @@ fn vec_retain() {
     vec.retain(|x| x % 2 == 0);
     vec.retain(|x| x % 2 == 0);
 
+    // Do lint, because we use pattern matching
+    let mut tuples = vec![(0, 1), (1, 2), (2, 3)];
+    tuples.retain(|(ref x, ref y)| *x == 0);
+    tuples.retain(|(x, y)| *x == 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>>();
@@ -246,3 +275,37 @@ fn _msrv_118() {
     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();
 }
+
+fn issue_10393() {
+    // Do lint
+    let mut vec = vec![(0, 1), (1, 2), (2, 3)];
+    vec.retain(|(x, y)| *x == 0);
+
+    // Do lint
+    let mut tuples = vec![(true, -2), (false, 3)];
+    tuples.retain(|(_, n)| *n > 0);
+}
+
+fn issue_11457() {
+    // Do not lint, as we need to modify the closure
+    let mut vals = vec![1, 2, 3, 4];
+    vals = vals.iter().filter(|v| **v != 1).cloned().collect();
+
+    // Do not lint, as we need to modify the closure
+    let mut s = String::from("foobar");
+    s = s.chars().filter(|c| *c != 'o').to_owned().collect();
+}
+
+fn issue_12081() {
+    let mut vec = vec![0, 1, 2];
+
+    // Do lint
+    vec.retain(|&x| x == 0);
+    vec.retain(|&x| x == 0);
+    vec.retain(|&x| x == 0);
+
+    // Do lint
+    vec.retain(|x| *x == 0);
+    vec.retain(|x| *x == 0);
+    vec.retain(|x| *x == 0);
+}
diff --git a/src/tools/clippy/tests/ui/manual_retain.rs b/src/tools/clippy/tests/ui/manual_retain.rs
index d839550f33a..cee641d9d65 100644
--- a/src/tools/clippy/tests/ui/manual_retain.rs
+++ b/src/tools/clippy/tests/ui/manual_retain.rs
@@ -14,6 +14,9 @@ fn main() {
     _msrv_153();
     _msrv_126();
     _msrv_118();
+
+    issue_10393();
+    issue_12081();
 }
 
 fn binary_heap_retain() {
@@ -23,6 +26,11 @@ fn binary_heap_retain() {
     binary_heap = binary_heap.iter().filter(|&x| x % 2 == 0).copied().collect();
     binary_heap = binary_heap.iter().filter(|&x| x % 2 == 0).cloned().collect();
 
+    // Do lint, because we use pattern matching
+    let mut tuples = BinaryHeap::from([(0, 1), (1, 2), (2, 3)]);
+    tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect();
+    tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect();
+
     // Do not lint, because type conversion is performed
     binary_heap = binary_heap
         .into_iter()
@@ -58,6 +66,9 @@ fn btree_map_retain() {
         .filter(|(k, v)| (k % 2 == 0) && (v % 2 == 0))
         .collect();
 
+    // Do not lint, because the parameters are not matched in tuple pattern
+    btree_map = btree_map.into_iter().filter(|t| t.0 % 2 == 0).collect();
+
     // Do not lint.
     btree_map = btree_map
         .into_iter()
@@ -79,6 +90,11 @@ fn btree_set_retain() {
     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 lint, because we use pattern matching
+    let mut tuples = BTreeSet::from([(0, 1), (1, 2), (2, 3)]);
+    tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect();
+    tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect();
+
     // Do not lint, because type conversion is performed
     btree_set = btree_set
         .iter()
@@ -114,6 +130,9 @@ fn hash_map_retain() {
         .filter(|(k, v)| (k % 2 == 0) && (v % 2 == 0))
         .collect();
 
+    // Do not lint, because the parameters are not matched in tuple pattern
+    hash_map = hash_map.into_iter().filter(|t| t.0 % 2 == 0).collect();
+
     // Do not lint.
     hash_map = hash_map
         .into_iter()
@@ -134,6 +153,11 @@ fn hash_set_retain() {
     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 lint, because we use pattern matching
+    let mut tuples = HashSet::from([(0, 1), (1, 2), (2, 3)]);
+    tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect();
+    tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().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
@@ -177,6 +201,11 @@ fn vec_retain() {
     vec = vec.iter().filter(|&x| x % 2 == 0).cloned().collect();
     vec = vec.into_iter().filter(|x| x % 2 == 0).collect();
 
+    // Do lint, because we use pattern matching
+    let mut tuples = vec![(0, 1), (1, 2), (2, 3)];
+    tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect();
+    tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().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>>();
@@ -252,3 +281,37 @@ fn _msrv_118() {
     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();
 }
+
+fn issue_10393() {
+    // Do lint
+    let mut vec = vec![(0, 1), (1, 2), (2, 3)];
+    vec = vec.into_iter().filter(|(x, y)| *x == 0).collect();
+
+    // Do lint
+    let mut tuples = vec![(true, -2), (false, 3)];
+    tuples = tuples.into_iter().filter(|(_, n)| *n > 0).collect();
+}
+
+fn issue_11457() {
+    // Do not lint, as we need to modify the closure
+    let mut vals = vec![1, 2, 3, 4];
+    vals = vals.iter().filter(|v| **v != 1).cloned().collect();
+
+    // Do not lint, as we need to modify the closure
+    let mut s = String::from("foobar");
+    s = s.chars().filter(|c| *c != 'o').to_owned().collect();
+}
+
+fn issue_12081() {
+    let mut vec = vec![0, 1, 2];
+
+    // Do lint
+    vec = vec.iter().filter(|&&x| x == 0).copied().collect();
+    vec = vec.iter().filter(|&&x| x == 0).cloned().collect();
+    vec = vec.into_iter().filter(|&x| x == 0).collect();
+
+    // Do lint
+    vec = vec.iter().filter(|&x| *x == 0).copied().collect();
+    vec = vec.iter().filter(|&x| *x == 0).cloned().collect();
+    vec = vec.into_iter().filter(|x| *x == 0).collect();
+}
diff --git a/src/tools/clippy/tests/ui/manual_retain.stderr b/src/tools/clippy/tests/ui/manual_retain.stderr
index 0c5b1383b6a..2c872f3b430 100644
--- a/src/tools/clippy/tests/ui/manual_retain.stderr
+++ b/src/tools/clippy/tests/ui/manual_retain.stderr
@@ -1,5 +1,5 @@
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:22:5
+  --> $DIR/manual_retain.rs:25:5
    |
 LL |     binary_heap = binary_heap.into_iter().filter(|x| x % 2 == 0).collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `binary_heap.retain(|x| x % 2 == 0)`
@@ -8,31 +8,43 @@ LL |     binary_heap = binary_heap.into_iter().filter(|x| x % 2 == 0).collect();
    = help: to override `-D warnings` add `#[allow(clippy::manual_retain)]`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:23:5
+  --> $DIR/manual_retain.rs:26:5
    |
 LL |     binary_heap = binary_heap.iter().filter(|&x| x % 2 == 0).copied().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `binary_heap.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:24:5
+  --> $DIR/manual_retain.rs:27:5
    |
 LL |     binary_heap = binary_heap.iter().filter(|&x| x % 2 == 0).cloned().collect();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `binary_heap.retain(|x| x % 2 == 0)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:54:5
+  --> $DIR/manual_retain.rs:31:5
+   |
+LL |     tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:32:5
+   |
+LL |     tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:62: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)`
 
 error: this expression can be written more simply using `.retain()`
-  --> $DIR/manual_retain.rs:55:5
+  --> $DIR/manual_retain.rs:63: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:56:5
+  --> $DIR/manual_retain.rs:64:5
    |
 LL | /     btree_map = btree_map
 LL | |         .into_iter()
@@ -41,37 +53,49 @@ 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:78:5
+  --> $DIR/manual_retain.rs:89: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:79:5
+  --> $DIR/manual_retain.rs:90: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:80:5
+  --> $DIR/manual_retain.rs:91: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:110:5
+  --> $DIR/manual_retain.rs:95:5
+   |
+LL |     tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:96:5
+   |
+LL |     tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:126: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:111:5
+  --> $DIR/manual_retain.rs:127: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:112:5
+  --> $DIR/manual_retain.rs:128:5
    |
 LL | /     hash_map = hash_map
 LL | |         .into_iter()
@@ -80,64 +104,136 @@ 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:133:5
+  --> $DIR/manual_retain.rs:152: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:134:5
+  --> $DIR/manual_retain.rs:153: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:135:5
+  --> $DIR/manual_retain.rs:154: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:164:5
+  --> $DIR/manual_retain.rs:158:5
+   |
+LL |     tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:159:5
+   |
+LL |     tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:188: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:176:5
+  --> $DIR/manual_retain.rs:200: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:177:5
+  --> $DIR/manual_retain.rs:201: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:178:5
+  --> $DIR/manual_retain.rs:202: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:200:5
+  --> $DIR/manual_retain.rs:206:5
+   |
+LL |     tuples = tuples.iter().filter(|(ref x, ref y)| *x == 0).copied().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(ref x, ref y)| *x == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:207:5
+   |
+LL |     tuples = tuples.iter().filter(|(x, y)| *x == 0).copied().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(x, y)| *x == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:229: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:201:5
+  --> $DIR/manual_retain.rs:230: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:202:5
+  --> $DIR/manual_retain.rs:231: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 22 previous errors
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:288:5
+   |
+LL |     vec = vec.into_iter().filter(|(x, y)| *x == 0).collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|(x, y)| *x == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:292:5
+   |
+LL |     tuples = tuples.into_iter().filter(|(_, n)| *n > 0).collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `tuples.retain(|(_, n)| *n > 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:309:5
+   |
+LL |     vec = vec.iter().filter(|&&x| x == 0).copied().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:310:5
+   |
+LL |     vec = vec.iter().filter(|&&x| x == 0).cloned().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:311:5
+   |
+LL |     vec = vec.into_iter().filter(|&x| x == 0).collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|&x| x == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:314:5
+   |
+LL |     vec = vec.iter().filter(|&x| *x == 0).copied().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:315:5
+   |
+LL |     vec = vec.iter().filter(|&x| *x == 0).cloned().collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)`
+
+error: this expression can be written more simply using `.retain()`
+  --> $DIR/manual_retain.rs:316:5
+   |
+LL |     vec = vec.into_iter().filter(|x| *x == 0).collect();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider calling `.retain()` instead: `vec.retain(|x| *x == 0)`
+
+error: aborting due to 38 previous errors
 
diff --git a/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs
index 4dbed24026c..c427b8580e1 100644
--- a/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs
+++ b/src/tools/clippy/tests/ui/modulo_arithmetic_integral.rs
@@ -114,4 +114,12 @@ fn main() {
     a_usize % b_usize;
     let mut a_usize: usize = 1;
     a_usize %= 2;
+
+    // No lint when comparing to zero
+    let a = -1;
+    let mut b = 2;
+    let c = a % b == 0;
+    let c = 0 == a % b;
+    let c = a % b != 0;
+    let c = 0 != a % b;
 }
diff --git a/src/tools/clippy/tests/ui/needless_borrow.fixed b/src/tools/clippy/tests/ui/needless_borrow.fixed
index ff1e2dc8875..23e8bf8a468 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.fixed
+++ b/src/tools/clippy/tests/ui/needless_borrow.fixed
@@ -131,6 +131,9 @@ fn main() {
             0
         }
     }
+
+    // issue #11786
+    let x: (&str,) = ("",);
 }
 
 #[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 597021539ac..27771a8f15b 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.rs
+++ b/src/tools/clippy/tests/ui/needless_borrow.rs
@@ -131,6 +131,9 @@ fn main() {
             0
         }
     }
+
+    // issue #11786
+    let x: (&str,) = (&"",);
 }
 
 #[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 44552ee6abe..a21ed8382c1 100644
--- a/src/tools/clippy/tests/ui/needless_borrow.stderr
+++ b/src/tools/clippy/tests/ui/needless_borrow.stderr
@@ -121,41 +121,47 @@ error: this expression creates a reference which is immediately dereferenced by
 LL |     (&&5).foo();
    |     ^^^^^ help: change this to: `(&5)`
 
+error: this expression creates a reference which is immediately dereferenced by the compiler
+  --> $DIR/needless_borrow.rs:136:23
+   |
+LL |     let x: (&str,) = (&"",);
+   |                       ^^^ help: change this to: `""`
+
 error: this expression borrows a value the compiler would automatically borrow
-  --> $DIR/needless_borrow.rs:175:13
+  --> $DIR/needless_borrow.rs:178:13
    |
 LL |             (&self.f)()
    |             ^^^^^^^^^ help: change this to: `(self.f)`
 
 error: this expression borrows a value the compiler would automatically borrow
-  --> $DIR/needless_borrow.rs:184:13
+  --> $DIR/needless_borrow.rs:187:13
    |
 LL |             (&mut self.f)()
    |             ^^^^^^^^^^^^^ help: change this to: `(self.f)`
 
 error: this expression borrows a value the compiler would automatically borrow
-  --> $DIR/needless_borrow.rs:221:22
+  --> $DIR/needless_borrow.rs:224:22
    |
 LL |         let _ = &mut (&mut { x.u }).x;
    |                      ^^^^^^^^^^^^^^ help: change this to: `{ x.u }`
 
 error: this expression borrows a value the compiler would automatically borrow
-  --> $DIR/needless_borrow.rs:228:22
+  --> $DIR/needless_borrow.rs:231:22
    |
 LL |         let _ = &mut (&mut { x.u }).x;
    |                      ^^^^^^^^^^^^^^ help: change this to: `{ x.u }`
 
 error: this expression borrows a value the compiler would automatically borrow
-  --> $DIR/needless_borrow.rs:232:22
+  --> $DIR/needless_borrow.rs:235:22
    |
 LL |         let _ = &mut (&mut x.u).x;
    |                      ^^^^^^^^^^ help: change this to: `x.u`
 
 error: this expression borrows a value the compiler would automatically borrow
-  --> $DIR/needless_borrow.rs:233:22
+  --> $DIR/needless_borrow.rs:236:22
    |
 LL |         let _ = &mut (&mut { x.u }).x;
    |                      ^^^^^^^^^^^^^^ help: change this to: `{ x.u }`
 
-error: aborting due to 26 previous errors
+error: aborting due to 27 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_return_with_question_mark.fixed b/src/tools/clippy/tests/ui/needless_return_with_question_mark.fixed
index 0147c73a94b..9b7da852663 100644
--- a/src/tools/clippy/tests/ui/needless_return_with_question_mark.fixed
+++ b/src/tools/clippy/tests/ui/needless_return_with_question_mark.fixed
@@ -77,3 +77,53 @@ fn issue11616() -> Result<(), ()> {
     };
     Ok(())
 }
+
+fn issue11982() {
+    mod bar {
+        pub struct Error;
+        pub fn foo(_: bool) -> Result<(), Error> {
+            Ok(())
+        }
+    }
+
+    pub struct Error;
+
+    impl From<bar::Error> for Error {
+        fn from(_: bar::Error) -> Self {
+            Error
+        }
+    }
+
+    fn foo(ok: bool) -> Result<(), Error> {
+        if !ok {
+            return bar::foo(ok).map(|_| Ok::<(), Error>(()))?;
+        };
+        Ok(())
+    }
+}
+
+fn issue11982_no_conversion() {
+    mod bar {
+        pub struct Error;
+        pub fn foo(_: bool) -> Result<(), Error> {
+            Ok(())
+        }
+    }
+
+    fn foo(ok: bool) -> Result<(), bar::Error> {
+        if !ok {
+            return bar::foo(ok).map(|_| Ok::<(), bar::Error>(()))?;
+        };
+        Ok(())
+    }
+}
+
+fn general_return() {
+    fn foo(ok: bool) -> Result<(), ()> {
+        let bar = Result::Ok(Result::<(), ()>::Ok(()));
+        if !ok {
+            return bar?;
+        };
+        Ok(())
+    }
+}
diff --git a/src/tools/clippy/tests/ui/needless_return_with_question_mark.rs b/src/tools/clippy/tests/ui/needless_return_with_question_mark.rs
index 66e1f438f8c..68e76d2b640 100644
--- a/src/tools/clippy/tests/ui/needless_return_with_question_mark.rs
+++ b/src/tools/clippy/tests/ui/needless_return_with_question_mark.rs
@@ -77,3 +77,53 @@ fn issue11616() -> Result<(), ()> {
     };
     Ok(())
 }
+
+fn issue11982() {
+    mod bar {
+        pub struct Error;
+        pub fn foo(_: bool) -> Result<(), Error> {
+            Ok(())
+        }
+    }
+
+    pub struct Error;
+
+    impl From<bar::Error> for Error {
+        fn from(_: bar::Error) -> Self {
+            Error
+        }
+    }
+
+    fn foo(ok: bool) -> Result<(), Error> {
+        if !ok {
+            return bar::foo(ok).map(|_| Ok::<(), Error>(()))?;
+        };
+        Ok(())
+    }
+}
+
+fn issue11982_no_conversion() {
+    mod bar {
+        pub struct Error;
+        pub fn foo(_: bool) -> Result<(), Error> {
+            Ok(())
+        }
+    }
+
+    fn foo(ok: bool) -> Result<(), bar::Error> {
+        if !ok {
+            return bar::foo(ok).map(|_| Ok::<(), bar::Error>(()))?;
+        };
+        Ok(())
+    }
+}
+
+fn general_return() {
+    fn foo(ok: bool) -> Result<(), ()> {
+        let bar = Result::Ok(Result::<(), ()>::Ok(()));
+        if !ok {
+            return bar?;
+        };
+        Ok(())
+    }
+}
diff --git a/src/tools/clippy/tests/ui/never_loop.rs b/src/tools/clippy/tests/ui/never_loop.rs
index c67a6d4494e..92f173d9db4 100644
--- a/src/tools/clippy/tests/ui/never_loop.rs
+++ b/src/tools/clippy/tests/ui/never_loop.rs
@@ -1,4 +1,4 @@
-#![feature(inline_const)]
+#![feature(inline_const, try_blocks)]
 #![allow(
     clippy::eq_op,
     clippy::single_match,
@@ -400,6 +400,15 @@ pub fn test32() {
     }
 }
 
+pub fn issue12205() -> Option<()> {
+    loop {
+        let _: Option<_> = try {
+            None?;
+            return Some(());
+        };
+    }
+}
+
 fn main() {
     test1();
     test2();
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.rs b/src/tools/clippy/tests/ui/nonminimal_bool.rs
index 4d48ef14d31..f7c3df7066f 100644
--- a/src/tools/clippy/tests/ui/nonminimal_bool.rs
+++ b/src/tools/clippy/tests/ui/nonminimal_bool.rs
@@ -145,3 +145,14 @@ fn issue10836() {
     // Should not lint
     let _: bool = !!Foo(true);
 }
+
+fn issue11932() {
+    let x: i32 = unimplemented!();
+
+    #[allow(clippy::nonminimal_bool)]
+    let _ = x % 2 == 0 || {
+        // Should not lint
+        assert!(x > 0);
+        x % 3 == 0
+    };
+}
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
index f272d8359a3..ce5c7f2600b 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.fixed
@@ -102,3 +102,12 @@ mod issue11707 {
 fn avoid_double_parens() {
     std::convert::identity(13_i32 + 36_i32).leading_zeros();
 }
+
+fn fp_11274() {
+    macro_rules! m {
+        ($closure:expr) => {
+            $closure(1)
+        };
+    }
+    m!(|x| println!("{x}"));
+}
diff --git a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
index f45db8c9cff..ac09390e6ea 100644
--- a/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
+++ b/src/tools/clippy/tests/ui/redundant_closure_call_fixable.rs
@@ -102,3 +102,12 @@ mod issue11707 {
 fn avoid_double_parens() {
     std::convert::identity((|| 13_i32 + 36_i32)()).leading_zeros();
 }
+
+fn fp_11274() {
+    macro_rules! m {
+        ($closure:expr) => {
+            $closure(1)
+        };
+    }
+    m!(|x| println!("{x}"));
+}
diff --git a/src/tools/clippy/tests/ui/redundant_locals.rs b/src/tools/clippy/tests/ui/redundant_locals.rs
index 182d067a5e9..f6909828aa9 100644
--- a/src/tools/clippy/tests/ui/redundant_locals.rs
+++ b/src/tools/clippy/tests/ui/redundant_locals.rs
@@ -1,6 +1,7 @@
 //@aux-build:proc_macros.rs
 #![allow(unused, clippy::no_effect, clippy::needless_pass_by_ref_mut)]
 #![warn(clippy::redundant_locals)]
+#![feature(async_closure, coroutines)]
 
 extern crate proc_macros;
 use proc_macros::{external, with_span};
@@ -163,3 +164,48 @@ fn drop_compose() {
     let b = ComposeDrop { d: WithDrop(1) };
     let a = a;
 }
+
+fn issue12225() {
+    fn assert_static<T: 'static>(_: T) {}
+
+    let v1 = String::new();
+    let v2 = String::new();
+    let v3 = String::new();
+    let v4 = String::new();
+    let v5 = String::new();
+    let v6 = String::new();
+
+    assert_static(|| {
+        let v1 = v1;
+        dbg!(&v1);
+    });
+    assert_static(async {
+        let v2 = v2;
+        dbg!(&v2);
+    });
+    assert_static(|| async {
+        let v3 = v3;
+        dbg!(&v3);
+    });
+    assert_static(async || {
+        let v4 = v4;
+        dbg!(&v4);
+    });
+    assert_static(static || {
+        let v5 = v5;
+        yield;
+    });
+    assert_static(|| {
+        let v6 = v6;
+        yield;
+    });
+
+    fn foo(a: &str, b: &str) {}
+
+    let do_not_move = String::new();
+    let things_to_move = vec!["a".to_string(), "b".to_string()];
+    let futures = things_to_move.into_iter().map(|move_me| async {
+        let move_me = move_me;
+        foo(&do_not_move, &move_me)
+    });
+}
diff --git a/src/tools/clippy/tests/ui/redundant_locals.stderr b/src/tools/clippy/tests/ui/redundant_locals.stderr
index 30ab4aa2ea9..610d587ddad 100644
--- a/src/tools/clippy/tests/ui/redundant_locals.stderr
+++ b/src/tools/clippy/tests/ui/redundant_locals.stderr
@@ -1,11 +1,11 @@
 error: redundant redefinition of a binding `x`
-  --> $DIR/redundant_locals.rs:12:5
+  --> $DIR/redundant_locals.rs:13:5
    |
 LL |     let x = x;
    |     ^^^^^^^^^^
    |
 help: `x` is initially defined here
-  --> $DIR/redundant_locals.rs:11:9
+  --> $DIR/redundant_locals.rs:12:9
    |
 LL |     let x = 1;
    |         ^
@@ -13,157 +13,157 @@ LL |     let x = 1;
    = help: to override `-D warnings` add `#[allow(clippy::redundant_locals)]`
 
 error: redundant redefinition of a binding `x`
-  --> $DIR/redundant_locals.rs:17:5
+  --> $DIR/redundant_locals.rs:18:5
    |
 LL |     let mut x = x;
    |     ^^^^^^^^^^^^^^
    |
 help: `x` is initially defined here
-  --> $DIR/redundant_locals.rs:16:9
+  --> $DIR/redundant_locals.rs:17:9
    |
 LL |     let mut x = 1;
    |         ^^^^^
 
 error: redundant redefinition of a binding `x`
-  --> $DIR/redundant_locals.rs:47:5
+  --> $DIR/redundant_locals.rs:48:5
    |
 LL |     let x = x;
    |     ^^^^^^^^^^
    |
 help: `x` is initially defined here
-  --> $DIR/redundant_locals.rs:46:14
+  --> $DIR/redundant_locals.rs:47:14
    |
 LL | fn parameter(x: i32) {
    |              ^
 
 error: redundant redefinition of a binding `x`
-  --> $DIR/redundant_locals.rs:52:5
+  --> $DIR/redundant_locals.rs:53:5
    |
 LL |     let x = x;
    |     ^^^^^^^^^^
    |
 help: `x` is initially defined here
-  --> $DIR/redundant_locals.rs:51:9
+  --> $DIR/redundant_locals.rs:52:9
    |
 LL |     let x = 1;
    |         ^
 
 error: redundant redefinition of a binding `x`
-  --> $DIR/redundant_locals.rs:53:5
+  --> $DIR/redundant_locals.rs:54:5
    |
 LL |     let x = x;
    |     ^^^^^^^^^^
    |
 help: `x` is initially defined here
-  --> $DIR/redundant_locals.rs:52:9
+  --> $DIR/redundant_locals.rs:53:9
    |
 LL |     let x = x;
    |         ^
 
 error: redundant redefinition of a binding `x`
-  --> $DIR/redundant_locals.rs:54:5
+  --> $DIR/redundant_locals.rs:55:5
    |
 LL |     let x = x;
    |     ^^^^^^^^^^
    |
 help: `x` is initially defined here
-  --> $DIR/redundant_locals.rs:53:9
+  --> $DIR/redundant_locals.rs:54:9
    |
 LL |     let x = x;
    |         ^
 
 error: redundant redefinition of a binding `x`
-  --> $DIR/redundant_locals.rs:55:5
+  --> $DIR/redundant_locals.rs:56:5
    |
 LL |     let x = x;
    |     ^^^^^^^^^^
    |
 help: `x` is initially defined here
-  --> $DIR/redundant_locals.rs:54:9
+  --> $DIR/redundant_locals.rs:55:9
    |
 LL |     let x = x;
    |         ^
 
 error: redundant redefinition of a binding `a`
-  --> $DIR/redundant_locals.rs:61:5
+  --> $DIR/redundant_locals.rs:62:5
    |
 LL |     let a = a;
    |     ^^^^^^^^^^
    |
 help: `a` is initially defined here
-  --> $DIR/redundant_locals.rs:59:9
+  --> $DIR/redundant_locals.rs:60:9
    |
 LL |     let a = 1;
    |         ^
 
 error: redundant redefinition of a binding `b`
-  --> $DIR/redundant_locals.rs:62:5
+  --> $DIR/redundant_locals.rs:63:5
    |
 LL |     let b = b;
    |     ^^^^^^^^^^
    |
 help: `b` is initially defined here
-  --> $DIR/redundant_locals.rs:60:9
+  --> $DIR/redundant_locals.rs:61:9
    |
 LL |     let b = 2;
    |         ^
 
 error: redundant redefinition of a binding `x`
-  --> $DIR/redundant_locals.rs:68:9
+  --> $DIR/redundant_locals.rs:69:9
    |
 LL |         let x = x;
    |         ^^^^^^^^^^
    |
 help: `x` is initially defined here
-  --> $DIR/redundant_locals.rs:67:13
+  --> $DIR/redundant_locals.rs:68:13
    |
 LL |         let x = 1;
    |             ^
 
 error: redundant redefinition of a binding `x`
-  --> $DIR/redundant_locals.rs:75:9
+  --> $DIR/redundant_locals.rs:76:9
    |
 LL |         let x = x;
    |         ^^^^^^^^^^
    |
 help: `x` is initially defined here
-  --> $DIR/redundant_locals.rs:74:13
+  --> $DIR/redundant_locals.rs:75:13
    |
 LL |         let x = 1;
    |             ^
 
 error: redundant redefinition of a binding `x`
-  --> $DIR/redundant_locals.rs:78:9
+  --> $DIR/redundant_locals.rs:79:9
    |
 LL |         let x = x;
    |         ^^^^^^^^^^
    |
 help: `x` is initially defined here
-  --> $DIR/redundant_locals.rs:77:6
+  --> $DIR/redundant_locals.rs:78:6
    |
 LL |     |x: i32| {
    |      ^
 
 error: redundant redefinition of a binding `x`
-  --> $DIR/redundant_locals.rs:97:9
+  --> $DIR/redundant_locals.rs:98:9
    |
 LL |         let x = x;
    |         ^^^^^^^^^^
    |
 help: `x` is initially defined here
-  --> $DIR/redundant_locals.rs:94:9
+  --> $DIR/redundant_locals.rs:95:9
    |
 LL |     let x = 1;
    |         ^
 
 error: redundant redefinition of a binding `a`
-  --> $DIR/redundant_locals.rs:152:5
+  --> $DIR/redundant_locals.rs:153:5
    |
 LL |     let a = a;
    |     ^^^^^^^^^^
    |
 help: `a` is initially defined here
-  --> $DIR/redundant_locals.rs:150:9
+  --> $DIR/redundant_locals.rs:151:9
    |
 LL |     let a = WithoutDrop(1);
    |         ^
diff --git a/src/tools/clippy/tests/ui/redundant_type_annotations.rs b/src/tools/clippy/tests/ui/redundant_type_annotations.rs
index acf53fea2bb..dc9b073ffba 100644
--- a/src/tools/clippy/tests/ui/redundant_type_annotations.rs
+++ b/src/tools/clippy/tests/ui/redundant_type_annotations.rs
@@ -196,13 +196,18 @@ fn test_simple_types() {
     let _var: &str = "test";
     //~^ ERROR: redundant type annotation
 
-    let _var: &[u8] = b"test";
+    let _var: &[u8; 4] = b"test";
     //~^ ERROR: redundant type annotation
 
     let _var: bool = false;
     //~^ ERROR: redundant type annotation
 }
 
+fn issue12212() {
+    // This should not be linted
+    let _var: &[u8] = b"test";
+}
+
 fn issue11190() {}
 
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/redundant_type_annotations.stderr b/src/tools/clippy/tests/ui/redundant_type_annotations.stderr
index d1f26f1832e..48df465ad49 100644
--- a/src/tools/clippy/tests/ui/redundant_type_annotations.stderr
+++ b/src/tools/clippy/tests/ui/redundant_type_annotations.stderr
@@ -94,8 +94,8 @@ LL |     let _var: &str = "test";
 error: redundant type annotation
   --> $DIR/redundant_type_annotations.rs:199:5
    |
-LL |     let _var: &[u8] = b"test";
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL |     let _var: &[u8; 4] = b"test";
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: redundant type annotation
   --> $DIR/redundant_type_annotations.rs:202:5
diff --git a/src/tools/clippy/tests/ui/ref_as_ptr.fixed b/src/tools/clippy/tests/ui/ref_as_ptr.fixed
new file mode 100644
index 00000000000..7a946393f25
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ref_as_ptr.fixed
@@ -0,0 +1,110 @@
+#![warn(clippy::ref_as_ptr)]
+#![allow(clippy::unnecessary_mut_passed)]
+
+fn main() {
+    let _ = std::ptr::from_ref(&1u8);
+    let _ = std::ptr::from_ref::<u32>(&2u32);
+    let _ = std::ptr::from_ref::<f64>(&3.0f64);
+
+    let _ = std::ptr::from_ref(&4) as *const f32;
+    let _ = std::ptr::from_ref::<f32>(&5.0f32) as *const u32;
+
+    let _ = std::ptr::from_ref(&mut 6u8);
+    let _ = std::ptr::from_ref::<u32>(&mut 7u32);
+    let _ = std::ptr::from_ref::<f64>(&mut 8.0f64);
+
+    let _ = std::ptr::from_ref(&mut 9) as *const f32;
+    let _ = std::ptr::from_ref::<f32>(&mut 10.0f32) as *const u32;
+
+    let _ = std::ptr::from_mut(&mut 11u8);
+    let _ = std::ptr::from_mut::<u32>(&mut 12u32);
+    let _ = std::ptr::from_mut::<f64>(&mut 13.0f64);
+
+    let _ = std::ptr::from_mut(&mut 14) as *const f32;
+    let _ = std::ptr::from_mut::<f32>(&mut 15.0f32) as *const u32;
+
+    let _ = std::ptr::from_ref(&1u8);
+    let _ = std::ptr::from_ref::<u32>(&2u32);
+    let _ = std::ptr::from_ref::<f64>(&3.0f64);
+
+    let _ = std::ptr::from_ref(&4) as *const f32;
+    let _ = std::ptr::from_ref::<f32>(&5.0f32) as *const u32;
+
+    let val = 1;
+    let _ = std::ptr::from_ref(&val);
+    let _ = std::ptr::from_ref::<i32>(&val);
+
+    let _ = std::ptr::from_ref(&val) as *const f32;
+    let _ = std::ptr::from_ref::<i32>(&val) as *const f64;
+
+    let mut val: u8 = 2;
+    let _ = std::ptr::from_mut::<u8>(&mut val);
+    let _ = std::ptr::from_mut(&mut val);
+
+    let _ = std::ptr::from_ref::<u8>(&mut val);
+    let _ = std::ptr::from_ref(&mut val);
+
+    let _ = std::ptr::from_ref::<u8>(&mut val) as *const f64;
+    let _: *const Option<u8> = std::ptr::from_ref(&mut val) as *const _;
+
+    let _ = std::ptr::from_ref::<[usize; 7]>(&std::array::from_fn(|i| i * i));
+    let _ = std::ptr::from_ref::<[usize; 8]>(&mut std::array::from_fn(|i| i * i));
+    let _ = std::ptr::from_mut::<[usize; 9]>(&mut std::array::from_fn(|i| i * i));
+}
+
+#[clippy::msrv = "1.75"]
+fn _msrv_1_75() {
+    let val = &42_i32;
+    let mut_val = &mut 42_i32;
+
+    // `std::ptr::from_{ref, mut}` was stabilized in 1.76. Do not lint this
+    let _ = val as *const i32;
+    let _ = mut_val as *mut i32;
+}
+
+#[clippy::msrv = "1.76"]
+fn _msrv_1_76() {
+    let val = &42_i32;
+    let mut_val = &mut 42_i32;
+
+    let _ = std::ptr::from_ref::<i32>(val);
+    let _ = std::ptr::from_mut::<i32>(mut_val);
+}
+
+fn foo(val: &[u8]) {
+    let _ = std::ptr::from_ref(val);
+    let _ = std::ptr::from_ref::<[u8]>(val);
+}
+
+fn bar(val: &mut str) {
+    let _ = std::ptr::from_mut(val);
+    let _ = std::ptr::from_mut::<str>(val);
+}
+
+struct X<'a>(&'a i32);
+
+impl<'a> X<'a> {
+    fn foo(&self) -> *const i64 {
+        std::ptr::from_ref(self.0) as *const _
+    }
+
+    fn bar(&mut self) -> *const i64 {
+        std::ptr::from_ref(self.0) as *const _
+    }
+}
+
+struct Y<'a>(&'a mut i32);
+
+impl<'a> Y<'a> {
+    fn foo(&self) -> *const i64 {
+        std::ptr::from_ref(self.0) as *const _
+    }
+
+    fn bar(&mut self) -> *const i64 {
+        std::ptr::from_ref(self.0) as *const _
+    }
+
+    fn baz(&mut self) -> *const i64 {
+        std::ptr::from_mut(self.0) as *mut _
+    }
+}
diff --git a/src/tools/clippy/tests/ui/ref_as_ptr.rs b/src/tools/clippy/tests/ui/ref_as_ptr.rs
new file mode 100644
index 00000000000..6f745505b46
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ref_as_ptr.rs
@@ -0,0 +1,110 @@
+#![warn(clippy::ref_as_ptr)]
+#![allow(clippy::unnecessary_mut_passed)]
+
+fn main() {
+    let _ = &1u8 as *const _;
+    let _ = &2u32 as *const u32;
+    let _ = &3.0f64 as *const f64;
+
+    let _ = &4 as *const _ as *const f32;
+    let _ = &5.0f32 as *const f32 as *const u32;
+
+    let _ = &mut 6u8 as *const _;
+    let _ = &mut 7u32 as *const u32;
+    let _ = &mut 8.0f64 as *const f64;
+
+    let _ = &mut 9 as *const _ as *const f32;
+    let _ = &mut 10.0f32 as *const f32 as *const u32;
+
+    let _ = &mut 11u8 as *mut _;
+    let _ = &mut 12u32 as *mut u32;
+    let _ = &mut 13.0f64 as *mut f64;
+
+    let _ = &mut 14 as *mut _ as *const f32;
+    let _ = &mut 15.0f32 as *mut f32 as *const u32;
+
+    let _ = &1u8 as *const _;
+    let _ = &2u32 as *const u32;
+    let _ = &3.0f64 as *const f64;
+
+    let _ = &4 as *const _ as *const f32;
+    let _ = &5.0f32 as *const f32 as *const u32;
+
+    let val = 1;
+    let _ = &val as *const _;
+    let _ = &val as *const i32;
+
+    let _ = &val as *const _ as *const f32;
+    let _ = &val as *const i32 as *const f64;
+
+    let mut val: u8 = 2;
+    let _ = &mut val as *mut u8;
+    let _ = &mut val as *mut _;
+
+    let _ = &mut val as *const u8;
+    let _ = &mut val as *const _;
+
+    let _ = &mut val as *const u8 as *const f64;
+    let _: *const Option<u8> = &mut val as *const _ as *const _;
+
+    let _ = &std::array::from_fn(|i| i * i) as *const [usize; 7];
+    let _ = &mut std::array::from_fn(|i| i * i) as *const [usize; 8];
+    let _ = &mut std::array::from_fn(|i| i * i) as *mut [usize; 9];
+}
+
+#[clippy::msrv = "1.75"]
+fn _msrv_1_75() {
+    let val = &42_i32;
+    let mut_val = &mut 42_i32;
+
+    // `std::ptr::from_{ref, mut}` was stabilized in 1.76. Do not lint this
+    let _ = val as *const i32;
+    let _ = mut_val as *mut i32;
+}
+
+#[clippy::msrv = "1.76"]
+fn _msrv_1_76() {
+    let val = &42_i32;
+    let mut_val = &mut 42_i32;
+
+    let _ = val as *const i32;
+    let _ = mut_val as *mut i32;
+}
+
+fn foo(val: &[u8]) {
+    let _ = val as *const _;
+    let _ = val as *const [u8];
+}
+
+fn bar(val: &mut str) {
+    let _ = val as *mut _;
+    let _ = val as *mut str;
+}
+
+struct X<'a>(&'a i32);
+
+impl<'a> X<'a> {
+    fn foo(&self) -> *const i64 {
+        self.0 as *const _ as *const _
+    }
+
+    fn bar(&mut self) -> *const i64 {
+        self.0 as *const _ as *const _
+    }
+}
+
+struct Y<'a>(&'a mut i32);
+
+impl<'a> Y<'a> {
+    fn foo(&self) -> *const i64 {
+        self.0 as *const _ as *const _
+    }
+
+    fn bar(&mut self) -> *const i64 {
+        self.0 as *const _ as *const _
+    }
+
+    fn baz(&mut self) -> *const i64 {
+        self.0 as *mut _ as *mut _
+    }
+}
diff --git a/src/tools/clippy/tests/ui/ref_as_ptr.stderr b/src/tools/clippy/tests/ui/ref_as_ptr.stderr
new file mode 100644
index 00000000000..371d42df528
--- /dev/null
+++ b/src/tools/clippy/tests/ui/ref_as_ptr.stderr
@@ -0,0 +1,269 @@
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:5:13
+   |
+LL |     let _ = &1u8 as *const _;
+   |             ^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&1u8)`
+   |
+   = note: `-D clippy::ref-as-ptr` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::ref_as_ptr)]`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:6:13
+   |
+LL |     let _ = &2u32 as *const u32;
+   |             ^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<u32>(&2u32)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:7:13
+   |
+LL |     let _ = &3.0f64 as *const f64;
+   |             ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<f64>(&3.0f64)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:9:13
+   |
+LL |     let _ = &4 as *const _ as *const f32;
+   |             ^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&4)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:10:13
+   |
+LL |     let _ = &5.0f32 as *const f32 as *const u32;
+   |             ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<f32>(&5.0f32)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:12:13
+   |
+LL |     let _ = &mut 6u8 as *const _;
+   |             ^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&mut 6u8)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:13:13
+   |
+LL |     let _ = &mut 7u32 as *const u32;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<u32>(&mut 7u32)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:14:13
+   |
+LL |     let _ = &mut 8.0f64 as *const f64;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<f64>(&mut 8.0f64)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:16:13
+   |
+LL |     let _ = &mut 9 as *const _ as *const f32;
+   |             ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&mut 9)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:17:13
+   |
+LL |     let _ = &mut 10.0f32 as *const f32 as *const u32;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<f32>(&mut 10.0f32)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:19:13
+   |
+LL |     let _ = &mut 11u8 as *mut _;
+   |             ^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut(&mut 11u8)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:20:13
+   |
+LL |     let _ = &mut 12u32 as *mut u32;
+   |             ^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut::<u32>(&mut 12u32)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:21:13
+   |
+LL |     let _ = &mut 13.0f64 as *mut f64;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut::<f64>(&mut 13.0f64)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:23:13
+   |
+LL |     let _ = &mut 14 as *mut _ as *const f32;
+   |             ^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut(&mut 14)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:24:13
+   |
+LL |     let _ = &mut 15.0f32 as *mut f32 as *const u32;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut::<f32>(&mut 15.0f32)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:26:13
+   |
+LL |     let _ = &1u8 as *const _;
+   |             ^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&1u8)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:27:13
+   |
+LL |     let _ = &2u32 as *const u32;
+   |             ^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<u32>(&2u32)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:28:13
+   |
+LL |     let _ = &3.0f64 as *const f64;
+   |             ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<f64>(&3.0f64)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:30:13
+   |
+LL |     let _ = &4 as *const _ as *const f32;
+   |             ^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&4)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:31:13
+   |
+LL |     let _ = &5.0f32 as *const f32 as *const u32;
+   |             ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<f32>(&5.0f32)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:34:13
+   |
+LL |     let _ = &val as *const _;
+   |             ^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&val)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:35:13
+   |
+LL |     let _ = &val as *const i32;
+   |             ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<i32>(&val)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:37:13
+   |
+LL |     let _ = &val as *const _ as *const f32;
+   |             ^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&val)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:38:13
+   |
+LL |     let _ = &val as *const i32 as *const f64;
+   |             ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<i32>(&val)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:41:13
+   |
+LL |     let _ = &mut val as *mut u8;
+   |             ^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut::<u8>(&mut val)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:42:13
+   |
+LL |     let _ = &mut val as *mut _;
+   |             ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut(&mut val)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:44:13
+   |
+LL |     let _ = &mut val as *const u8;
+   |             ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<u8>(&mut val)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:45:13
+   |
+LL |     let _ = &mut val as *const _;
+   |             ^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&mut val)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:47:13
+   |
+LL |     let _ = &mut val as *const u8 as *const f64;
+   |             ^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<u8>(&mut val)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:48:32
+   |
+LL |     let _: *const Option<u8> = &mut val as *const _ as *const _;
+   |                                ^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(&mut val)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:50:13
+   |
+LL |     let _ = &std::array::from_fn(|i| i * i) as *const [usize; 7];
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<[usize; 7]>(&std::array::from_fn(|i| i * i))`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:51:13
+   |
+LL |     let _ = &mut std::array::from_fn(|i| i * i) as *const [usize; 8];
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<[usize; 8]>(&mut std::array::from_fn(|i| i * i))`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:52:13
+   |
+LL |     let _ = &mut std::array::from_fn(|i| i * i) as *mut [usize; 9];
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut::<[usize; 9]>(&mut std::array::from_fn(|i| i * i))`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:70:13
+   |
+LL |     let _ = val as *const i32;
+   |             ^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<i32>(val)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:71:13
+   |
+LL |     let _ = mut_val as *mut i32;
+   |             ^^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut::<i32>(mut_val)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:75:13
+   |
+LL |     let _ = val as *const _;
+   |             ^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(val)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:76:13
+   |
+LL |     let _ = val as *const [u8];
+   |             ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref::<[u8]>(val)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:80:13
+   |
+LL |     let _ = val as *mut _;
+   |             ^^^^^^^^^^^^^ help: try: `std::ptr::from_mut(val)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:81:13
+   |
+LL |     let _ = val as *mut str;
+   |             ^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut::<str>(val)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:88:9
+   |
+LL |         self.0 as *const _ as *const _
+   |         ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(self.0)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:92:9
+   |
+LL |         self.0 as *const _ as *const _
+   |         ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(self.0)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:100:9
+   |
+LL |         self.0 as *const _ as *const _
+   |         ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(self.0)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:104:9
+   |
+LL |         self.0 as *const _ as *const _
+   |         ^^^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_ref(self.0)`
+
+error: reference as raw pointer
+  --> $DIR/ref_as_ptr.rs:108:9
+   |
+LL |         self.0 as *mut _ as *mut _
+   |         ^^^^^^^^^^^^^^^^ help: try: `std::ptr::from_mut(self.0)`
+
+error: aborting due to 44 previous errors
+
diff --git a/src/tools/clippy/tests/ui/strlen_on_c_strings.fixed b/src/tools/clippy/tests/ui/strlen_on_c_strings.fixed
index 8304e2afd8b..1e7d04ffb9d 100644
--- a/src/tools/clippy/tests/ui/strlen_on_c_strings.fixed
+++ b/src/tools/clippy/tests/ui/strlen_on_c_strings.fixed
@@ -1,5 +1,5 @@
 #![warn(clippy::strlen_on_c_strings)]
-#![allow(dead_code)]
+#![allow(dead_code, clippy::manual_c_str_literals)]
 #![feature(rustc_private)]
 extern crate libc;
 
diff --git a/src/tools/clippy/tests/ui/strlen_on_c_strings.rs b/src/tools/clippy/tests/ui/strlen_on_c_strings.rs
index deba40a9ea5..c3ad03591d4 100644
--- a/src/tools/clippy/tests/ui/strlen_on_c_strings.rs
+++ b/src/tools/clippy/tests/ui/strlen_on_c_strings.rs
@@ -1,5 +1,5 @@
 #![warn(clippy::strlen_on_c_strings)]
-#![allow(dead_code)]
+#![allow(dead_code, clippy::manual_c_str_literals)]
 #![feature(rustc_private)]
 extern crate libc;
 
diff --git a/src/tools/clippy/tests/ui/to_string_trait_impl.rs b/src/tools/clippy/tests/ui/to_string_trait_impl.rs
new file mode 100644
index 00000000000..b0731632d45
--- /dev/null
+++ b/src/tools/clippy/tests/ui/to_string_trait_impl.rs
@@ -0,0 +1,31 @@
+#![warn(clippy::to_string_trait_impl)]
+
+use std::fmt::{self, Display};
+
+struct Point {
+    x: usize,
+    y: usize,
+}
+
+impl ToString for Point {
+    fn to_string(&self) -> String {
+        format!("({}, {})", self.x, self.y)
+    }
+}
+
+struct Foo;
+
+impl Display for Foo {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "Foo")
+    }
+}
+
+struct Bar;
+
+impl Bar {
+    #[allow(clippy::inherent_to_string)]
+    fn to_string(&self) -> String {
+        String::from("Bar")
+    }
+}
diff --git a/src/tools/clippy/tests/ui/to_string_trait_impl.stderr b/src/tools/clippy/tests/ui/to_string_trait_impl.stderr
new file mode 100644
index 00000000000..55fa9f12c0e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/to_string_trait_impl.stderr
@@ -0,0 +1,16 @@
+error: direct implementation of `ToString`
+  --> $DIR/to_string_trait_impl.rs:10:1
+   |
+LL | / impl ToString for Point {
+LL | |     fn to_string(&self) -> String {
+LL | |         format!("({}, {})", self.x, self.y)
+LL | |     }
+LL | | }
+   | |_^
+   |
+   = help: prefer implementing `Display` instead
+   = note: `-D clippy::to-string-trait-impl` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::to_string_trait_impl)]`
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui/unconditional_recursion.rs b/src/tools/clippy/tests/ui/unconditional_recursion.rs
index 7b898a6e0e7..6ad3bde51cd 100644
--- a/src/tools/clippy/tests/ui/unconditional_recursion.rs
+++ b/src/tools/clippy/tests/ui/unconditional_recursion.rs
@@ -206,6 +206,7 @@ impl PartialEq for S8 {
 
 struct S9;
 
+#[allow(clippy::to_string_trait_impl)]
 impl std::string::ToString for S9 {
     fn to_string(&self) -> String {
         //~^ ERROR: function cannot return without recursing
@@ -215,6 +216,7 @@ impl std::string::ToString for S9 {
 
 struct S10;
 
+#[allow(clippy::to_string_trait_impl)]
 impl std::string::ToString for S10 {
     fn to_string(&self) -> String {
         //~^ ERROR: function cannot return without recursing
@@ -225,6 +227,7 @@ impl std::string::ToString for S10 {
 
 struct S11;
 
+#[allow(clippy::to_string_trait_impl)]
 impl std::string::ToString for S11 {
     fn to_string(&self) -> String {
         //~^ ERROR: function cannot return without recursing
@@ -288,4 +291,63 @@ impl PartialEq for S15<'_> {
     }
 }
 
+mod issue12154 {
+    struct MyBox<T>(T);
+
+    impl<T> std::ops::Deref for MyBox<T> {
+        type Target = T;
+        fn deref(&self) -> &T {
+            &self.0
+        }
+    }
+
+    impl<T: PartialEq> PartialEq for MyBox<T> {
+        fn eq(&self, other: &Self) -> bool {
+            (**self).eq(&**other)
+        }
+    }
+
+    // Not necessarily related to the issue but another FP from the http crate that was fixed with it:
+    // https://docs.rs/http/latest/src/http/header/name.rs.html#1424
+    // We used to simply peel refs from the LHS and RHS, so we couldn't differentiate
+    // between `PartialEq<T> for &T` and `PartialEq<&T> for T` impls.
+    #[derive(PartialEq)]
+    struct HeaderName;
+    impl<'a> PartialEq<&'a HeaderName> for HeaderName {
+        fn eq(&self, other: &&'a HeaderName) -> bool {
+            *self == **other
+        }
+    }
+
+    impl<'a> PartialEq<HeaderName> for &'a HeaderName {
+        fn eq(&self, other: &HeaderName) -> bool {
+            *other == *self
+        }
+    }
+
+    // Issue #12181 but also fixed by the same PR
+    struct Foo;
+
+    impl Foo {
+        fn as_str(&self) -> &str {
+            "Foo"
+        }
+    }
+
+    impl PartialEq for Foo {
+        fn eq(&self, other: &Self) -> bool {
+            self.as_str().eq(other.as_str())
+        }
+    }
+
+    impl<T> PartialEq<T> for Foo
+    where
+        for<'a> &'a str: PartialEq<T>,
+    {
+        fn eq(&self, other: &T) -> bool {
+            (&self.as_str()).eq(other)
+        }
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/unconditional_recursion.stderr b/src/tools/clippy/tests/ui/unconditional_recursion.stderr
index 094b80d4586..93a5eac91d8 100644
--- a/src/tools/clippy/tests/ui/unconditional_recursion.stderr
+++ b/src/tools/clippy/tests/ui/unconditional_recursion.stderr
@@ -23,7 +23,7 @@ LL |         self.eq(other)
    = help: a `loop` may express intention better if this is on purpose
 
 error: function cannot return without recursing
-  --> $DIR/unconditional_recursion.rs:210:5
+  --> $DIR/unconditional_recursion.rs:211:5
    |
 LL |     fn to_string(&self) -> String {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
@@ -34,7 +34,7 @@ LL |         self.to_string()
    = help: a `loop` may express intention better if this is on purpose
 
 error: function cannot return without recursing
-  --> $DIR/unconditional_recursion.rs:219:5
+  --> $DIR/unconditional_recursion.rs:221:5
    |
 LL |     fn to_string(&self) -> String {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
@@ -45,7 +45,7 @@ LL |         x.to_string()
    = help: a `loop` may express intention better if this is on purpose
 
 error: function cannot return without recursing
-  --> $DIR/unconditional_recursion.rs:229:5
+  --> $DIR/unconditional_recursion.rs:232:5
    |
 LL |     fn to_string(&self) -> String {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot return without recursing
@@ -326,7 +326,7 @@ LL |         mine == theirs
    |         ^^^^^^^^^^^^^^
 
 error: function cannot return without recursing
-  --> $DIR/unconditional_recursion.rs:244:5
+  --> $DIR/unconditional_recursion.rs:247:5
    |
 LL | /     fn new() -> Self {
 LL | |
@@ -335,13 +335,13 @@ LL | |     }
    | |_____^
    |
 note: recursive call site
-  --> $DIR/unconditional_recursion.rs:246:9
+  --> $DIR/unconditional_recursion.rs:249:9
    |
 LL |         Self::default()
    |         ^^^^^^^^^^^^^^^
 
 error: function cannot return without recursing
-  --> $DIR/unconditional_recursion.rs:283:5
+  --> $DIR/unconditional_recursion.rs:286:5
    |
 LL | /     fn eq(&self, other: &Self) -> bool {
 LL | |
@@ -352,7 +352,7 @@ LL | |     }
    | |_____^
    |
 note: recursive call site
-  --> $DIR/unconditional_recursion.rs:287:9
+  --> $DIR/unconditional_recursion.rs:290:9
    |
 LL |         mine.eq(theirs)
    |         ^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/unnecessary_fold.fixed b/src/tools/clippy/tests/ui/unnecessary_fold.fixed
index c884d26eb61..c5bc11b55ab 100644
--- a/src/tools/clippy/tests/ui/unnecessary_fold.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_fold.fixed
@@ -1,9 +1,15 @@
 #![allow(dead_code)]
 
+fn is_any(acc: bool, x: usize) -> bool {
+    acc || x > 2
+}
+
 /// Calls which should trigger the `UNNECESSARY_FOLD` lint
 fn unnecessary_fold() {
     // Can be replaced by .any
     let _ = (0..3).any(|x| x > 2);
+    // Can be replaced by .any (checking suggestion)
+    let _ = (0..3).fold(false, is_any);
     // Can be replaced by .all
     let _ = (0..3).all(|x| x > 2);
     // Can be replaced by .sum
diff --git a/src/tools/clippy/tests/ui/unnecessary_fold.rs b/src/tools/clippy/tests/ui/unnecessary_fold.rs
index 2e6d6ba52eb..3a5136eeeae 100644
--- a/src/tools/clippy/tests/ui/unnecessary_fold.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_fold.rs
@@ -1,9 +1,15 @@
 #![allow(dead_code)]
 
+fn is_any(acc: bool, x: usize) -> bool {
+    acc || x > 2
+}
+
 /// Calls which should trigger the `UNNECESSARY_FOLD` lint
 fn unnecessary_fold() {
     // Can be replaced by .any
     let _ = (0..3).fold(false, |acc, x| acc || x > 2);
+    // Can be replaced by .any (checking suggestion)
+    let _ = (0..3).fold(false, |acc, x| is_any(acc, x));
     // Can be replaced by .all
     let _ = (0..3).fold(true, |acc, x| acc && x > 2);
     // Can be replaced by .sum
diff --git a/src/tools/clippy/tests/ui/unnecessary_fold.stderr b/src/tools/clippy/tests/ui/unnecessary_fold.stderr
index f0d03963842..123d4a3be75 100644
--- a/src/tools/clippy/tests/ui/unnecessary_fold.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_fold.stderr
@@ -1,5 +1,5 @@
 error: this `.fold` can be written more succinctly using another method
-  --> $DIR/unnecessary_fold.rs:6:20
+  --> $DIR/unnecessary_fold.rs:10:20
    |
 LL |     let _ = (0..3).fold(false, |acc, x| acc || x > 2);
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)`
@@ -7,89 +7,98 @@ LL |     let _ = (0..3).fold(false, |acc, x| acc || x > 2);
    = note: `-D clippy::unnecessary-fold` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_fold)]`
 
+error: redundant closure
+  --> $DIR/unnecessary_fold.rs:12:32
+   |
+LL |     let _ = (0..3).fold(false, |acc, x| is_any(acc, x));
+   |                                ^^^^^^^^^^^^^^^^^^^^^^^ help: replace the closure with the function itself: `is_any`
+   |
+   = note: `-D clippy::redundant-closure` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]`
+
 error: this `.fold` can be written more succinctly using another method
-  --> $DIR/unnecessary_fold.rs:8:20
+  --> $DIR/unnecessary_fold.rs:14:20
    |
 LL |     let _ = (0..3).fold(true, |acc, x| acc && x > 2);
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `all(|x| x > 2)`
 
 error: this `.fold` can be written more succinctly using another method
-  --> $DIR/unnecessary_fold.rs:10:25
+  --> $DIR/unnecessary_fold.rs:16:25
    |
 LL |     let _: i32 = (0..3).fold(0, |acc, x| acc + x);
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `sum()`
 
 error: this `.fold` can be written more succinctly using another method
-  --> $DIR/unnecessary_fold.rs:12:25
+  --> $DIR/unnecessary_fold.rs:18:25
    |
 LL |     let _: i32 = (0..3).fold(1, |acc, x| acc * x);
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `product()`
 
 error: this `.fold` can be written more succinctly using another method
-  --> $DIR/unnecessary_fold.rs:17:41
+  --> $DIR/unnecessary_fold.rs:23:41
    |
 LL |     let _: bool = (0..3).map(|x| 2 * x).fold(false, |acc, x| acc || x > 2);
    |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)`
 
 error: this `.fold` can be written more succinctly using another method
-  --> $DIR/unnecessary_fold.rs:47:10
+  --> $DIR/unnecessary_fold.rs:53:10
    |
 LL |         .fold(false, |acc, x| acc || x > 2);
    |          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `any(|x| x > 2)`
 
 error: this `.fold` can be written more succinctly using another method
-  --> $DIR/unnecessary_fold.rs:58:33
+  --> $DIR/unnecessary_fold.rs:64:33
    |
 LL |         assert_eq!(map.values().fold(0, |x, y| x + y), 0);
    |                                 ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum::<i32>()`
 
 error: this `.fold` can be written more succinctly using another method
-  --> $DIR/unnecessary_fold.rs:61:30
+  --> $DIR/unnecessary_fold.rs:67:30
    |
 LL |         let _ = map.values().fold(0, |x, y| x + y);
    |                              ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum::<i32>()`
 
 error: this `.fold` can be written more succinctly using another method
-  --> $DIR/unnecessary_fold.rs:62:30
+  --> $DIR/unnecessary_fold.rs:68:30
    |
 LL |         let _ = map.values().fold(1, |x, y| x * y);
    |                              ^^^^^^^^^^^^^^^^^^^^^ help: try: `product::<i32>()`
 
 error: this `.fold` can be written more succinctly using another method
-  --> $DIR/unnecessary_fold.rs:63:35
+  --> $DIR/unnecessary_fold.rs:69:35
    |
 LL |         let _: i32 = map.values().fold(0, |x, y| x + y);
    |                                   ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum()`
 
 error: this `.fold` can be written more succinctly using another method
-  --> $DIR/unnecessary_fold.rs:64:35
+  --> $DIR/unnecessary_fold.rs:70:35
    |
 LL |         let _: i32 = map.values().fold(1, |x, y| x * y);
    |                                   ^^^^^^^^^^^^^^^^^^^^^ help: try: `product()`
 
 error: this `.fold` can be written more succinctly using another method
-  --> $DIR/unnecessary_fold.rs:65:31
+  --> $DIR/unnecessary_fold.rs:71:31
    |
 LL |         anything(map.values().fold(0, |x, y| x + y));
    |                               ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum::<i32>()`
 
 error: this `.fold` can be written more succinctly using another method
-  --> $DIR/unnecessary_fold.rs:66:31
+  --> $DIR/unnecessary_fold.rs:72:31
    |
 LL |         anything(map.values().fold(1, |x, y| x * y));
    |                               ^^^^^^^^^^^^^^^^^^^^^ help: try: `product::<i32>()`
 
 error: this `.fold` can be written more succinctly using another method
-  --> $DIR/unnecessary_fold.rs:67:26
+  --> $DIR/unnecessary_fold.rs:73:26
    |
 LL |         num(map.values().fold(0, |x, y| x + y));
    |                          ^^^^^^^^^^^^^^^^^^^^^ help: try: `sum()`
 
 error: this `.fold` can be written more succinctly using another method
-  --> $DIR/unnecessary_fold.rs:68:26
+  --> $DIR/unnecessary_fold.rs:74:26
    |
 LL |         num(map.values().fold(1, |x, y| x * y));
    |                          ^^^^^^^^^^^^^^^^^^^^^ help: try: `product()`
 
-error: aborting due to 15 previous errors
+error: aborting due to 16 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.fixed b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
index 463412daec0..11761c6c90e 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
@@ -106,4 +106,11 @@ fn main() {
 
     // Issue #11885
     Cout << 16;
+
+    // Issue #11575
+    // Bad formatting is required to trigger the bug
+    #[rustfmt::skip]
+    'label: {
+        break 'label
+    };
 }
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.rs b/src/tools/clippy/tests/ui/unnecessary_operation.rs
index f0d28e28902..de0081289ac 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.rs
@@ -110,4 +110,11 @@ fn main() {
 
     // Issue #11885
     Cout << 16;
+
+    // Issue #11575
+    // Bad formatting is required to trigger the bug
+    #[rustfmt::skip]
+    'label: {
+        break 'label
+    };
 }
diff --git a/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.fixed b/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.fixed
new file mode 100644
index 00000000000..224e0b52d75
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.fixed
@@ -0,0 +1,61 @@
+#![warn(clippy::unnecessary_result_map_or_else)]
+#![allow(clippy::unnecessary_literal_unwrap, clippy::let_and_return, clippy::let_unit_value)]
+
+fn main() {
+    let x: Result<(), ()> = Ok(());
+    x.unwrap_or_else(|err| err); //~ ERROR: unused "map closure" when calling
+
+    // Type ascribtion.
+    let x: Result<(), ()> = Ok(());
+    x.unwrap_or_else(|err: ()| err); //~ ERROR: unused "map closure" when calling
+
+    // Auto-deref.
+    let y = String::new();
+    let x: Result<&String, &String> = Ok(&y);
+    let y: &str = x.unwrap_or_else(|err| err); //~ ERROR: unused "map closure" when calling
+
+    // Temporary variable.
+    let x: Result<(), ()> = Ok(());
+    x.unwrap_or_else(|err| err);
+
+    // Should not warn.
+    let x: Result<usize, usize> = Ok(0);
+    x.map_or_else(|err| err, |n| n + 1);
+
+    // Should not warn.
+    let y = ();
+    let x: Result<(), ()> = Ok(());
+    x.map_or_else(|err| err, |_| y);
+
+    // Should not warn.
+    let y = ();
+    let x: Result<(), ()> = Ok(());
+    x.map_or_else(
+        |err| err,
+        |_| {
+            let tmp = y;
+            tmp
+        },
+    );
+
+    // Should not warn.
+    let x: Result<usize, usize> = Ok(1);
+    x.map_or_else(
+        |err| err,
+        |n| {
+            let tmp = n + 1;
+            tmp
+        },
+    );
+
+    // Should not warn.
+    let y = 0;
+    let x: Result<usize, usize> = Ok(1);
+    x.map_or_else(
+        |err| err,
+        |n| {
+            let tmp = n;
+            y
+        },
+    );
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.rs b/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.rs
new file mode 100644
index 00000000000..4fe950a4cfa
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.rs
@@ -0,0 +1,69 @@
+#![warn(clippy::unnecessary_result_map_or_else)]
+#![allow(clippy::unnecessary_literal_unwrap, clippy::let_and_return, clippy::let_unit_value)]
+
+fn main() {
+    let x: Result<(), ()> = Ok(());
+    x.map_or_else(|err| err, |n| n); //~ ERROR: unused "map closure" when calling
+
+    // Type ascribtion.
+    let x: Result<(), ()> = Ok(());
+    x.map_or_else(|err: ()| err, |n: ()| n); //~ ERROR: unused "map closure" when calling
+
+    // Auto-deref.
+    let y = String::new();
+    let x: Result<&String, &String> = Ok(&y);
+    let y: &str = x.map_or_else(|err| err, |n| n); //~ ERROR: unused "map closure" when calling
+
+    // Temporary variable.
+    let x: Result<(), ()> = Ok(());
+    x.map_or_else(
+        //~^ ERROR: unused "map closure" when calling
+        |err| err,
+        |n| {
+            let tmp = n;
+            let tmp2 = tmp;
+            tmp2
+        },
+    );
+
+    // Should not warn.
+    let x: Result<usize, usize> = Ok(0);
+    x.map_or_else(|err| err, |n| n + 1);
+
+    // Should not warn.
+    let y = ();
+    let x: Result<(), ()> = Ok(());
+    x.map_or_else(|err| err, |_| y);
+
+    // Should not warn.
+    let y = ();
+    let x: Result<(), ()> = Ok(());
+    x.map_or_else(
+        |err| err,
+        |_| {
+            let tmp = y;
+            tmp
+        },
+    );
+
+    // Should not warn.
+    let x: Result<usize, usize> = Ok(1);
+    x.map_or_else(
+        |err| err,
+        |n| {
+            let tmp = n + 1;
+            tmp
+        },
+    );
+
+    // Should not warn.
+    let y = 0;
+    let x: Result<usize, usize> = Ok(1);
+    x.map_or_else(
+        |err| err,
+        |n| {
+            let tmp = n;
+            y
+        },
+    );
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.stderr b/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.stderr
new file mode 100644
index 00000000000..0f83be5d556
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_result_map_or_else.stderr
@@ -0,0 +1,35 @@
+error: unused "map closure" when calling `Result::map_or_else` value
+  --> $DIR/unnecessary_result_map_or_else.rs:6:5
+   |
+LL |     x.map_or_else(|err| err, |n| n);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `x.unwrap_or_else(|err| err)`
+   |
+   = note: `-D clippy::unnecessary-result-map-or-else` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::unnecessary_result_map_or_else)]`
+
+error: unused "map closure" when calling `Result::map_or_else` value
+  --> $DIR/unnecessary_result_map_or_else.rs:10:5
+   |
+LL |     x.map_or_else(|err: ()| err, |n: ()| n);
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `x.unwrap_or_else(|err: ()| err)`
+
+error: unused "map closure" when calling `Result::map_or_else` value
+  --> $DIR/unnecessary_result_map_or_else.rs:15:19
+   |
+LL |     let y: &str = x.map_or_else(|err| err, |n| n);
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider using `unwrap_or_else`: `x.unwrap_or_else(|err| err)`
+
+error: unused "map closure" when calling `Result::map_or_else` value
+  --> $DIR/unnecessary_result_map_or_else.rs:19:5
+   |
+LL | /     x.map_or_else(
+LL | |
+LL | |         |err| err,
+LL | |         |n| {
+...  |
+LL | |         },
+LL | |     );
+   | |_____^ help: consider using `unwrap_or_else`: `x.unwrap_or_else(|err| err)`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
index 2dd1d746626..7f01c981a93 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
@@ -27,6 +27,7 @@ impl AsRef<str> for X {
     }
 }
 
+#[allow(clippy::to_string_trait_impl)]
 impl ToString for X {
     fn to_string(&self) -> String {
         self.0.to_string()
@@ -265,6 +266,7 @@ mod issue_8507 {
         }
     }
 
+    #[allow(clippy::to_string_trait_impl)]
     impl ToString for Y {
         fn to_string(&self) -> String {
             self.0.to_string()
@@ -338,6 +340,7 @@ mod issue_9317 {
 
     struct Bytes {}
 
+    #[allow(clippy::to_string_trait_impl)]
     impl ToString for Bytes {
         fn to_string(&self) -> String {
             "123".to_string()
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
index 17fad33402b..a270ed1e1c2 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
@@ -27,6 +27,7 @@ impl AsRef<str> for X {
     }
 }
 
+#[allow(clippy::to_string_trait_impl)]
 impl ToString for X {
     fn to_string(&self) -> String {
         self.0.to_string()
@@ -265,6 +266,7 @@ mod issue_8507 {
         }
     }
 
+    #[allow(clippy::to_string_trait_impl)]
     impl ToString for Y {
         fn to_string(&self) -> String {
             self.0.to_string()
@@ -338,6 +340,7 @@ mod issue_9317 {
 
     struct Bytes {}
 
+    #[allow(clippy::to_string_trait_impl)]
     impl ToString for Bytes {
         fn to_string(&self) -> String {
             "123".to_string()
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
index ad6fa422b8c..95ff5f2ec2c 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
@@ -1,11 +1,11 @@
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:154:64
+  --> $DIR/unnecessary_to_owned.rs:155:64
    |
 LL |     require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
    |                                                                ^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/unnecessary_to_owned.rs:154:20
+  --> $DIR/unnecessary_to_owned.rs:155:20
    |
 LL |     require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned());
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -13,55 +13,55 @@ LL |     require_c_str(&CString::from_vec_with_nul(vec![0]).unwrap().to_owned())
    = help: to override `-D warnings` add `#[allow(clippy::redundant_clone)]`
 
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:155:40
+  --> $DIR/unnecessary_to_owned.rs:156:40
    |
 LL |     require_os_str(&OsString::from("x").to_os_string());
    |                                        ^^^^^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/unnecessary_to_owned.rs:155:21
+  --> $DIR/unnecessary_to_owned.rs:156:21
    |
 LL |     require_os_str(&OsString::from("x").to_os_string());
    |                     ^^^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:156:48
+  --> $DIR/unnecessary_to_owned.rs:157:48
    |
 LL |     require_path(&std::path::PathBuf::from("x").to_path_buf());
    |                                                ^^^^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/unnecessary_to_owned.rs:156:19
+  --> $DIR/unnecessary_to_owned.rs:157:19
    |
 LL |     require_path(&std::path::PathBuf::from("x").to_path_buf());
    |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:157:35
+  --> $DIR/unnecessary_to_owned.rs:158:35
    |
 LL |     require_str(&String::from("x").to_string());
    |                                   ^^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/unnecessary_to_owned.rs:157:18
+  --> $DIR/unnecessary_to_owned.rs:158:18
    |
 LL |     require_str(&String::from("x").to_string());
    |                  ^^^^^^^^^^^^^^^^^
 
 error: redundant clone
-  --> $DIR/unnecessary_to_owned.rs:158:39
+  --> $DIR/unnecessary_to_owned.rs:159:39
    |
 LL |     require_slice(&[String::from("x")].to_owned());
    |                                       ^^^^^^^^^^^ help: remove this
    |
 note: this value is dropped without further use
-  --> $DIR/unnecessary_to_owned.rs:158:20
+  --> $DIR/unnecessary_to_owned.rs:159:20
    |
 LL |     require_slice(&[String::from("x")].to_owned());
    |                    ^^^^^^^^^^^^^^^^^^^
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:63:36
+  --> $DIR/unnecessary_to_owned.rs:64:36
    |
 LL |     require_c_str(&Cow::from(c_str).into_owned());
    |                                    ^^^^^^^^^^^^^ help: remove this
@@ -70,415 +70,415 @@ LL |     require_c_str(&Cow::from(c_str).into_owned());
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_to_owned)]`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:64:19
+  --> $DIR/unnecessary_to_owned.rs:65:19
    |
 LL |     require_c_str(&c_str.to_owned());
    |                   ^^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_os_string`
-  --> $DIR/unnecessary_to_owned.rs:66:20
+  --> $DIR/unnecessary_to_owned.rs:67:20
    |
 LL |     require_os_str(&os_str.to_os_string());
    |                    ^^^^^^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:67:38
+  --> $DIR/unnecessary_to_owned.rs:68:38
    |
 LL |     require_os_str(&Cow::from(os_str).into_owned());
    |                                      ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:68:20
+  --> $DIR/unnecessary_to_owned.rs:69:20
    |
 LL |     require_os_str(&os_str.to_owned());
    |                    ^^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_path_buf`
-  --> $DIR/unnecessary_to_owned.rs:70:18
+  --> $DIR/unnecessary_to_owned.rs:71:18
    |
 LL |     require_path(&path.to_path_buf());
    |                  ^^^^^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:71:34
+  --> $DIR/unnecessary_to_owned.rs:72:34
    |
 LL |     require_path(&Cow::from(path).into_owned());
    |                                  ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:72:18
+  --> $DIR/unnecessary_to_owned.rs:73:18
    |
 LL |     require_path(&path.to_owned());
    |                  ^^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:74:17
+  --> $DIR/unnecessary_to_owned.rs:75:17
    |
 LL |     require_str(&s.to_string());
    |                 ^^^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:75:30
+  --> $DIR/unnecessary_to_owned.rs:76:30
    |
 LL |     require_str(&Cow::from(s).into_owned());
    |                              ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:76:17
+  --> $DIR/unnecessary_to_owned.rs:77:17
    |
 LL |     require_str(&s.to_owned());
    |                 ^^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:77:17
+  --> $DIR/unnecessary_to_owned.rs:78:17
    |
 LL |     require_str(&x_ref.to_string());
    |                 ^^^^^^^^^^^^^^^^^^ help: use: `x_ref.as_ref()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:79:19
+  --> $DIR/unnecessary_to_owned.rs:80:19
    |
 LL |     require_slice(&slice.to_vec());
    |                   ^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:80:36
+  --> $DIR/unnecessary_to_owned.rs:81:36
    |
 LL |     require_slice(&Cow::from(slice).into_owned());
    |                                    ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:81:19
+  --> $DIR/unnecessary_to_owned.rs:82:19
    |
 LL |     require_slice(&array.to_owned());
    |                   ^^^^^^^^^^^^^^^^^ help: use: `array.as_ref()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:82:19
+  --> $DIR/unnecessary_to_owned.rs:83:19
    |
 LL |     require_slice(&array_ref.to_owned());
    |                   ^^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref.as_ref()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:83:19
+  --> $DIR/unnecessary_to_owned.rs:84:19
    |
 LL |     require_slice(&slice.to_owned());
    |                   ^^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `into_owned`
-  --> $DIR/unnecessary_to_owned.rs:86:42
+  --> $DIR/unnecessary_to_owned.rs:87:42
    |
 LL |     require_x(&Cow::<X>::Owned(x.clone()).into_owned());
    |                                          ^^^^^^^^^^^^^ help: remove this
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:89:25
+  --> $DIR/unnecessary_to_owned.rs:90:25
    |
 LL |     require_deref_c_str(c_str.to_owned());
    |                         ^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:90:26
+  --> $DIR/unnecessary_to_owned.rs:91:26
    |
 LL |     require_deref_os_str(os_str.to_owned());
    |                          ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:91:24
+  --> $DIR/unnecessary_to_owned.rs:92:24
    |
 LL |     require_deref_path(path.to_owned());
    |                        ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:92:23
+  --> $DIR/unnecessary_to_owned.rs:93:23
    |
 LL |     require_deref_str(s.to_owned());
    |                       ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:93:25
+  --> $DIR/unnecessary_to_owned.rs:94:25
    |
 LL |     require_deref_slice(slice.to_owned());
    |                         ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:95:30
+  --> $DIR/unnecessary_to_owned.rs:96:30
    |
 LL |     require_impl_deref_c_str(c_str.to_owned());
    |                              ^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:96:31
+  --> $DIR/unnecessary_to_owned.rs:97:31
    |
 LL |     require_impl_deref_os_str(os_str.to_owned());
    |                               ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:97:29
+  --> $DIR/unnecessary_to_owned.rs:98:29
    |
 LL |     require_impl_deref_path(path.to_owned());
    |                             ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:98:28
+  --> $DIR/unnecessary_to_owned.rs:99:28
    |
 LL |     require_impl_deref_str(s.to_owned());
    |                            ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:99:30
+  --> $DIR/unnecessary_to_owned.rs:100:30
    |
 LL |     require_impl_deref_slice(slice.to_owned());
    |                              ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:101:29
+  --> $DIR/unnecessary_to_owned.rs:102:29
    |
 LL |     require_deref_str_slice(s.to_owned(), slice.to_owned());
    |                             ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:101:43
+  --> $DIR/unnecessary_to_owned.rs:102:43
    |
 LL |     require_deref_str_slice(s.to_owned(), slice.to_owned());
    |                                           ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:102:29
+  --> $DIR/unnecessary_to_owned.rs:103:29
    |
 LL |     require_deref_slice_str(slice.to_owned(), s.to_owned());
    |                             ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:102:47
+  --> $DIR/unnecessary_to_owned.rs:103:47
    |
 LL |     require_deref_slice_str(slice.to_owned(), s.to_owned());
    |                                               ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:104:26
+  --> $DIR/unnecessary_to_owned.rs:105:26
    |
 LL |     require_as_ref_c_str(c_str.to_owned());
    |                          ^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:105:27
+  --> $DIR/unnecessary_to_owned.rs:106:27
    |
 LL |     require_as_ref_os_str(os_str.to_owned());
    |                           ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:106:25
+  --> $DIR/unnecessary_to_owned.rs:107:25
    |
 LL |     require_as_ref_path(path.to_owned());
    |                         ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:107:24
+  --> $DIR/unnecessary_to_owned.rs:108:24
    |
 LL |     require_as_ref_str(s.to_owned());
    |                        ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:108:24
+  --> $DIR/unnecessary_to_owned.rs:109:24
    |
 LL |     require_as_ref_str(x.to_owned());
    |                        ^^^^^^^^^^^^ help: use: `&x`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:109:26
+  --> $DIR/unnecessary_to_owned.rs:110:26
    |
 LL |     require_as_ref_slice(array.to_owned());
    |                          ^^^^^^^^^^^^^^^^ help: use: `array`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:110:26
+  --> $DIR/unnecessary_to_owned.rs:111:26
    |
 LL |     require_as_ref_slice(array_ref.to_owned());
    |                          ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:111:26
+  --> $DIR/unnecessary_to_owned.rs:112:26
    |
 LL |     require_as_ref_slice(slice.to_owned());
    |                          ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:113:31
+  --> $DIR/unnecessary_to_owned.rs:114:31
    |
 LL |     require_impl_as_ref_c_str(c_str.to_owned());
    |                               ^^^^^^^^^^^^^^^^ help: use: `c_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:114:32
+  --> $DIR/unnecessary_to_owned.rs:115:32
    |
 LL |     require_impl_as_ref_os_str(os_str.to_owned());
    |                                ^^^^^^^^^^^^^^^^^ help: use: `os_str`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:115:30
+  --> $DIR/unnecessary_to_owned.rs:116:30
    |
 LL |     require_impl_as_ref_path(path.to_owned());
    |                              ^^^^^^^^^^^^^^^ help: use: `path`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:116:29
+  --> $DIR/unnecessary_to_owned.rs:117:29
    |
 LL |     require_impl_as_ref_str(s.to_owned());
    |                             ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:117:29
+  --> $DIR/unnecessary_to_owned.rs:118:29
    |
 LL |     require_impl_as_ref_str(x.to_owned());
    |                             ^^^^^^^^^^^^ help: use: `&x`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:118:31
+  --> $DIR/unnecessary_to_owned.rs:119:31
    |
 LL |     require_impl_as_ref_slice(array.to_owned());
    |                               ^^^^^^^^^^^^^^^^ help: use: `array`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:119:31
+  --> $DIR/unnecessary_to_owned.rs:120:31
    |
 LL |     require_impl_as_ref_slice(array_ref.to_owned());
    |                               ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:120:31
+  --> $DIR/unnecessary_to_owned.rs:121:31
    |
 LL |     require_impl_as_ref_slice(slice.to_owned());
    |                               ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:122:30
+  --> $DIR/unnecessary_to_owned.rs:123:30
    |
 LL |     require_as_ref_str_slice(s.to_owned(), array.to_owned());
    |                              ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:122:44
+  --> $DIR/unnecessary_to_owned.rs:123:44
    |
 LL |     require_as_ref_str_slice(s.to_owned(), array.to_owned());
    |                                            ^^^^^^^^^^^^^^^^ help: use: `array`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:123:30
+  --> $DIR/unnecessary_to_owned.rs:124:30
    |
 LL |     require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
    |                              ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:123:44
+  --> $DIR/unnecessary_to_owned.rs:124:44
    |
 LL |     require_as_ref_str_slice(s.to_owned(), array_ref.to_owned());
    |                                            ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:124:30
+  --> $DIR/unnecessary_to_owned.rs:125:30
    |
 LL |     require_as_ref_str_slice(s.to_owned(), slice.to_owned());
    |                              ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:124:44
+  --> $DIR/unnecessary_to_owned.rs:125:44
    |
 LL |     require_as_ref_str_slice(s.to_owned(), slice.to_owned());
    |                                            ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:125:30
+  --> $DIR/unnecessary_to_owned.rs:126:30
    |
 LL |     require_as_ref_slice_str(array.to_owned(), s.to_owned());
    |                              ^^^^^^^^^^^^^^^^ help: use: `array`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:125:48
+  --> $DIR/unnecessary_to_owned.rs:126:48
    |
 LL |     require_as_ref_slice_str(array.to_owned(), s.to_owned());
    |                                                ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:126:30
+  --> $DIR/unnecessary_to_owned.rs:127:30
    |
 LL |     require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
    |                              ^^^^^^^^^^^^^^^^^^^^ help: use: `array_ref`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:126:52
+  --> $DIR/unnecessary_to_owned.rs:127:52
    |
 LL |     require_as_ref_slice_str(array_ref.to_owned(), s.to_owned());
    |                                                    ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:127:30
+  --> $DIR/unnecessary_to_owned.rs:128:30
    |
 LL |     require_as_ref_slice_str(slice.to_owned(), s.to_owned());
    |                              ^^^^^^^^^^^^^^^^ help: use: `slice`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:127:48
+  --> $DIR/unnecessary_to_owned.rs:128:48
    |
 LL |     require_as_ref_slice_str(slice.to_owned(), s.to_owned());
    |                                                ^^^^^^^^^^^^ help: use: `s`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:129:20
+  --> $DIR/unnecessary_to_owned.rs:130:20
    |
 LL |     let _ = x.join(&x_ref.to_string());
    |                    ^^^^^^^^^^^^^^^^^^ help: use: `x_ref`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:131:13
+  --> $DIR/unnecessary_to_owned.rs:132:13
    |
 LL |     let _ = slice.to_vec().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:132:13
+  --> $DIR/unnecessary_to_owned.rs:133:13
    |
 LL |     let _ = slice.to_owned().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:133:13
+  --> $DIR/unnecessary_to_owned.rs:134:13
    |
 LL |     let _ = [std::path::PathBuf::new()][..].to_vec().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:134:13
+  --> $DIR/unnecessary_to_owned.rs:135:13
    |
 LL |     let _ = [std::path::PathBuf::new()][..].to_owned().into_iter();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:136:13
+  --> $DIR/unnecessary_to_owned.rs:137:13
    |
 LL |     let _ = IntoIterator::into_iter(slice.to_vec());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:137:13
+  --> $DIR/unnecessary_to_owned.rs:138:13
    |
 LL |     let _ = IntoIterator::into_iter(slice.to_owned());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `slice.iter().copied()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:138:13
+  --> $DIR/unnecessary_to_owned.rs:139:13
    |
 LL |     let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_vec());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned.rs:139:13
+  --> $DIR/unnecessary_to_owned.rs:140:13
    |
 LL |     let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:201:14
+  --> $DIR/unnecessary_to_owned.rs:202:14
    |
 LL |     for t in file_types.to_vec() {
    |              ^^^^^^^^^^^^^^^^^^^
@@ -494,31 +494,31 @@ LL +         let path = match get_file_path(t) {
    |
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:224:14
+  --> $DIR/unnecessary_to_owned.rs:225:14
    |
 LL |     let _ = &["x"][..].to_vec().into_iter();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:229:14
+  --> $DIR/unnecessary_to_owned.rs:230:14
    |
 LL |     let _ = &["x"][..].to_vec().into_iter();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:276:24
+  --> $DIR/unnecessary_to_owned.rs:278:24
    |
 LL |         Box::new(build(y.to_string()))
    |                        ^^^^^^^^^^^^^ help: use: `y`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned.rs:384:12
+  --> $DIR/unnecessary_to_owned.rs:387:12
    |
 LL |         id("abc".to_string())
    |            ^^^^^^^^^^^^^^^^^ help: use: `"abc"`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned.rs:527:37
+  --> $DIR/unnecessary_to_owned.rs:530:37
    |
 LL |         IntoFuture::into_future(foo([].to_vec(), &0));
    |                                     ^^^^^^^^^^^ help: use: `[]`
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.fixed b/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.fixed
index f87c898f9b7..e0ba216f41b 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.fixed
@@ -8,6 +8,7 @@ impl AsRef<str> for Issue12068 {
     }
 }
 
+#[allow(clippy::to_string_trait_impl)]
 impl ToString for Issue12068 {
     fn to_string(&self) -> String {
         String::new()
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.rs
index db5719e5880..70efc6ebba5 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.rs
@@ -8,6 +8,7 @@ impl AsRef<str> for Issue12068 {
     }
 }
 
+#[allow(clippy::to_string_trait_impl)]
 impl ToString for Issue12068 {
     fn to_string(&self) -> String {
         String::new()
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.stderr b/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.stderr
index 4cfaeed3384..9aea15b48bf 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned_on_split.stderr
@@ -1,5 +1,5 @@
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned_on_split.rs:18:13
+  --> $DIR/unnecessary_to_owned_on_split.rs:19:13
    |
 LL |     let _ = "a".to_string().split('a').next().unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `"a".split('a')`
@@ -8,49 +8,49 @@ LL |     let _ = "a".to_string().split('a').next().unwrap();
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_to_owned)]`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned_on_split.rs:20:13
+  --> $DIR/unnecessary_to_owned_on_split.rs:21:13
    |
 LL |     let _ = "a".to_string().split("a").next().unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `"a".split("a")`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned_on_split.rs:22:13
+  --> $DIR/unnecessary_to_owned_on_split.rs:23:13
    |
 LL |     let _ = "a".to_owned().split('a').next().unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `"a".split('a')`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned_on_split.rs:24:13
+  --> $DIR/unnecessary_to_owned_on_split.rs:25:13
    |
 LL |     let _ = "a".to_owned().split("a").next().unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `"a".split("a")`
 
 error: unnecessary use of `to_string`
-  --> $DIR/unnecessary_to_owned_on_split.rs:26:13
+  --> $DIR/unnecessary_to_owned_on_split.rs:27:13
    |
 LL |     let _ = Issue12068.to_string().split('a').next().unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `Issue12068.as_ref().split('a')`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned_on_split.rs:29:13
+  --> $DIR/unnecessary_to_owned_on_split.rs:30:13
    |
 LL |     let _ = [1].to_vec().split(|x| *x == 2).next().unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[1].split(|x| *x == 2)`
 
 error: unnecessary use of `to_vec`
-  --> $DIR/unnecessary_to_owned_on_split.rs:31:13
+  --> $DIR/unnecessary_to_owned_on_split.rs:32:13
    |
 LL |     let _ = [1].to_vec().split(|x| *x == 2).next().unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[1].split(|x| *x == 2)`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned_on_split.rs:33:13
+  --> $DIR/unnecessary_to_owned_on_split.rs:34:13
    |
 LL |     let _ = [1].to_owned().split(|x| *x == 2).next().unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[1].split(|x| *x == 2)`
 
 error: unnecessary use of `to_owned`
-  --> $DIR/unnecessary_to_owned_on_split.rs:35:13
+  --> $DIR/unnecessary_to_owned_on_split.rs:36:13
    |
 LL |     let _ = [1].to_owned().split(|x| *x == 2).next().unwrap();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[1].split(|x| *x == 2)`
diff --git a/src/tools/clippy/tests/ui/unused_io_amount.rs b/src/tools/clippy/tests/ui/unused_io_amount.rs
index 9974600dad5..7e5a10c911b 100644
--- a/src/tools/clippy/tests/ui/unused_io_amount.rs
+++ b/src/tools/clippy/tests/ui/unused_io_amount.rs
@@ -229,4 +229,47 @@ fn on_return_should_not_raise<T: io::Read + io::Write>(s: &mut T) -> io::Result<
     s.read(&mut buf)
 }
 
+pub fn unwrap_in_block(rdr: &mut dyn std::io::Read) -> std::io::Result<usize> {
+    let read = { rdr.read(&mut [0])? };
+    Ok(read)
+}
+
+pub fn consumed_example(rdr: &mut dyn std::io::Read) {
+    match rdr.read(&mut [0]) {
+        Ok(0) => println!("EOF"),
+        Ok(_) => println!("fully read"),
+        Err(_) => println!("fail"),
+    };
+    match rdr.read(&mut [0]) {
+        Ok(0) => println!("EOF"),
+        Ok(_) => println!("fully read"),
+        Err(_) => println!("fail"),
+    }
+}
+
+pub fn unreachable_or_panic(rdr: &mut dyn std::io::Read) {
+    {
+        match rdr.read(&mut [0]) {
+            Ok(_) => unreachable!(),
+            Err(_) => println!("expected"),
+        }
+    }
+
+    {
+        match rdr.read(&mut [0]) {
+            Ok(_) => panic!(),
+            Err(_) => println!("expected"),
+        }
+    }
+}
+
+pub fn wildcards(rdr: &mut dyn std::io::Read) {
+    {
+        match rdr.read(&mut [0]) {
+            Ok(1) => todo!(),
+            _ => todo!(),
+        }
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/unused_io_amount.stderr b/src/tools/clippy/tests/ui/unused_io_amount.stderr
index 4af56d264bf..1aab56966a8 100644
--- a/src/tools/clippy/tests/ui/unused_io_amount.stderr
+++ b/src/tools/clippy/tests/ui/unused_io_amount.stderr
@@ -180,7 +180,7 @@ note: the result is consumed here, but the amount of I/O bytes remains unhandled
   --> $DIR/unused_io_amount.rs:149:9
    |
 LL |         Ok(_) => todo!(),
-   |         ^^^^^^^^^^^^^^^^
+   |         ^^^^^
 
 error: read amount is not handled
   --> $DIR/unused_io_amount.rs:155:11
@@ -193,7 +193,7 @@ note: the result is consumed here, but the amount of I/O bytes remains unhandled
   --> $DIR/unused_io_amount.rs:157:9
    |
 LL |         Ok(_) => todo!(),
-   |         ^^^^^^^^^^^^^^^^
+   |         ^^^^^
 
 error: read amount is not handled
   --> $DIR/unused_io_amount.rs:164:11
@@ -206,7 +206,7 @@ note: the result is consumed here, but the amount of I/O bytes remains unhandled
   --> $DIR/unused_io_amount.rs:166:9
    |
 LL |         Ok(_) => todo!(),
-   |         ^^^^^^^^^^^^^^^^
+   |         ^^^^^
 
 error: written amount is not handled
   --> $DIR/unused_io_amount.rs:173:11
@@ -219,7 +219,7 @@ note: the result is consumed here, but the amount of I/O bytes remains unhandled
   --> $DIR/unused_io_amount.rs:175:9
    |
 LL |         Ok(_) => todo!(),
-   |         ^^^^^^^^^^^^^^^^
+   |         ^^^^^
 
 error: read amount is not handled
   --> $DIR/unused_io_amount.rs:186:8
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 d628d2227b7..59b5c858d04 100644
--- a/src/tools/clippy/tests/ui/while_let_on_iterator.fixed
+++ b/src/tools/clippy/tests/ui/while_let_on_iterator.fixed
@@ -406,7 +406,7 @@ fn issue_8113() {
 fn fn_once_closure() {
     let mut it = 0..10;
     (|| {
-        for x in it {
+        for x in it.by_ref() {
             if x % 2 == 0 {
                 break;
             }
@@ -441,7 +441,19 @@ fn fn_once_closure() {
                 break;
             }
         }
-    })
+    });
+
+    trait MySpecialFnMut: FnOnce() {}
+    impl<T: FnOnce()> MySpecialFnMut for T {}
+    fn f4(_: impl MySpecialFnMut) {}
+    let mut it = 0..10;
+    f4(|| {
+        for x in it {
+            if x % 2 == 0 {
+                break;
+            }
+        }
+    });
 }
 
 fn main() {
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 525dbbaaab6..559513d5694 100644
--- a/src/tools/clippy/tests/ui/while_let_on_iterator.rs
+++ b/src/tools/clippy/tests/ui/while_let_on_iterator.rs
@@ -441,7 +441,19 @@ fn fn_once_closure() {
                 break;
             }
         }
-    })
+    });
+
+    trait MySpecialFnMut: FnOnce() {}
+    impl<T: FnOnce()> MySpecialFnMut for T {}
+    fn f4(_: impl MySpecialFnMut) {}
+    let mut it = 0..10;
+    f4(|| {
+        while let Some(x) = it.next() {
+            if x % 2 == 0 {
+                break;
+            }
+        }
+    });
 }
 
 fn main() {
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 cdc83b81667..7b9a9dc049a 100644
--- a/src/tools/clippy/tests/ui/while_let_on_iterator.stderr
+++ b/src/tools/clippy/tests/ui/while_let_on_iterator.stderr
@@ -131,7 +131,7 @@ error: this loop could be written as a `for` loop
   --> $DIR/while_let_on_iterator.rs:409:9
    |
 LL |         while let Some(x) = it.next() {
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it`
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for x in it.by_ref()`
 
 error: this loop could be written as a `for` loop
   --> $DIR/while_let_on_iterator.rs:419:9
@@ -152,10 +152,16 @@ 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:449:5
+  --> $DIR/while_let_on_iterator.rs:451: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:461:5
    |
 LL |     while let Some(..) = it.next() {
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `for _ in it`
 
-error: aborting due to 26 previous errors
+error: aborting due to 27 previous errors
 
diff --git a/tests/codegen/async-closure-debug.rs b/tests/codegen/async-closure-debug.rs
new file mode 100644
index 00000000000..6718d2b6627
--- /dev/null
+++ b/tests/codegen/async-closure-debug.rs
@@ -0,0 +1,21 @@
+// Just make sure that async closures don't ICE.
+//
+// compile-flags: -C debuginfo=2 --edition=2018
+// ignore-msvc
+
+// CHECK-DAG:  [[GEN_FN:!.*]] = !DINamespace(name: "async_closure_test"
+// CHECK-DAG:  [[CLOSURE:!.*]] = !DICompositeType(tag: DW_TAG_structure_type, name: "{closure_env#0}", scope: [[GEN_FN]]
+// CHECK-DAG:  [[UPVAR:!.*]] = !DIDerivedType(tag: DW_TAG_member, name: "upvar", scope: [[CLOSURE]]
+
+#![feature(async_closure)]
+
+fn async_closure_test(upvar: &str) -> impl async Fn() + '_ {
+    async move || {
+        let hello = String::from("hello");
+        println!("{hello}, {upvar}");
+    }
+}
+
+fn main() {
+    let _async_closure = async_closure_test("world");
+}
diff --git a/tests/ui/async-await/async-closures/async-fn-mut-for-async-fn.rs b/tests/ui/async-await/async-closures/async-fn-mut-for-async-fn.rs
index f73b43dd152..897def791fe 100644
--- a/tests/ui/async-await/async-closures/async-fn-mut-for-async-fn.rs
+++ b/tests/ui/async-await/async-closures/async-fn-mut-for-async-fn.rs
@@ -2,9 +2,6 @@
 // edition:2021
 // run-pass
 
-// FIXME(async_closures): When `fn_sig_for_fn_abi` is fixed, remove this.
-// ignore-pass (test emits codegen-time warnings)
-
 #![feature(async_closure)]
 
 extern crate block_on;
diff --git a/tests/ui/async-await/async-closures/async-fn-once-for-async-fn.rs b/tests/ui/async-await/async-closures/async-fn-once-for-async-fn.rs
index 0ba323a71cd..0e9b25e6d30 100644
--- a/tests/ui/async-await/async-closures/async-fn-once-for-async-fn.rs
+++ b/tests/ui/async-await/async-closures/async-fn-once-for-async-fn.rs
@@ -2,9 +2,6 @@
 // edition:2021
 // run-pass
 
-// FIXME(async_closures): When `fn_sig_for_fn_abi` is fixed, remove this.
-// ignore-pass (test emits codegen-time warnings)
-
 #![feature(async_closure)]
 
 extern crate block_on;
diff --git a/tests/ui/cast/enum-to-numeric-cast.rs b/tests/ui/cast/enum-to-numeric-cast.rs
new file mode 100644
index 00000000000..d5ab2a6a1fe
--- /dev/null
+++ b/tests/ui/cast/enum-to-numeric-cast.rs
@@ -0,0 +1,46 @@
+// Tests that `as` casts from enums to numeric types succeed
+// only if the enum type is "unit-only" or "fieldless" as
+// described here: https://doc.rust-lang.org/reference/items/enumerations.html#casting
+
+pub enum UnitOnly {
+    Foo,
+    Bar,
+    Baz,
+}
+
+pub enum Fieldless {
+    Tuple(),
+    Struct{},
+    Unit,
+}
+
+pub enum NotUnitOnlyOrFieldless {
+    Foo,
+    Bar(u8),
+    Baz
+}
+
+fn main() {
+    let unit_only = UnitOnly::Foo;
+
+    let _ = unit_only as isize;
+    let _ = unit_only as i32;
+    let _ = unit_only as usize;
+    let _ = unit_only as u32;
+
+
+    let fieldless = Fieldless::Struct{};
+
+    let _ = fieldless as isize;
+    let _ = fieldless as i32;
+    let _ = fieldless as usize;
+    let _ = fieldless as u32;
+
+
+    let not_unit_only_or_fieldless = NotUnitOnlyOrFieldless::Foo;
+
+    let _ = not_unit_only_or_fieldless as isize; //~ ERROR non-primitive cast: `NotUnitOnlyOrFieldless` as `isize`
+    let _ = not_unit_only_or_fieldless as i32; //~ ERROR non-primitive cast: `NotUnitOnlyOrFieldless` as `i32`
+    let _ = not_unit_only_or_fieldless as usize; //~ ERROR non-primitive cast: `NotUnitOnlyOrFieldless` as `usize`
+    let _ = not_unit_only_or_fieldless as u32; //~ ERROR non-primitive cast: `NotUnitOnlyOrFieldless` as `u32`
+}
diff --git a/tests/ui/cast/enum-to-numeric-cast.stderr b/tests/ui/cast/enum-to-numeric-cast.stderr
new file mode 100644
index 00000000000..1a49cb97451
--- /dev/null
+++ b/tests/ui/cast/enum-to-numeric-cast.stderr
@@ -0,0 +1,35 @@
+error[E0605]: non-primitive cast: `NotUnitOnlyOrFieldless` as `isize`
+  --> $DIR/enum-to-numeric-cast.rs:42:13
+   |
+LL |     let _ = not_unit_only_or_fieldless as isize;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can be used to convert enum types to numeric types only if the enum type is unit-only or field-less
+   |
+   = note: see https://doc.rust-lang.org/reference/items/enumerations.html#casting for more information
+
+error[E0605]: non-primitive cast: `NotUnitOnlyOrFieldless` as `i32`
+  --> $DIR/enum-to-numeric-cast.rs:43:13
+   |
+LL |     let _ = not_unit_only_or_fieldless as i32;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can be used to convert enum types to numeric types only if the enum type is unit-only or field-less
+   |
+   = note: see https://doc.rust-lang.org/reference/items/enumerations.html#casting for more information
+
+error[E0605]: non-primitive cast: `NotUnitOnlyOrFieldless` as `usize`
+  --> $DIR/enum-to-numeric-cast.rs:44:13
+   |
+LL |     let _ = not_unit_only_or_fieldless as usize;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can be used to convert enum types to numeric types only if the enum type is unit-only or field-less
+   |
+   = note: see https://doc.rust-lang.org/reference/items/enumerations.html#casting for more information
+
+error[E0605]: non-primitive cast: `NotUnitOnlyOrFieldless` as `u32`
+  --> $DIR/enum-to-numeric-cast.rs:45:13
+   |
+LL |     let _ = not_unit_only_or_fieldless as u32;
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ an `as` expression can be used to convert enum types to numeric types only if the enum type is unit-only or field-less
+   |
+   = note: see https://doc.rust-lang.org/reference/items/enumerations.html#casting for more information
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0605`.
diff --git a/tests/ui/cast/issue-88621.stderr b/tests/ui/cast/issue-88621.stderr
index 0459ce5eabd..201651a207a 100644
--- a/tests/ui/cast/issue-88621.stderr
+++ b/tests/ui/cast/issue-88621.stderr
@@ -2,7 +2,9 @@ error[E0605]: non-primitive cast: `Kind2` as `u8`
   --> $DIR/issue-88621.rs:9:13
    |
 LL |     let _ = Kind2::Foo() as u8;
-   |             ^^^^^^^^^^^^^^^^^^ an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
+   |             ^^^^^^^^^^^^^^^^^^ an `as` expression can be used to convert enum types to numeric types only if the enum type is unit-only or field-less
+   |
+   = note: see https://doc.rust-lang.org/reference/items/enumerations.html#casting for more information
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/tag-variant-cast-non-nullary.stderr b/tests/ui/tag-variant-cast-non-nullary.stderr
index 560dd7e8164..2e1dde27d0f 100644
--- a/tests/ui/tag-variant-cast-non-nullary.stderr
+++ b/tests/ui/tag-variant-cast-non-nullary.stderr
@@ -4,7 +4,8 @@ error[E0605]: non-primitive cast: `NonNullary` as `isize`
 LL |     let val = v as isize;
    |               ^^^^^^^^^^ help: consider using the `From` trait instead: `isize::from(v)`
    |
-   = note: an `as` expression can only be used to convert between primitive types or to coerce to a specific trait object
+   = note: an `as` expression can be used to convert enum types to numeric types only if the enum type is unit-only or field-less
+   = note: see https://doc.rust-lang.org/reference/items/enumerations.html#casting for more information
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs b/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs
index c7e2e2d5e04..f6c75317a34 100644
--- a/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs
+++ b/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.rs
@@ -24,6 +24,18 @@ where
 {
 }
 
+// HACK: This impls is necessary so that the impl above is well-formed.
+//
+// When checking that the impl above is well-formed we check `B<T>: Trait<'a, 'b>`
+// with the where clauses `A<T>: Trait<'a, 'b>` and `A<T> NotImplemented`. Trying to
+// use the impl itself to prove that adds region constraints as we uniquified the
+// regions in the `A<T>: Trait<'a, 'b>` where-bound. As both the impl above
+// and the impl below now apply with some constraints, we failed with ambiguity.
+impl<'a, 'b, T: ?Sized> Trait<'a, 'b> for B<T>
+where
+    A<T>: NotImplemented,
+{}
+
 // This impl directly requires 'b to be equal to 'static.
 //
 // Because of the coinductive cycle through `C<T>` it also requires
diff --git a/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.stderr b/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.stderr
index 7b3075f4ff3..0cbd9654044 100644
--- a/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.stderr
+++ b/tests/ui/traits/next-solver/cycles/fixpoint-rerun-all-cycle-heads.stderr
@@ -1,5 +1,5 @@
 error: lifetime may not live long enough
-  --> $DIR/fixpoint-rerun-all-cycle-heads.rs:47:5
+  --> $DIR/fixpoint-rerun-all-cycle-heads.rs:59:5
    |
 LL | fn check<'a, T: ?Sized>() {
    |          -- lifetime `'a` defined here
diff --git a/tests/ui/traits/next-solver/env-shadows-impls/ambig-env-no-shadow.rs b/tests/ui/traits/next-solver/env-shadows-impls/ambig-env-no-shadow.rs
new file mode 100644
index 00000000000..37730d38c7a
--- /dev/null
+++ b/tests/ui/traits/next-solver/env-shadows-impls/ambig-env-no-shadow.rs
@@ -0,0 +1,40 @@
+// compile-flags: -Znext-solver
+// check-pass
+
+// If a trait goal is proven using the environment, we discard
+// impl candidates when normalizing. However, in this example
+// the env candidates start as ambiguous and end up not applying,
+// so normalization should succeed later on.
+
+trait Trait<T>: Sized {
+    type Assoc: From<Self>;
+}
+
+impl<T, U> Trait<U> for T {
+    type Assoc = T;
+}
+
+fn mk_assoc<T: Trait<U>, U>(t: T, _: U) -> <T as Trait<U>>::Assoc {
+    t.into()
+}
+
+fn generic<T>(t: T) -> T
+where
+    T: Trait<u32>,
+    T: Trait<i16>,
+{
+    let u = Default::default();
+
+    // at this point we have 2 ambig env candidates
+    let ret: T = mk_assoc(t, u);
+
+    // now both env candidates don't apply, so we're now able to
+    // normalize using this impl candidates. For this to work
+    // the normalizes-to must have remained ambiguous above.
+    let _: u8 = u;
+    ret
+}
+
+fn main() {
+    assert_eq!(generic(1), 1);
+}
diff --git a/tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-1.rs b/tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-1.rs
new file mode 100644
index 00000000000..63742d0d1a1
--- /dev/null
+++ b/tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-1.rs
@@ -0,0 +1,30 @@
+// compile-flags: -Znext-solver
+// check-pass
+
+// Normalizing `<T as Trait>::TraitAssoc` in the elaborated environment
+// `[T: Trait, T: Super, <T as Super>::SuperAssoc = <T as Trait>::TraitAssoc]`
+// has a single impl candidate, which uses the environment to
+// normalize `<T as Trait>::TraitAssoc` to itself. We avoid this overflow
+// by discarding impl candidates the trait bound is proven by a where-clause.
+
+// https://github.com/rust-lang/trait-system-refactor-initiative/issues/76
+trait Super {
+    type SuperAssoc;
+}
+
+trait Trait: Super<SuperAssoc = Self::TraitAssoc> {
+    type TraitAssoc;
+}
+
+impl<T, U> Trait for T
+where
+    T: Super<SuperAssoc = U>,
+{
+    type TraitAssoc = U;
+}
+
+fn overflow<T: Trait>() {
+    let x: <T as Trait>::TraitAssoc;
+}
+
+fn main() {}
diff --git a/tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-2.rs b/tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-2.rs
new file mode 100644
index 00000000000..b0ef0d44baf
--- /dev/null
+++ b/tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-2.rs
@@ -0,0 +1,29 @@
+// revisions: next current
+//[next] compile-flags: -Znext-solver
+// check-pass
+
+#![allow(warnings)]
+trait Trait<U> {
+    type Assoc;
+}
+
+impl<T> Trait<u64> for T {
+    type Assoc = T;
+}
+
+fn lazy_init<T: Trait<U>, U>() -> (T, <T as Trait<U>>::Assoc) {
+    todo!()
+}
+
+fn foo<T: Trait<u32, Assoc = T>>(x: T) {
+    // When considering impl candidates to be equally valid as env candidates
+    // this ends up being ambiguous as `U` can be both `u32ยด and `u64` here.
+    //
+    // This is acceptable breakage but we should still note that it's
+    // theoretically breaking.
+    let (delayed, mut proj) = lazy_init::<_, _>();
+    proj = x;
+    let _: T = delayed;
+}
+
+fn main() {}
diff --git a/tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-3.rs b/tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-3.rs
new file mode 100644
index 00000000000..807e19a4a58
--- /dev/null
+++ b/tests/ui/traits/next-solver/env-shadows-impls/discard-impls-shadowed-by-env-3.rs
@@ -0,0 +1,18 @@
+// compile-flags: -Znext-solver
+// check-pass
+
+// If we normalize using the impl here the constraints from normalization and
+// trait goals can differ. This is especially bad if normalization results
+// in stronger constraints.
+trait Trait<'a> {
+    type Assoc;
+}
+
+impl<T> Trait<'static> for T {
+    type Assoc = ();
+}
+
+// normalizing requires `'a == 'static`, the trait bound does not.
+fn foo<'a, T: Trait<'a>>(_: T::Assoc) {}
+
+fn main() {}
diff --git a/tests/ui/traits/next-solver/env-shadows-impls/normalizes_to_ignores_unnormalizable_candidate.rs b/tests/ui/traits/next-solver/env-shadows-impls/normalizes_to_ignores_unnormalizable_candidate.rs
new file mode 100644
index 00000000000..af2c44ea233
--- /dev/null
+++ b/tests/ui/traits/next-solver/env-shadows-impls/normalizes_to_ignores_unnormalizable_candidate.rs
@@ -0,0 +1,29 @@
+// compile-flags: -Znext-solver
+
+// Checks whether the new solver is smart enough to infer `?0 = U` when solving:
+// `normalizes-to(<Vec<?0> as Trait>::Assoc, u8)`
+// with `normalizes-to(<Vec<U> as Trait>::Assoc, u8)` in the paramenv even when
+// there is a separate `Vec<T>: Trait` bound  in the paramenv.
+//
+// We currently intentionally do not guide inference this way.
+
+trait Trait {
+    type Assoc;
+}
+
+fn foo<T: Trait<Assoc = u8>>(x: T) {}
+
+fn unconstrained<T>() -> Vec<T> {
+    todo!()
+}
+
+fn bar<T, U>()
+where
+    Vec<T>: Trait,
+    Vec<U>: Trait<Assoc = u8>,
+{
+    foo(unconstrained())
+    //~^ ERROR type annotations needed
+}
+
+fn main() {}
diff --git a/tests/ui/traits/next-solver/normalizes_to_ignores_unnormalizable_candidate.self_infer.stderr b/tests/ui/traits/next-solver/env-shadows-impls/normalizes_to_ignores_unnormalizable_candidate.stderr
index c1a8b74df08..36d281e11dd 100644
--- a/tests/ui/traits/next-solver/normalizes_to_ignores_unnormalizable_candidate.self_infer.stderr
+++ b/tests/ui/traits/next-solver/env-shadows-impls/normalizes_to_ignores_unnormalizable_candidate.stderr
@@ -1,21 +1,21 @@
 error[E0283]: type annotations needed
-  --> $DIR/normalizes_to_ignores_unnormalizable_candidate.rs:36:5
+  --> $DIR/normalizes_to_ignores_unnormalizable_candidate.rs:25:5
    |
 LL |     foo(unconstrained())
    |     ^^^ --------------- type must be known at this point
    |     |
    |     cannot infer type of the type parameter `T` declared on the function `foo`
    |
-   = note: cannot satisfy `_: Trait`
+   = note: cannot satisfy `Vec<_>: Trait`
 note: required by a bound in `foo`
-  --> $DIR/normalizes_to_ignores_unnormalizable_candidate.rs:19:11
+  --> $DIR/normalizes_to_ignores_unnormalizable_candidate.rs:14:11
    |
 LL | fn foo<T: Trait<Assoc = u8>>(x: T) {}
    |           ^^^^^^^^^^^^^^^^^ required by this bound in `foo`
 help: consider specifying the generic argument
    |
-LL |     foo::<T>(unconstrained())
-   |        +++++
+LL |     foo::<Vec<T>>(unconstrained())
+   |        ++++++++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.rs b/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.rs
new file mode 100644
index 00000000000..5989e605bd9
--- /dev/null
+++ b/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.rs
@@ -0,0 +1,31 @@
+// compile-flags: -Znext-solver
+
+trait Foo {
+    type Assoc;
+}
+
+trait Bar {}
+
+impl<T> Foo for T {
+    type Assoc = i32;
+}
+
+impl<T> Bar for T where T: Foo<Assoc = i32> {}
+
+fn require_bar<T: Bar>() {}
+
+fn foo<T: Foo>() {
+    // Unlike the classic solver, the new solver previously projected
+    // `<T as Foo>::Assoc = _` down to `i32` even though there's a param-env
+    // candidate here, since we don't assemble any param-env projection
+    // candidates for `T: Foo` alone.
+    //
+    // However, allowing impl candidates shadowed by env candidates results
+    // in multiple issues, so we explicitly hide them, e.g.
+    //
+    //     https://github.com/rust-lang/trait-system-refactor-initiative/issues/76
+    require_bar::<T>();
+    //~^ ERROR the trait bound `T: Bar` is not satisfied
+}
+
+fn main() {}
diff --git a/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.stderr b/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.stderr
new file mode 100644
index 00000000000..2785357e792
--- /dev/null
+++ b/tests/ui/traits/next-solver/env-shadows-impls/param-candidate-shadows-project.stderr
@@ -0,0 +1,19 @@
+error[E0277]: the trait bound `T: Bar` is not satisfied
+  --> $DIR/param-candidate-shadows-project.rs:27:19
+   |
+LL |     require_bar::<T>();
+   |                   ^ the trait `Bar` is not implemented for `T`
+   |
+note: required by a bound in `require_bar`
+  --> $DIR/param-candidate-shadows-project.rs:15:19
+   |
+LL | fn require_bar<T: Bar>() {}
+   |                   ^^^ required by this bound in `require_bar`
+help: consider further restricting this bound
+   |
+LL | fn foo<T: Foo + Bar>() {
+   |               +++++
+
+error: aborting due to 1 previous error
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/next-solver/generalize/occurs-check-nested-alias.next.stderr b/tests/ui/traits/next-solver/generalize/occurs-check-nested-alias.next.stderr
index ad8b24a39c7..aaadf604a80 100644
--- a/tests/ui/traits/next-solver/generalize/occurs-check-nested-alias.next.stderr
+++ b/tests/ui/traits/next-solver/generalize/occurs-check-nested-alias.next.stderr
@@ -1,11 +1,9 @@
-error[E0275]: overflow evaluating the requirement `<<T as Id<_>>::Id as Unnormalizable>::Assoc == _`
+error[E0284]: type annotations needed: cannot satisfy `<<T as Id<_>>::Id as Unnormalizable>::Assoc == _`
   --> $DIR/occurs-check-nested-alias.rs:36:9
    |
 LL |     x = y;
-   |         ^
-   |
-   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`occurs_check_nested_alias`)
+   |         ^ cannot satisfy `<<T as Id<_>>::Id as Unnormalizable>::Assoc == _`
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0275`.
+For more information about this error, try `rustc --explain E0284`.
diff --git a/tests/ui/traits/next-solver/normalize-param-env-2.rs b/tests/ui/traits/next-solver/normalize-param-env-2.rs
index ce084651bfb..9da1f8dbec1 100644
--- a/tests/ui/traits/next-solver/normalize-param-env-2.rs
+++ b/tests/ui/traits/next-solver/normalize-param-env-2.rs
@@ -1,24 +1,29 @@
-// check-pass
 // compile-flags: -Znext-solver
-// Issue 92505
+// known-bug: #92505
 
+// When checking that the impl method where-bounds are implied by the trait,
+// we prove  `<() as A<T>>::Assoc: A<T>` in the environment `<() as A<T>>::Assoc: A<T>`.
+//
+// Normalizing `<() as A<T>>::Assoc` is ambiguous in that environment. The
+// where-bound `<() as A<T>>::Assoc: A<T>` may apply, resulting in overflow.
 trait A<T> {
-    type I;
+    type Assoc;
 
     fn f()
     where
-        Self::I: A<T>,
+        Self::Assoc: A<T>,
     {
     }
 }
 
 impl<T> A<T> for () {
-    type I = ();
+    type Assoc = ();
 
     fn f()
     where
-        Self::I: A<T>,
+        Self::Assoc: A<T>,
     {
+
         <() as A<T>>::f();
     }
 }
diff --git a/tests/ui/traits/next-solver/normalize-param-env-2.stderr b/tests/ui/traits/next-solver/normalize-param-env-2.stderr
new file mode 100644
index 00000000000..a52022e539e
--- /dev/null
+++ b/tests/ui/traits/next-solver/normalize-param-env-2.stderr
@@ -0,0 +1,35 @@
+error[E0275]: overflow evaluating the requirement `<() as A<T>>::Assoc: A<T>`
+  --> $DIR/normalize-param-env-2.rs:24:22
+   |
+LL |         Self::Assoc: A<T>,
+   |                      ^^^^
+   |
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_2`)
+note: the requirement `<() as A<T>>::Assoc: A<T>` appears on the `impl`'s method `f` but not on the corresponding trait's method
+  --> $DIR/normalize-param-env-2.rs:12:8
+   |
+LL | trait A<T> {
+   |       - in this trait
+...
+LL |     fn f()
+   |        ^ this trait's method doesn't have the requirement `<() as A<T>>::Assoc: A<T>`
+
+error[E0275]: overflow evaluating the requirement `<() as A<T>>::Assoc: A<T>`
+  --> $DIR/normalize-param-env-2.rs:24:22
+   |
+LL |         Self::Assoc: A<T>,
+   |                      ^^^^
+   |
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_2`)
+
+error[E0275]: overflow evaluating the requirement `<() as A<T>>::Assoc well-formed`
+  --> $DIR/normalize-param-env-2.rs:24:22
+   |
+LL |         Self::Assoc: A<T>,
+   |                      ^^^^
+   |
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_2`)
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/traits/next-solver/normalize-param-env-4.next.stderr b/tests/ui/traits/next-solver/normalize-param-env-4.next.stderr
new file mode 100644
index 00000000000..dec820c61b0
--- /dev/null
+++ b/tests/ui/traits/next-solver/normalize-param-env-4.next.stderr
@@ -0,0 +1,19 @@
+error[E0275]: overflow evaluating the requirement `<T as Trait>::Assoc: Trait`
+  --> $DIR/normalize-param-env-4.rs:18:26
+   |
+LL |     <T as Trait>::Assoc: Trait,
+   |                          ^^^^^
+   |
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_4`)
+
+error[E0275]: overflow evaluating the requirement `<T as Trait>::Assoc well-formed`
+  --> $DIR/normalize-param-env-4.rs:18:26
+   |
+LL |     <T as Trait>::Assoc: Trait,
+   |                          ^^^^^
+   |
+   = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`normalize_param_env_4`)
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0275`.
diff --git a/tests/ui/traits/next-solver/normalize-param-env-4.rs b/tests/ui/traits/next-solver/normalize-param-env-4.rs
new file mode 100644
index 00000000000..d49f7492297
--- /dev/null
+++ b/tests/ui/traits/next-solver/normalize-param-env-4.rs
@@ -0,0 +1,34 @@
+// revisions: current next
+//[next] compile-flags: -Znext-solver
+//[next] known-bug: #92505
+//[current] check-pass
+
+trait Trait {
+    type Assoc;
+}
+
+impl<T> Trait for T {
+    type Assoc = T;
+}
+
+fn impls_trait<T: Trait>() {}
+
+fn foo<T>()
+where
+    <T as Trait>::Assoc: Trait,
+{
+    // Trying to use `<T as Trait>::Assoc: Trait` to prove `T: Trait`
+    // requires normalizing `<T as Trait>::Assoc`. We do not normalize
+    // using impl candidates if there's a where-bound for that trait.
+    //
+    // We therefore check whether `T: Trait` is proven by the environment.
+    // For that we try to apply the `<T as Trait>::Assoc: Trait` candidate,
+    // trying to normalize its self type results in overflow.
+    //
+    // In the old solver we eagerly normalize the environment, ignoring the
+    // unnormalized `<T as Trait>::Assoc: Trait` where-bound when normalizing
+    // `<T as Trait>::Asosc`
+    impls_trait::<T>();
+}
+
+fn main() {}
diff --git a/tests/ui/traits/next-solver/normalizes_to_ignores_unnormalizable_candidate.rs b/tests/ui/traits/next-solver/normalizes_to_ignores_unnormalizable_candidate.rs
deleted file mode 100644
index 7dc87daccd9..00000000000
--- a/tests/ui/traits/next-solver/normalizes_to_ignores_unnormalizable_candidate.rs
+++ /dev/null
@@ -1,40 +0,0 @@
-// [no_self_infer] check-pass
-// compile-flags: -Znext-solver
-// revisions: self_infer no_self_infer
-
-// checks that the new solver is smart enough to infer `?0 = U` when solving:
-// `normalizes-to(<Vec<?0> as Trait>::Assoc, u8)`
-// with `normalizes-to(<Vec<U> as Trait>::Assoc, u8)` in the paramenv even when
-// there is a separate `Vec<T>: Trait` bound  in the paramenv.
-//
-// FIXME(-Znext-solver)
-// This could also compile for `normalizes-to(<?0 as Trait>::Assoc, u8)` but
-// we currently immediately consider a goal ambiguous if the self type is an
-// inference variable.
-
-trait Trait {
-    type Assoc;
-}
-
-fn foo<T: Trait<Assoc = u8>>(x: T) {}
-
-#[cfg(self_infer)]
-fn unconstrained<T>() -> T {
-    todo!()
-}
-
-#[cfg(no_self_infer)]
-fn unconstrained<T>() -> Vec<T> {
-    todo!()
-}
-
-fn bar<T, U>()
-where
-    Vec<T>: Trait,
-    Vec<U>: Trait<Assoc = u8>,
-{
-    foo(unconstrained())
-    //[self_infer]~^ ERROR type annotations needed
-}
-
-fn main() {}
diff --git a/tests/ui/traits/next-solver/param-candidate-doesnt-shadow-project.rs b/tests/ui/traits/next-solver/param-candidate-doesnt-shadow-project.rs
deleted file mode 100644
index f67b073c53c..00000000000
--- a/tests/ui/traits/next-solver/param-candidate-doesnt-shadow-project.rs
+++ /dev/null
@@ -1,25 +0,0 @@
-// compile-flags: -Znext-solver
-// check-pass
-
-trait Foo {
-    type Assoc;
-}
-
-trait Bar {}
-
-impl<T> Foo for T {
-    type Assoc = i32;
-}
-
-impl<T> Bar for T where T: Foo<Assoc = i32> {}
-
-fn require_bar<T: Bar>() {}
-
-fn foo<T: Foo>() {
-    // Unlike the classic solver, `<T as Foo>::Assoc = _` will still project
-    // down to `i32` even though there's a param-env candidate here, since we
-    // don't assemble any param-env projection candidates for `T: Foo` alone.
-    require_bar::<T>();
-}
-
-fn main() {}
diff --git a/tests/ui/traits/pointee-normalize-equate.rs b/tests/ui/traits/pointee-normalize-equate.rs
new file mode 100644
index 00000000000..2e75933aca0
--- /dev/null
+++ b/tests/ui/traits/pointee-normalize-equate.rs
@@ -0,0 +1,56 @@
+// check-pass
+// revisions: old next
+//[next] compile-flags: -Znext-solver
+
+#![feature(ptr_metadata)]
+
+use std::ptr::{self, Pointee};
+
+fn cast_same_meta<T: ?Sized, U: ?Sized>(ptr: *const T) -> *const U
+where
+    T: Pointee<Metadata = <U as Pointee>::Metadata>,
+{
+    let (thin, meta) = ptr.to_raw_parts();
+    ptr::from_raw_parts(thin, meta)
+}
+
+struct Wrapper<T: ?Sized>(T);
+
+// normalize `Wrapper<T>::Metadata` -> `T::Metadata`
+fn wrapper_to_tail<T: ?Sized>(ptr: *const T) -> *const Wrapper<T> {
+    cast_same_meta(ptr)
+}
+
+// normalize `Wrapper<T>::Metadata` -> `T::Metadata` -> `()`
+fn wrapper_to_unit<T>(ptr: *const ()) -> *const Wrapper<T> {
+    cast_same_meta(ptr)
+}
+
+trait Project {
+    type Assoc: ?Sized;
+}
+
+struct WrapperProject<T: ?Sized + Project>(T::Assoc);
+
+// normalize `WrapperProject<T>::Metadata` -> `T::Assoc::Metadata`
+fn wrapper_project_tail<T: ?Sized + Project>(ptr: *const T::Assoc) -> *const WrapperProject<T> {
+    cast_same_meta(ptr)
+}
+
+// normalize `WrapperProject<T>::Metadata` -> `T::Assoc::Metadata` -> `()`
+fn wrapper_project_unit<T: ?Sized + Project>(ptr: *const ()) -> *const WrapperProject<T>
+where
+    T::Assoc: Sized,
+{
+    cast_same_meta(ptr)
+}
+
+// normalize `<[T] as Pointee>::Metadata` -> `usize`, even if `[T]: Sized`
+fn sized_slice<T>(ptr: *const [T]) -> *const str
+where
+    [T]: Sized,
+{
+    cast_same_meta(ptr)
+}
+
+fn main() {}