about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorbors <bors@rust-lang.org>2024-07-16 13:38:39 +0000
committerbors <bors@rust-lang.org>2024-07-16 13:38:39 +0000
commit216bef36bccb67bcc4c0a4d7940f6dc84c4fac7b (patch)
tree11e65227b6348995609411f581e100f97e9475b6 /src
parente5b1a2b3cd1e3a53b939e81a3eef1f4da924eb0f (diff)
parent137c736d667dd500ddac47450df21ae9be345ba0 (diff)
Auto merge of #17609 - lnicola:sync-from-rust, r=lnicola
minor: Sync from downstream
Diffstat (limited to 'src')
-rw-r--r--src/bootstrap/Cargo.lock1
-rw-r--r--src/bootstrap/Cargo.toml1
-rw-r--r--src/bootstrap/README.md18
-rw-r--r--src/bootstrap/bootstrap.py2
-rw-r--r--src/bootstrap/mk/Makefile.in9
-rw-r--r--src/bootstrap/src/bin/main.rs2
-rw-r--r--src/bootstrap/src/core/build_steps/compile.rs46
-rw-r--r--src/bootstrap/src/core/build_steps/dist.rs12
-rw-r--r--src/bootstrap/src/core/build_steps/doc.rs15
-rw-r--r--src/bootstrap/src/core/build_steps/llvm.rs7
-rw-r--r--src/bootstrap/src/core/build_steps/setup.rs60
-rw-r--r--src/bootstrap/src/core/build_steps/test.rs89
-rw-r--r--src/bootstrap/src/core/build_steps/toolstate.rs95
-rw-r--r--src/bootstrap/src/core/builder.rs14
-rw-r--r--src/bootstrap/src/core/builder/tests.rs2
-rw-r--r--src/bootstrap/src/core/config/config.rs169
-rw-r--r--src/bootstrap/src/core/config/flags.rs2
-rw-r--r--src/bootstrap/src/core/download.rs8
-rw-r--r--src/bootstrap/src/core/sanity.rs4
-rw-r--r--src/bootstrap/src/lib.rs114
-rw-r--r--src/bootstrap/src/utils/channel.rs11
-rw-r--r--src/bootstrap/src/utils/exec.rs59
-rw-r--r--src/bootstrap/src/utils/helpers.rs11
-rw-r--r--src/bootstrap/src/utils/render_tests.rs3
-rw-r--r--src/bootstrap/src/utils/tarball.rs14
-rw-r--r--src/ci/docker/host-x86_64/dist-riscv64-linux/Dockerfile1
-rw-r--r--src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0001-divdi3-div-zero.patch37
-rw-r--r--src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0002-hidden-jump-target.patch117
-rw-r--r--src/ci/docker/host-x86_64/dist-riscv64-linux/patches/glibc/2.29/0001-hidden-jump-target.patch58
-rw-r--r--src/ci/docker/host-x86_64/dist-riscv64-linux/riscv64-unknown-linux-gnu.defconfig4
-rwxr-xr-xsrc/ci/scripts/install-clang.sh2
m---------src/doc/book0
m---------src/doc/edition-guide0
m---------src/doc/embedded-book0
m---------src/doc/reference0
m---------src/doc/rust-by-example0
m---------src/doc/rustc-dev-guide0
-rw-r--r--src/doc/rustc/src/platform-support.md6
-rw-r--r--src/doc/rustc/src/platform-support/x86_64-fortanix-unknown-sgx.md2
-rw-r--r--src/librustdoc/clean/mod.rs9
-rw-r--r--src/librustdoc/clean/simplify.rs2
-rw-r--r--src/librustdoc/clean/types.rs2
-rw-r--r--src/librustdoc/html/format.rs14
-rw-r--r--src/librustdoc/html/render/search_index.rs6
-rw-r--r--src/librustdoc/json/conversions.rs1
-rw-r--r--src/rustdoc-json-types/lib.rs4
-rw-r--r--src/tools/build_helper/src/ci.rs14
-rw-r--r--src/tools/build_helper/src/drop_bomb/mod.rs (renamed from src/tools/run-make-support/src/drop_bomb/mod.rs)20
-rw-r--r--src/tools/build_helper/src/drop_bomb/tests.rs (renamed from src/tools/run-make-support/src/drop_bomb/tests.rs)0
-rw-r--r--src/tools/build_helper/src/lib.rs1
-rw-r--r--src/tools/clippy/.github/workflows/lintcheck.yml4
-rw-r--r--src/tools/clippy/CHANGELOG.md6
-rw-r--r--src/tools/clippy/book/src/lint_configuration.md3
-rw-r--r--src/tools/clippy/clippy.toml4
-rw-r--r--src/tools/clippy/clippy_config/src/conf.rs3
-rw-r--r--src/tools/clippy/clippy_config/src/msrvs.rs4
-rw-r--r--src/tools/clippy/clippy_dev/src/new_lint.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/almost_complete_range.rs104
-rw-r--r--src/tools/clippy/clippy_lints/src/assertions_on_constants.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/assigning_clones.rs496
-rw-r--r--src/tools/clippy/clippy_lints/src/await_holding_invalid.rs46
-rw-r--r--src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs134
-rw-r--r--src/tools/clippy/clippy_lints/src/byte_char_slices.rs80
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/mod.rs113
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/cfg_not_test.rs60
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/default.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs37
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_methods.rs42
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_names.rs46
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs41
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/mod.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/endian_bytes.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/incompatible_msrv.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/inherent_to_string.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/init_numbered_fields.rs87
-rw-r--r--src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs41
-rw-r--r--src/tools/clippy/clippy_lints/src/instant_subtraction.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/items_after_statements.rs59
-rw-r--r--src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs21
-rw-r--r--src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/large_const_arrays.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/large_enum_variant.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/large_futures.rs43
-rw-r--r--src/tools/clippy/clippy_lints/src/large_include_file.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs46
-rw-r--r--src/tools/clippy/clippy_lints/src/len_zero.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/let_if_seq.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/let_underscore.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs24
-rw-r--r--src/tools/clippy/clippy_lints/src/literal_representation.rs16
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_bits.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_float_methods.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_let_else.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_range_patterns.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs49
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_retain.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_rotate.rs117
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_strip.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/map_unit_fn.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/mod.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs111
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs70
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/minmax.rs37
-rw-r--r--src/tools/clippy/clippy_lints/src/misc.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/misc_early/mod.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_assert_message.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs33
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs (renamed from src/tools/clippy/clippy_lints/src/thread_local_initializer_can_be_made_const.rs)14
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/no_effect.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/float_cmp.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/mod.rs140
-rw-r--r--src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs70
-rw-r--r--src/tools/clippy/clippy_lints/src/panicking_overflow_checks.rs86
-rw-r--r--src/tools/clippy/clippy_lints/src/renamed_lints.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/returns.rs47
-rw-r--r--src/tools/clippy/clippy_lints/src/same_name_method.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs125
-rw-r--r--src/tools/clippy/clippy_lints/src/trait_bounds.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/types/type_complexity.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints.rs1
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs73
-rw-r--r--src/tools/clippy/clippy_lints/src/wildcard_imports.rs19
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs5
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs67
-rw-r--r--src/tools/clippy/clippy_utils/src/paths.rs1
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs26
-rw-r--r--src/tools/clippy/lintcheck/Cargo.toml1
-rw-r--r--src/tools/clippy/lintcheck/README.md16
-rw-r--r--src/tools/clippy/lintcheck/lintcheck_crates.toml52
-rw-r--r--src/tools/clippy/lintcheck/src/config.rs4
-rw-r--r--src/tools/clippy/lintcheck/src/driver.rs2
-rw-r--r--src/tools/clippy/lintcheck/src/input.rs288
-rw-r--r--src/tools/clippy/lintcheck/src/json.rs99
-rw-r--r--src/tools/clippy/lintcheck/src/main.rs595
-rw-r--r--src/tools/clippy/lintcheck/src/output.rs235
-rw-r--r--src/tools/clippy/lintcheck/src/popular_crates.rs2
-rw-r--r--src/tools/clippy/lintcheck/src/recursive.rs7
-rw-r--r--src/tools/clippy/rust-toolchain3
-rw-r--r--src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr16
-rw-r--r--src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr6
-rw-r--r--src/tools/clippy/tests/ui-toml/needless_pass_by_ref_mut/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.fixed10
-rw-r--r--src/tools/clippy/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.rs10
-rw-r--r--src/tools/clippy/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.stderr12
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr36
-rw-r--r--src/tools/clippy/tests/ui/assertions_on_constants.rs11
-rw-r--r--src/tools/clippy/tests/ui/assertions_on_constants.stderr18
-rw-r--r--src/tools/clippy/tests/ui/await_holding_lock.rs26
-rw-r--r--src/tools/clippy/tests/ui/await_holding_lock.stderr78
-rw-r--r--src/tools/clippy/tests/ui/await_holding_refcell_ref.rs12
-rw-r--r--src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr24
-rw-r--r--src/tools/clippy/tests/ui/byte_char_slices.fixed13
-rw-r--r--src/tools/clippy/tests/ui/byte_char_slices.rs13
-rw-r--r--src/tools/clippy/tests/ui/byte_char_slices.stderr38
-rw-r--r--src/tools/clippy/tests/ui/cfg_not_test.rs32
-rw-r--r--src/tools/clippy/tests/ui/cfg_not_test.stderr45
-rw-r--r--src/tools/clippy/tests/ui/crashes/ice-12616.stderr2
-rw-r--r--src/tools/clippy/tests/ui/disallowed_names.rs1
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.fixed47
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.rs43
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.stderr56
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.fixed12
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.rs12
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.stderr12
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed31
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_lazy_list.rs22
-rw-r--r--src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr49
-rw-r--r--src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs17
-rw-r--r--src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr18
-rw-r--r--src/tools/clippy/tests/ui/explicit_auto_deref.fixed36
-rw-r--r--src/tools/clippy/tests/ui/explicit_auto_deref.rs36
-rw-r--r--src/tools/clippy/tests/ui/explicit_auto_deref.stderr8
-rw-r--r--src/tools/clippy/tests/ui/float_cmp.rs6
-rw-r--r--src/tools/clippy/tests/ui/float_cmp.stderr21
-rw-r--r--src/tools/clippy/tests/ui/float_cmp_const.rs8
-rw-r--r--src/tools/clippy/tests/ui/float_cmp_const.stderr29
-rw-r--r--src/tools/clippy/tests/ui/init_numbered_fields.fixed (renamed from src/tools/clippy/tests/ui/numbered_fields.fixed)9
-rw-r--r--src/tools/clippy/tests/ui/init_numbered_fields.rs (renamed from src/tools/clippy/tests/ui/numbered_fields.rs)9
-rw-r--r--src/tools/clippy/tests/ui/init_numbered_fields.stderr (renamed from src/tools/clippy/tests/ui/numbered_fields.stderr)12
-rw-r--r--src/tools/clippy/tests/ui/into_iter_without_iter.rs39
-rw-r--r--src/tools/clippy/tests/ui/iter_next_loop.rs2
-rw-r--r--src/tools/clippy/tests/ui/iter_next_loop.stderr10
-rw-r--r--src/tools/clippy/tests/ui/manual_inspect.stderr24
-rw-r--r--src/tools/clippy/tests/ui/manual_rotate.fixed31
-rw-r--r--src/tools/clippy/tests/ui/manual_rotate.rs31
-rw-r--r--src/tools/clippy/tests/ui/manual_rotate.stderr71
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs18
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed30
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs30
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr41
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.fixed14
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.rs14
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.stderr59
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_thread_local.fixed (renamed from src/tools/clippy/tests/ui/thread_local_initializer_can_be_made_const.fixed)2
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_thread_local.rs (renamed from src/tools/clippy/tests/ui/thread_local_initializer_can_be_made_const.rs)2
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_thread_local.stderr (renamed from src/tools/clippy/tests/ui/thread_local_initializer_can_be_made_const.stderr)16
-rw-r--r--src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs18
-rw-r--r--src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr52
-rw-r--r--src/tools/clippy/tests/ui/needless_pass_by_ref_mut2.fixed4
-rw-r--r--src/tools/clippy/tests/ui/needless_pass_by_ref_mut2.rs4
-rw-r--r--src/tools/clippy/tests/ui/needless_pass_by_ref_mut2.stderr15
-rw-r--r--src/tools/clippy/tests/ui/needless_return.fixed35
-rw-r--r--src/tools/clippy/tests/ui/needless_return.rs35
-rw-r--r--src/tools/clippy/tests/ui/needless_return.stderr28
-rw-r--r--src/tools/clippy/tests/ui/overflow_check_conditional.rs36
-rw-r--r--src/tools/clippy/tests/ui/overflow_check_conditional.stderr53
-rw-r--r--src/tools/clippy/tests/ui/panicking_overflow_checks.rs27
-rw-r--r--src/tools/clippy/tests/ui/panicking_overflow_checks.stderr41
-rw-r--r--src/tools/clippy/tests/ui/ptr_as_ptr.stderr66
-rw-r--r--src/tools/clippy/tests/ui/rename.fixed4
-rw-r--r--src/tools/clippy/tests/ui/rename.rs4
-rw-r--r--src/tools/clippy/tests/ui/rename.stderr130
-rw-r--r--src/tools/clippy/tests/ui/set_contains_or_insert.rs83
-rw-r--r--src/tools/clippy/tests/ui/set_contains_or_insert.stderr61
-rw-r--r--src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs26
-rw-r--r--src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr29
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.fixed14
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.rs14
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_operation.stderr8
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.fixed19
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.rs19
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_to_owned.stderr60
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports.fixed3
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports.rs3
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports.stderr10
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed3
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr10
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed3
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr10
-rw-r--r--src/tools/clippy/tests/ui/wildcard_imports_2021.rs3
-rw-r--r--src/tools/compiletest/src/header.rs18
-rw-r--r--src/tools/jsondoclint/src/validator.rs1
-rw-r--r--src/tools/lint-docs/src/groups.rs2
-rw-r--r--src/tools/lint-docs/src/lib.rs2
-rw-r--r--src/tools/miri/cargo-miri/src/phases.rs26
-rwxr-xr-xsrc/tools/miri/ci/ci.sh4
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/src/bin/miri.rs3
-rw-r--r--src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs12
-rw-r--r--src/tools/miri/src/concurrency/cpu_affinity.rs90
-rw-r--r--src/tools/miri/src/concurrency/mod.rs1
-rw-r--r--src/tools/miri/src/concurrency/sync.rs21
-rw-r--r--src/tools/miri/src/concurrency/thread.rs5
-rw-r--r--src/tools/miri/src/eval.rs3
-rw-r--r--src/tools/miri/src/helpers.rs55
-rw-r--r--src/tools/miri/src/intrinsics/mod.rs4
-rw-r--r--src/tools/miri/src/lib.rs1
-rw-r--r--src/tools/miri/src/machine.rs23
-rw-r--r--src/tools/miri/src/provenance_gc.rs11
-rw-r--r--src/tools/miri/src/shims/tls.rs65
-rw-r--r--src/tools/miri/src/shims/unix/fd.rs7
-rw-r--r--src/tools/miri/src/shims/unix/foreign_items.rs95
-rw-r--r--src/tools/miri/src/shims/unix/linux/foreign_items.rs13
-rw-r--r--src/tools/miri/src/shims/unix/macos/foreign_items.rs24
-rw-r--r--src/tools/miri/src/shims/unix/macos/mod.rs1
-rw-r--r--src/tools/miri/src/shims/unix/macos/sync.rs107
-rw-r--r--src/tools/miri/src/shims/unix/sync.rs2
-rwxr-xr-xsrc/tools/miri/test-cargo-miri/run-test.py20
-rw-r--r--src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref22
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_assert_not_owner.rs13
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_assert_not_owner.stderr13
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_assert_owner.rs12
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_assert_owner.stderr13
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_reentrant.rs13
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_reentrant.stderr13
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_unowned.rs12
-rw-r--r--src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_unowned.stderr13
-rw-r--r--src/tools/miri/tests/fail-dep/libc/affinity.rs17
-rw-r--r--src/tools/miri/tests/fail-dep/libc/affinity.stderr20
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs27
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.stderr15
-rw-r--r--src/tools/miri/tests/fail/alloc/global_system_mixup.rs2
-rw-r--r--src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.rs20
-rw-r--r--src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.stderr20
-rw-r--r--src/tools/miri/tests/pass-dep/concurrency/apple-os-unfair-lock.rs25
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-affinity.rs218
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-fs.rs35
-rw-r--r--src/tools/miri/tests/pass/float.rs642
-rw-r--r--src/tools/miri/tests/pass/tls/macos_tlv_atexit.rs43
-rw-r--r--src/tools/miri/tests/pass/tls/macos_tlv_atexit.stdout7
-rw-r--r--src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs3
-rw-r--r--src/tools/run-make-support/Cargo.toml2
-rw-r--r--src/tools/run-make-support/src/command.rs2
-rw-r--r--src/tools/run-make-support/src/diff/mod.rs2
-rw-r--r--src/tools/run-make-support/src/lib.rs6
-rw-r--r--src/tools/run-make-support/src/rustc.rs9
-rw-r--r--src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs5
-rw-r--r--src/tools/rust-analyzer/crates/rust-analyzer/src/version.rs2
-rw-r--r--src/tools/rust-analyzer/rust-version2
-rw-r--r--src/tools/rust-installer/src/combiner.rs2
-rwxr-xr-xsrc/tools/rust-installer/test.sh2
-rw-r--r--src/tools/tidy/src/allowed_run_make_makefiles.txt5
-rw-r--r--src/tools/tidy/src/deps.rs1
307 files changed, 6470 insertions, 3141 deletions
diff --git a/src/bootstrap/Cargo.lock b/src/bootstrap/Cargo.lock
index c095127da14..de0924c0f42 100644
--- a/src/bootstrap/Cargo.lock
+++ b/src/bootstrap/Cargo.lock
@@ -48,7 +48,6 @@ dependencies = [
  "clap_complete",
  "cmake",
  "fd-lock",
- "filetime",
  "home",
  "ignore",
  "junction",
diff --git a/src/bootstrap/Cargo.toml b/src/bootstrap/Cargo.toml
index df7b5b88193..f723407c3ce 100644
--- a/src/bootstrap/Cargo.toml
+++ b/src/bootstrap/Cargo.toml
@@ -44,7 +44,6 @@ build_helper = { path = "../tools/build_helper" }
 clap = { version = "4.4", default-features = false, features = ["std", "usage", "help", "derive", "error-context"] }
 clap_complete = "4.4"
 fd-lock = "4.0"
-filetime = "0.2"
 home = "0.5"
 ignore = "0.4"
 libc = "0.2"
diff --git a/src/bootstrap/README.md b/src/bootstrap/README.md
index fb3c8627043..0ac58645d2d 100644
--- a/src/bootstrap/README.md
+++ b/src/bootstrap/README.md
@@ -1,7 +1,7 @@
-# rustbuild - Bootstrapping Rust
+# Bootstrapping Rust
 
 This README is aimed at helping to explain how Rust is bootstrapped,
-and some of the technical details of the build system.
+and some of the technical details of the bootstrap build system.
 
 Note that this README only covers internal information, not how to use the tool.
 Please check [bootstrapping dev guide][bootstrapping-dev-guide] for further information.
@@ -12,17 +12,17 @@ Please check [bootstrapping dev guide][bootstrapping-dev-guide] for further info
 
 The build system defers most of the complicated logic of managing invocations
 of rustc and rustdoc to Cargo itself. However, moving through various stages
-and copying artifacts is still necessary for it to do. Each time rustbuild
+and copying artifacts is still necessary for it to do. Each time bootstrap
 is invoked, it will iterate through the list of predefined steps and execute
 each serially in turn if it matches the paths passed or is a default rule.
-For each step, rustbuild relies on the step internally being incremental and
-parallel. Note, though, that the `-j` parameter to rustbuild gets forwarded
+For each step, bootstrap relies on the step internally being incremental and
+parallel. Note, though, that the `-j` parameter to bootstrap gets forwarded
 to appropriate test harnesses and such.
 
 ## Build phases
 
-The rustbuild build system goes through a few phases to actually build the
-compiler. What actually happens when you invoke rustbuild is:
+Bootstrap build system goes through a few phases to actually build the
+compiler. What actually happens when you invoke bootstrap is:
 
 1. The entry point script (`x` for unix like systems, `x.ps1` for windows systems,
    `x.py` cross-platform) is run. This script is responsible for downloading the stage0
@@ -151,9 +151,9 @@ build/
     stage3/
 ```
 
-## Extending rustbuild
+## Extending bootstrap
 
-When you use the bootstrap system, you'll call it through the entry point script
+When you use bootstrap, you'll call it through the entry point script
 (`x`, `x.ps1`, or `x.py`). However, most of the code lives in `src/bootstrap`.
 `bootstrap` has a difficult problem: it is written in Rust, but yet it is run
 before the Rust compiler is built! To work around this, there are two components
diff --git a/src/bootstrap/bootstrap.py b/src/bootstrap/bootstrap.py
index 7e47b373ff9..4e8e0fd2532 100644
--- a/src/bootstrap/bootstrap.py
+++ b/src/bootstrap/bootstrap.py
@@ -1038,7 +1038,7 @@ class RustBuild(object):
 
     def check_vendored_status(self):
         """Check that vendoring is configured properly"""
-        # keep this consistent with the equivalent check in rustbuild:
+        # keep this consistent with the equivalent check in bootstrap:
         # https://github.com/rust-lang/rust/blob/a8a33cf27166d3eabaffc58ed3799e054af3b0c6/src/bootstrap/lib.rs#L399-L405
         if 'SUDO_USER' in os.environ and not self.use_vendored_sources:
             if os.getuid() == 0:
diff --git a/src/bootstrap/mk/Makefile.in b/src/bootstrap/mk/Makefile.in
index cab37e0da47..7e38a0996e5 100644
--- a/src/bootstrap/mk/Makefile.in
+++ b/src/bootstrap/mk/Makefile.in
@@ -20,7 +20,7 @@ all:
 	$(Q)$(BOOTSTRAP) doc --stage 2 $(BOOTSTRAP_ARGS)
 
 help:
-	$(Q)echo 'Welcome to the rustbuild build system!'
+	$(Q)echo 'Welcome to bootstrap, the Rust build system!'
 	$(Q)echo
 	$(Q)echo This makefile is a thin veneer over the ./x.py script located
 	$(Q)echo in this directory. To get the full power of the build system
@@ -58,9 +58,8 @@ check-aux:
 		library/core \
 		library/alloc \
 		--no-doc
-	# Some doctests have intentional memory leaks.
-	# Some use file system operations to demonstrate dealing with `Result`.
-	$(Q)MIRIFLAGS="-Zmiri-ignore-leaks -Zmiri-disable-isolation" \
+	# Some doctests use file system operations to demonstrate dealing with `Result`.
+	$(Q)MIRIFLAGS="-Zmiri-disable-isolation" \
 		$(BOOTSTRAP) miri --stage 2 \
 		library/core \
 		library/alloc \
@@ -70,7 +69,7 @@ check-aux:
 		$(BOOTSTRAP) miri --stage 2 library/std \
 		--no-doc -- \
 		--skip fs:: --skip net:: --skip process:: --skip sys::pal::
-	$(Q)MIRIFLAGS="-Zmiri-ignore-leaks -Zmiri-disable-isolation" \
+	$(Q)MIRIFLAGS="-Zmiri-disable-isolation" \
 		$(BOOTSTRAP) miri --stage 2 library/std \
 		--doc -- \
 		--skip fs:: --skip net:: --skip process:: --skip sys::pal::
diff --git a/src/bootstrap/src/bin/main.rs b/src/bootstrap/src/bin/main.rs
index 44fb1911fc6..a7d21ba6ae1 100644
--- a/src/bootstrap/src/bin/main.rs
+++ b/src/bootstrap/src/bin/main.rs
@@ -1,4 +1,4 @@
-//! rustbuild, the Rust build system
+//! bootstrap, the Rust build system
 //!
 //! This is the entry point for the build system used to compile the `rustc`
 //! compiler. Lots of documentation can be found in the `README.md` file in the
diff --git a/src/bootstrap/src/core/build_steps/compile.rs b/src/bootstrap/src/core/build_steps/compile.rs
index 11a7a404535..ba2ad53a94e 100644
--- a/src/bootstrap/src/core/build_steps/compile.rs
+++ b/src/bootstrap/src/core/build_steps/compile.rs
@@ -1,7 +1,7 @@
 //! Implementation of compiling various phases of the compiler and standard
 //! library.
 //!
-//! This module contains some of the real meat in the rustbuild build system
+//! This module contains some of the real meat in the bootstrap build system
 //! which is where Cargo is used to compile the standard library, libtest, and
 //! the compiler. This module is also responsible for assembling the sysroot as it
 //! goes along from the output of the previous stage.
@@ -14,7 +14,7 @@ use std::fs;
 use std::io::prelude::*;
 use std::io::BufReader;
 use std::path::{Path, PathBuf};
-use std::process::{Command, Stdio};
+use std::process::Stdio;
 use std::str;
 
 use serde_derive::Deserialize;
@@ -33,7 +33,6 @@ use crate::utils::helpers::{
 };
 use crate::LLVM_TOOLS;
 use crate::{CLang, Compiler, DependencyType, GitRepo, Mode};
-use filetime::FileTime;
 
 #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
 pub struct Std {
@@ -696,10 +695,10 @@ fn copy_sanitizers(
             || target == "x86_64-apple-ios"
         {
             // Update the library’s install name to reflect that it has been renamed.
-            apple_darwin_update_library_name(&dst, &format!("@rpath/{}", &runtime.name));
+            apple_darwin_update_library_name(builder, &dst, &format!("@rpath/{}", &runtime.name));
             // Upon renaming the install name, the code signature of the file will invalidate,
             // so we will sign it again.
-            apple_darwin_sign_file(&dst);
+            apple_darwin_sign_file(builder, &dst);
         }
 
         target_deps.push(dst);
@@ -708,25 +707,17 @@ fn copy_sanitizers(
     target_deps
 }
 
-fn apple_darwin_update_library_name(library_path: &Path, new_name: &str) {
-    let status = Command::new("install_name_tool")
-        .arg("-id")
-        .arg(new_name)
-        .arg(library_path)
-        .status()
-        .expect("failed to execute `install_name_tool`");
-    assert!(status.success());
+fn apple_darwin_update_library_name(builder: &Builder<'_>, library_path: &Path, new_name: &str) {
+    command("install_name_tool").arg("-id").arg(new_name).arg(library_path).run(builder);
 }
 
-fn apple_darwin_sign_file(file_path: &Path) {
-    let status = Command::new("codesign")
+fn apple_darwin_sign_file(builder: &Builder<'_>, file_path: &Path) {
+    command("codesign")
         .arg("-f") // Force to rewrite the existing signature
         .arg("-s")
         .arg("-")
         .arg(file_path)
-        .status()
-        .expect("failed to execute `codesign`");
-    assert!(status.success());
+        .run(builder);
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -818,8 +809,8 @@ pub struct Rustc {
     pub compiler: Compiler,
     /// Whether to build a subset of crates, rather than the whole compiler.
     ///
-    /// This should only be requested by the user, not used within rustbuild itself.
-    /// Using it within rustbuild can lead to confusing situation where lints are replayed
+    /// This should only be requested by the user, not used within bootstrap itself.
+    /// Using it within bootstrap can lead to confusing situation where lints are replayed
     /// in two different steps.
     crates: Vec<String>,
 }
@@ -1172,7 +1163,7 @@ fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelect
     if builder.config.llvm_profile_generate && target.is_msvc() {
         if let Some(ref clang_cl_path) = builder.config.llvm_clang_cl {
             // Add clang's runtime library directory to the search path
-            let clang_rt_dir = get_clang_cl_resource_dir(clang_cl_path);
+            let clang_rt_dir = get_clang_cl_resource_dir(builder, clang_cl_path);
             llvm_linker_flags.push_str(&format!("-L{}", clang_rt_dir.display()));
         }
     }
@@ -1213,8 +1204,8 @@ fn rustc_llvm_env(builder: &Builder<'_>, cargo: &mut Cargo, target: TargetSelect
     if builder.config.llvm_use_libcxx {
         cargo.env("LLVM_USE_LIBCXX", "1");
     }
-    if builder.config.llvm_optimize && !builder.config.llvm_release_debuginfo {
-        cargo.env("LLVM_NDEBUG", "1");
+    if builder.config.llvm_assertions {
+        cargo.env("LLVM_ASSERTIONS", "1");
     }
 }
 
@@ -2080,7 +2071,8 @@ pub fn stream_cargo(
     tail_args: Vec<String>,
     cb: &mut dyn FnMut(CargoMessage<'_>),
 ) -> bool {
-    let mut cargo = cargo.into_cmd().command;
+    let mut cmd = cargo.into_cmd();
+    let cargo = cmd.as_command_mut();
     // Instruct Cargo to give us json messages on stdout, critically leaving
     // stderr as piped so we can get those pretty colors.
     let mut message_format = if builder.config.json_output {
@@ -2160,9 +2152,11 @@ pub fn strip_debug(builder: &Builder<'_>, target: TargetSelection, path: &Path)
         return;
     }
 
-    let previous_mtime = FileTime::from_last_modification_time(&path.metadata().unwrap());
+    let previous_mtime = t!(t!(path.metadata()).modified());
     command("strip").capture().arg("--strip-debug").arg(path).run(builder);
 
+    let file = t!(fs::File::open(path));
+
     // After running `strip`, we have to set the file modification time to what it was before,
     // otherwise we risk Cargo invalidating its fingerprint and rebuilding the world next time
     // bootstrap is invoked.
@@ -2175,5 +2169,5 @@ pub fn strip_debug(builder: &Builder<'_>, target: TargetSelection, path: &Path)
     // In the second invocation of bootstrap, Cargo will see that the mtime of librustc_driver.so
     // is greater than the mtime of rustc-main, and will rebuild rustc-main. That will then cause
     // everything else (standard library, future stages...) to be rebuilt.
-    t!(filetime::set_file_mtime(path, previous_mtime));
+    t!(file.set_modified(previous_mtime));
 }
diff --git a/src/bootstrap/src/core/build_steps/dist.rs b/src/bootstrap/src/core/build_steps/dist.rs
index 7bc5405e92f..1e9d2025bc7 100644
--- a/src/bootstrap/src/core/build_steps/dist.rs
+++ b/src/bootstrap/src/core/build_steps/dist.rs
@@ -1060,11 +1060,7 @@ impl Step for PlainSourceTarball {
                 cmd.arg("--sync").arg(manifest_path);
             }
 
-            let config = if !builder.config.dry_run() {
-                cmd.capture().run(builder).stdout()
-            } else {
-                String::new()
-            };
+            let config = cmd.capture().run(builder).stdout();
 
             let cargo_config_dir = plain_dst_src.join(".cargo");
             builder.create_dir(&cargo_config_dir);
@@ -2072,11 +2068,7 @@ fn maybe_install_llvm(
         let mut cmd = command(llvm_config);
         cmd.arg("--libfiles");
         builder.verbose(|| println!("running {cmd:?}"));
-        let files = if builder.config.dry_run() {
-            "".into()
-        } else {
-            cmd.capture_stdout().run(builder).stdout()
-        };
+        let files = cmd.capture_stdout().run(builder).stdout();
         let build_llvm_out = &builder.llvm_out(builder.config.build);
         let target_llvm_out = &builder.llvm_out(target);
         for file in files.trim_end().split(' ') {
diff --git a/src/bootstrap/src/core/build_steps/doc.rs b/src/bootstrap/src/core/build_steps/doc.rs
index 4b35d6c5d4c..633e66afe59 100644
--- a/src/bootstrap/src/core/build_steps/doc.rs
+++ b/src/bootstrap/src/core/build_steps/doc.rs
@@ -1,4 +1,4 @@
-//! Documentation generation for rustbuilder.
+//! Documentation generation for bootstrap.
 //!
 //! This module implements generation for all bits and pieces of documentation
 //! for the Rust project. This notably includes suites like the rust book, the
@@ -146,7 +146,6 @@ impl<P: Step> Step for RustbookSrc<P> {
         let out = out.join(&name);
         let index = out.join("index.html");
         let rustbook = builder.tool_exe(Tool::Rustbook);
-        let mut rustbook_cmd = builder.tool_cmd(Tool::Rustbook);
 
         if !builder.config.dry_run()
             && (!up_to_date(&src, &index) || !up_to_date(&rustbook, &index))
@@ -154,7 +153,13 @@ impl<P: Step> Step for RustbookSrc<P> {
             builder.info(&format!("Rustbook ({target}) - {name}"));
             let _ = fs::remove_dir_all(&out);
 
-            rustbook_cmd.arg("build").arg(&src).arg("-d").arg(&out).run(builder);
+            builder
+                .tool_cmd(Tool::Rustbook)
+                .arg("build")
+                .arg(&src)
+                .arg("-d")
+                .arg(&out)
+                .run(builder);
 
             for lang in &self.languages {
                 let out = out.join(lang);
@@ -253,10 +258,6 @@ impl Step for TheBook {
         // build the version info page and CSS
         let shared_assets = builder.ensure(SharedAssets { target });
 
-        // build the command first so we don't nest GHA groups
-        // FIXME: this doesn't do anything!
-        builder.rustdoc_cmd(compiler);
-
         // build the redirect pages
         let _guard = builder.msg_doc(compiler, "book redirect pages", target);
         for file in t!(fs::read_dir(redirect_path)) {
diff --git a/src/bootstrap/src/core/build_steps/llvm.rs b/src/bootstrap/src/core/build_steps/llvm.rs
index 872823506f8..888290a0479 100644
--- a/src/bootstrap/src/core/build_steps/llvm.rs
+++ b/src/bootstrap/src/core/build_steps/llvm.rs
@@ -125,6 +125,7 @@ pub fn prebuilt_llvm_config(builder: &Builder<'_>, target: TargetSelection) -> L
     static STAMP_HASH_MEMO: OnceLock<String> = OnceLock::new();
     let smart_stamp_hash = STAMP_HASH_MEMO.get_or_init(|| {
         generate_smart_stamp_hash(
+            builder,
             &builder.config.src.join("src/llvm-project"),
             builder.in_tree_llvm_info.sha().unwrap_or_default(),
         )
@@ -172,7 +173,7 @@ pub(crate) fn detect_llvm_sha(config: &Config, is_git: bool) -> String {
             // the LLVM shared object file is named `LLVM-12-rust-{version}-nightly`
             config.src.join("src/version"),
         ]);
-        output(&mut rev_list.command).trim().to_owned()
+        output(rev_list.as_command_mut()).trim().to_owned()
     } else if let Some(info) = channel::read_commit_info_file(&config.src) {
         info.sha.trim().to_owned()
     } else {
@@ -254,7 +255,7 @@ pub(crate) fn is_ci_llvm_modified(config: &Config) -> bool {
         // `true` here.
         let llvm_sha = detect_llvm_sha(config, true);
         let head_sha =
-            output(&mut helpers::git(Some(&config.src)).arg("rev-parse").arg("HEAD").command);
+            output(helpers::git(Some(&config.src)).arg("rev-parse").arg("HEAD").as_command_mut());
         let head_sha = head_sha.trim();
         llvm_sha == head_sha
     }
@@ -912,7 +913,7 @@ impl Step for Lld {
             if let Some(clang_cl_path) = builder.config.llvm_clang_cl.as_ref() {
                 // Find clang's runtime library directory and push that as a search path to the
                 // cmake linker flags.
-                let clang_rt_dir = get_clang_cl_resource_dir(clang_cl_path);
+                let clang_rt_dir = get_clang_cl_resource_dir(builder, clang_cl_path);
                 ldflags.push_all(format!("/libpath:{}", clang_rt_dir.display()));
             }
         }
diff --git a/src/bootstrap/src/core/build_steps/setup.rs b/src/bootstrap/src/core/build_steps/setup.rs
index e6a09e8cb8e..226b3729c10 100644
--- a/src/bootstrap/src/core/build_steps/setup.rs
+++ b/src/bootstrap/src/core/build_steps/setup.rs
@@ -8,6 +8,7 @@
 use crate::core::builder::{Builder, RunConfig, ShouldRun, Step};
 use crate::t;
 use crate::utils::change_tracker::CONFIG_CHANGE_HISTORY;
+use crate::utils::exec::command;
 use crate::utils::helpers::{self, hex_encode};
 use crate::Config;
 use sha2::Digest;
@@ -16,7 +17,6 @@ use std::fmt::Write as _;
 use std::fs::File;
 use std::io::Write;
 use std::path::{Path, PathBuf, MAIN_SEPARATOR_STR};
-use std::process::Command;
 use std::str::FromStr;
 use std::{fmt, fs, io};
 
@@ -266,20 +266,16 @@ impl Step for Link {
         }
         let stage_path =
             ["build", config.build.rustc_target_arg(), "stage1"].join(MAIN_SEPARATOR_STR);
-        if !rustup_installed() {
+        if !rustup_installed(builder) {
             eprintln!("`rustup` is not installed; cannot link `stage1` toolchain");
         } else if stage_dir_exists(&stage_path[..]) && !config.dry_run() {
-            attempt_toolchain_link(&stage_path[..]);
+            attempt_toolchain_link(builder, &stage_path[..]);
         }
     }
 }
 
-fn rustup_installed() -> bool {
-    Command::new("rustup")
-        .arg("--version")
-        .stdout(std::process::Stdio::null())
-        .output()
-        .map_or(false, |output| output.status.success())
+fn rustup_installed(builder: &Builder<'_>) -> bool {
+    command("rustup").capture_stdout().arg("--version").run(builder).is_success()
 }
 
 fn stage_dir_exists(stage_path: &str) -> bool {
@@ -289,8 +285,8 @@ fn stage_dir_exists(stage_path: &str) -> bool {
     }
 }
 
-fn attempt_toolchain_link(stage_path: &str) {
-    if toolchain_is_linked() {
+fn attempt_toolchain_link(builder: &Builder<'_>, stage_path: &str) {
+    if toolchain_is_linked(builder) {
         return;
     }
 
@@ -301,7 +297,7 @@ fn attempt_toolchain_link(stage_path: &str) {
         return;
     }
 
-    if try_link_toolchain(stage_path) {
+    if try_link_toolchain(builder, stage_path) {
         println!(
             "Added `stage1` rustup toolchain; try `cargo +stage1 build` on a separate rust project to run a newly-built toolchain"
         );
@@ -315,14 +311,16 @@ fn attempt_toolchain_link(stage_path: &str) {
     }
 }
 
-fn toolchain_is_linked() -> bool {
-    match Command::new("rustup")
+fn toolchain_is_linked(builder: &Builder<'_>) -> bool {
+    match command("rustup")
+        .capture_stdout()
+        .allow_failure()
         .args(["toolchain", "list"])
-        .stdout(std::process::Stdio::piped())
-        .output()
+        .run(builder)
+        .stdout_if_ok()
     {
-        Ok(toolchain_list) => {
-            if !String::from_utf8_lossy(&toolchain_list.stdout).contains("stage1") {
+        Some(toolchain_list) => {
+            if !toolchain_list.contains("stage1") {
                 return false;
             }
             // The toolchain has already been linked.
@@ -330,7 +328,7 @@ fn toolchain_is_linked() -> bool {
                 "`stage1` toolchain already linked; not attempting to link `stage1` toolchain"
             );
         }
-        Err(_) => {
+        None => {
             // In this case, we don't know if the `stage1` toolchain has been linked;
             // but `rustup` failed, so let's not go any further.
             println!(
@@ -341,12 +339,12 @@ fn toolchain_is_linked() -> bool {
     true
 }
 
-fn try_link_toolchain(stage_path: &str) -> bool {
-    Command::new("rustup")
-        .stdout(std::process::Stdio::null())
+fn try_link_toolchain(builder: &Builder<'_>, stage_path: &str) -> bool {
+    command("rustup")
+        .capture_stdout()
         .args(["toolchain", "link", "stage1", stage_path])
-        .output()
-        .map_or(false, |output| output.status.success())
+        .run(builder)
+        .is_success()
 }
 
 fn ensure_stage1_toolchain_placeholder_exists(stage_path: &str) -> bool {
@@ -476,20 +474,18 @@ impl Step for Hook {
         if config.dry_run() {
             return;
         }
-        t!(install_git_hook_maybe(config));
+        t!(install_git_hook_maybe(builder, config));
     }
 }
 
 // install a git hook to automatically run tidy, if they want
-fn install_git_hook_maybe(config: &Config) -> io::Result<()> {
+fn install_git_hook_maybe(builder: &Builder<'_>, config: &Config) -> io::Result<()> {
     let git = helpers::git(Some(&config.src))
+        .capture()
         .args(["rev-parse", "--git-common-dir"])
-        .command
-        .output()
-        .map(|output| {
-            assert!(output.status.success(), "failed to run `git`");
-            PathBuf::from(t!(String::from_utf8(output.stdout)).trim())
-        })?;
+        .run(builder)
+        .stdout();
+    let git = PathBuf::from(git.trim());
     let hooks_dir = git.join("hooks");
     let dst = hooks_dir.join("pre-push");
     if dst.exists() {
diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs
index 0b60587bb79..3f0cbde64e3 100644
--- a/src/bootstrap/src/core/build_steps/test.rs
+++ b/src/bootstrap/src/core/build_steps/test.rs
@@ -9,7 +9,6 @@ use std::ffi::OsString;
 use std::fs;
 use std::iter;
 use std::path::{Path, PathBuf};
-use std::process::{Command, Stdio};
 
 use clap_complete::shells;
 
@@ -169,12 +168,8 @@ You can skip linkcheck with --skip src/tools/linkchecker"
     }
 }
 
-fn check_if_tidy_is_installed() -> bool {
-    Command::new("tidy")
-        .arg("--version")
-        .stdout(Stdio::null())
-        .status()
-        .map_or(false, |status| status.success())
+fn check_if_tidy_is_installed(builder: &Builder<'_>) -> bool {
+    command("tidy").capture_stdout().allow_failure().arg("--version").run(builder).is_success()
 }
 
 #[derive(Debug, Clone, PartialEq, Eq, Hash)]
@@ -188,8 +183,9 @@ impl Step for HtmlCheck {
     const ONLY_HOSTS: bool = true;
 
     fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        let builder = run.builder;
         let run = run.path("src/tools/html-checker");
-        run.lazy_default_condition(Box::new(check_if_tidy_is_installed))
+        run.lazy_default_condition(Box::new(|| check_if_tidy_is_installed(builder)))
     }
 
     fn make_run(run: RunConfig<'_>) {
@@ -197,7 +193,7 @@ impl Step for HtmlCheck {
     }
 
     fn run(self, builder: &Builder<'_>) {
-        if !check_if_tidy_is_installed() {
+        if !check_if_tidy_is_installed(builder) {
             eprintln!("not running HTML-check tool because `tidy` is missing");
             eprintln!(
                 "You need the HTML tidy tool https://www.html-tidy.org/, this tool is *not* part of the rust project and needs to be installed separately, for example via your package manager."
@@ -471,16 +467,12 @@ impl Miri {
         // We re-use the `cargo` from above.
         cargo.arg("--print-sysroot");
 
-        if builder.config.dry_run() {
-            String::new()
-        } else {
-            builder.verbose(|| println!("running: {cargo:?}"));
-            let stdout = cargo.capture_stdout().run(builder).stdout();
-            // Output is "<sysroot>\n".
-            let sysroot = stdout.trim_end();
-            builder.verbose(|| println!("`cargo miri setup --print-sysroot` said: {sysroot:?}"));
-            sysroot.to_owned()
-        }
+        builder.verbose(|| println!("running: {cargo:?}"));
+        let stdout = cargo.capture_stdout().run(builder).stdout();
+        // Output is "<sysroot>\n".
+        let sysroot = stdout.trim_end();
+        builder.verbose(|| println!("`cargo miri setup --print-sysroot` said: {sysroot:?}"));
+        sysroot.to_owned()
     }
 }
 
@@ -686,6 +678,8 @@ impl Step for CompiletestTest {
         let mut cargo = tool::prepare_tool_cargo(
             builder,
             compiler,
+            // compiletest uses libtest internals; make it use the in-tree std to make sure it never breaks
+            // when std sources change.
             Mode::ToolStd,
             host,
             "test",
@@ -1325,13 +1319,12 @@ impl Step for CrateRunMakeSupport {
     /// Runs `cargo test` for run-make-support.
     fn run(self, builder: &Builder<'_>) {
         let host = self.host;
-        let compiler = builder.compiler(builder.top_stage, host);
+        let compiler = builder.compiler(0, host);
 
-        builder.ensure(compile::Std::new(compiler, host));
         let mut cargo = tool::prepare_tool_cargo(
             builder,
             compiler,
-            Mode::ToolStd,
+            Mode::ToolBootstrap,
             host,
             "test",
             "src/tools/run-make-support",
@@ -1352,6 +1345,52 @@ impl Step for CrateRunMakeSupport {
     }
 }
 
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+pub struct CrateBuildHelper {
+    host: TargetSelection,
+}
+
+impl Step for CrateBuildHelper {
+    type Output = ();
+    const ONLY_HOSTS: bool = true;
+
+    fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
+        run.path("src/tools/build_helper")
+    }
+
+    fn make_run(run: RunConfig<'_>) {
+        run.builder.ensure(CrateBuildHelper { host: run.target });
+    }
+
+    /// Runs `cargo test` for build_helper.
+    fn run(self, builder: &Builder<'_>) {
+        let host = self.host;
+        let compiler = builder.compiler(0, host);
+
+        let mut cargo = tool::prepare_tool_cargo(
+            builder,
+            compiler,
+            Mode::ToolBootstrap,
+            host,
+            "test",
+            "src/tools/build_helper",
+            SourceType::InTree,
+            &[],
+        );
+        cargo.allow_features("test");
+        run_cargo_test(
+            cargo,
+            &[],
+            &[],
+            "build_helper",
+            "build_helper self test",
+            compiler,
+            host,
+            builder,
+        );
+    }
+}
+
 default_test!(Ui { path: "tests/ui", mode: "ui", suite: "ui" });
 
 default_test!(Crashes { path: "tests/crashes", mode: "crashes", suite: "crashes" });
@@ -2056,9 +2095,7 @@ NOTE: if you're sure you want to do this, please open an issue as to why. In the
         let git_config = builder.config.git_config();
         cmd.arg("--git-repository").arg(git_config.git_repository);
         cmd.arg("--nightly-branch").arg(git_config.nightly_branch);
-
-        // FIXME: Move CiEnv back to bootstrap, it is only used here anyway
-        builder.ci_env.force_coloring_in_ci(&mut cmd.command);
+        cmd.force_coloring_in_ci(builder.ci_env);
 
         #[cfg(feature = "build-metrics")]
         builder.metrics.begin_test_suite(
@@ -3001,7 +3038,7 @@ impl Step for Bootstrap {
             // https://github.com/rust-lang/rust/issues/49215
             cmd.env("RUSTFLAGS", flags);
         }
-        // rustbuild tests are racy on directory creation so just run them one at a time.
+        // bootstrap tests are racy on directory creation so just run them one at a time.
         // Since there's not many this shouldn't be a problem.
         run_cargo_test(cmd, &["--test-threads=1"], &[], "bootstrap", None, compiler, host, builder);
     }
diff --git a/src/bootstrap/src/core/build_steps/toolstate.rs b/src/bootstrap/src/core/build_steps/toolstate.rs
index e3e7931a5a2..2ab0ce7454b 100644
--- a/src/bootstrap/src/core/build_steps/toolstate.rs
+++ b/src/bootstrap/src/core/build_steps/toolstate.rs
@@ -99,24 +99,16 @@ fn print_error(tool: &str, submodule: &str) {
     crate::exit!(3);
 }
 
-fn check_changed_files(toolstates: &HashMap<Box<str>, ToolState>) {
+fn check_changed_files(builder: &Builder<'_>, toolstates: &HashMap<Box<str>, ToolState>) {
     // Changed files
     let output = helpers::git(None)
+        .capture()
         .arg("diff")
         .arg("--name-status")
         .arg("HEAD")
         .arg("HEAD^")
-        .command
-        .output();
-    let output = match output {
-        Ok(o) => o,
-        Err(e) => {
-            eprintln!("Failed to get changed files: {e:?}");
-            crate::exit!(1);
-        }
-    };
-
-    let output = t!(String::from_utf8(output.stdout));
+        .run(builder)
+        .stdout();
 
     for (tool, submodule) in STABLE_TOOLS.iter().chain(NIGHTLY_TOOLS.iter()) {
         let changed = output.lines().any(|l| l.starts_with('M') && l.ends_with(submodule));
@@ -186,8 +178,8 @@ impl Step for ToolStateCheck {
             crate::exit!(1);
         }
 
-        check_changed_files(&toolstates);
-        checkout_toolstate_repo();
+        check_changed_files(builder, &toolstates);
+        checkout_toolstate_repo(builder);
         let old_toolstate = read_old_toolstate();
 
         for (tool, _) in STABLE_TOOLS.iter() {
@@ -231,7 +223,7 @@ impl Step for ToolStateCheck {
         }
 
         if builder.config.channel == "nightly" && env::var_os("TOOLSTATE_PUBLISH").is_some() {
-            commit_toolstate_change(&toolstates);
+            commit_toolstate_change(builder, &toolstates);
         }
     }
 
@@ -315,50 +307,34 @@ fn toolstate_repo() -> String {
 const TOOLSTATE_DIR: &str = "rust-toolstate";
 
 /// Checks out the toolstate repo into `TOOLSTATE_DIR`.
-fn checkout_toolstate_repo() {
+fn checkout_toolstate_repo(builder: &Builder<'_>) {
     if let Ok(token) = env::var("TOOLSTATE_REPO_ACCESS_TOKEN") {
-        prepare_toolstate_config(&token);
+        prepare_toolstate_config(builder, &token);
     }
     if Path::new(TOOLSTATE_DIR).exists() {
         eprintln!("Cleaning old toolstate directory...");
         t!(fs::remove_dir_all(TOOLSTATE_DIR));
     }
 
-    let status = helpers::git(None)
+    helpers::git(None)
         .arg("clone")
         .arg("--depth=1")
         .arg(toolstate_repo())
         .arg(TOOLSTATE_DIR)
-        .command
-        .status();
-    let success = match status {
-        Ok(s) => s.success(),
-        Err(_) => false,
-    };
-    if !success {
-        panic!("git clone unsuccessful (status: {status:?})");
-    }
+        .run(builder);
 }
 
 /// Sets up config and authentication for modifying the toolstate repo.
-fn prepare_toolstate_config(token: &str) {
-    fn git_config(key: &str, value: &str) {
-        let status =
-            helpers::git(None).arg("config").arg("--global").arg(key).arg(value).command.status();
-        let success = match status {
-            Ok(s) => s.success(),
-            Err(_) => false,
-        };
-        if !success {
-            panic!("git config key={key} value={value} failed (status: {status:?})");
-        }
+fn prepare_toolstate_config(builder: &Builder<'_>, token: &str) {
+    fn git_config(builder: &Builder<'_>, key: &str, value: &str) {
+        helpers::git(None).arg("config").arg("--global").arg(key).arg(value).run(builder);
     }
 
     // If changing anything here, then please check that `src/ci/publish_toolstate.sh` is up to date
     // as well.
-    git_config("user.email", "7378925+rust-toolstate-update@users.noreply.github.com");
-    git_config("user.name", "Rust Toolstate Update");
-    git_config("credential.helper", "store");
+    git_config(builder, "user.email", "7378925+rust-toolstate-update@users.noreply.github.com");
+    git_config(builder, "user.name", "Rust Toolstate Update");
+    git_config(builder, "credential.helper", "store");
 
     let credential = format!("https://{token}:x-oauth-basic@github.com\n",);
     let git_credential_path = PathBuf::from(t!(env::var("HOME"))).join(".git-credentials");
@@ -398,55 +374,51 @@ fn read_old_toolstate() -> Vec<RepoState> {
 ///
 ///       * See <https://help.github.com/articles/about-commit-email-addresses/>
 ///           if a private email by GitHub is wanted.
-fn commit_toolstate_change(current_toolstate: &ToolstateData) {
+fn commit_toolstate_change(builder: &Builder<'_>, current_toolstate: &ToolstateData) {
     let message = format!("({} CI update)", OS.expect("linux/windows only"));
     let mut success = false;
     for _ in 1..=5 {
         // Upload the test results (the new commit-to-toolstate mapping) to the toolstate repo.
         // This does *not* change the "current toolstate"; that only happens post-landing
         // via `src/ci/docker/publish_toolstate.sh`.
-        publish_test_results(current_toolstate);
+        publish_test_results(builder, current_toolstate);
 
         // `git commit` failing means nothing to commit.
-        let status = t!(helpers::git(Some(Path::new(TOOLSTATE_DIR)))
+        let status = helpers::git(Some(Path::new(TOOLSTATE_DIR)))
+            .allow_failure()
             .arg("commit")
             .arg("-a")
             .arg("-m")
             .arg(&message)
-            .command
-            .status());
-        if !status.success() {
+            .run(builder);
+        if !status.is_success() {
             success = true;
             break;
         }
 
-        let status = t!(helpers::git(Some(Path::new(TOOLSTATE_DIR)))
+        let status = helpers::git(Some(Path::new(TOOLSTATE_DIR)))
+            .allow_failure()
             .arg("push")
             .arg("origin")
             .arg("master")
-            .command
-            .status());
+            .run(builder);
         // If we successfully push, exit.
-        if status.success() {
+        if status.is_success() {
             success = true;
             break;
         }
         eprintln!("Sleeping for 3 seconds before retrying push");
         std::thread::sleep(std::time::Duration::from_secs(3));
-        let status = t!(helpers::git(Some(Path::new(TOOLSTATE_DIR)))
+        helpers::git(Some(Path::new(TOOLSTATE_DIR)))
             .arg("fetch")
             .arg("origin")
             .arg("master")
-            .command
-            .status());
-        assert!(status.success());
-        let status = t!(helpers::git(Some(Path::new(TOOLSTATE_DIR)))
+            .run(builder);
+        helpers::git(Some(Path::new(TOOLSTATE_DIR)))
             .arg("reset")
             .arg("--hard")
             .arg("origin/master")
-            .command
-            .status());
-        assert!(status.success());
+            .run(builder);
     }
 
     if !success {
@@ -459,9 +431,8 @@ fn commit_toolstate_change(current_toolstate: &ToolstateData) {
 /// These results will later be promoted to `latest.json` by the
 /// `publish_toolstate.py` script if the PR passes all tests and is merged to
 /// master.
-fn publish_test_results(current_toolstate: &ToolstateData) {
-    let commit = t!(helpers::git(None).arg("rev-parse").arg("HEAD").command.output());
-    let commit = t!(String::from_utf8(commit.stdout));
+fn publish_test_results(builder: &Builder<'_>, current_toolstate: &ToolstateData) {
+    let commit = helpers::git(None).capture().arg("rev-parse").arg("HEAD").run(builder).stdout();
 
     let toolstate_serialized = t!(serde_json::to_string(&current_toolstate));
 
diff --git a/src/bootstrap/src/core/builder.rs b/src/bootstrap/src/core/builder.rs
index 65cc1fa7478..ebd62bb032f 100644
--- a/src/bootstrap/src/core/builder.rs
+++ b/src/bootstrap/src/core/builder.rs
@@ -860,6 +860,7 @@ impl<'a> Builder<'a> {
                 test::Clippy,
                 test::CompiletestTest,
                 test::CrateRunMakeSupport,
+                test::CrateBuildHelper,
                 test::RustdocJSStd,
                 test::RustdocJSNotStd,
                 test::RustdocGUI,
@@ -1626,11 +1627,11 @@ impl<'a> Builder<'a> {
         }
 
         // This tells Cargo (and in turn, rustc) to output more complete
-        // dependency information.  Most importantly for rustbuild, this
+        // dependency information.  Most importantly for bootstrap, this
         // includes sysroot artifacts, like libstd, which means that we don't
-        // need to track those in rustbuild (an error prone process!). This
+        // need to track those in bootstrap (an error prone process!). This
         // feature is currently unstable as there may be some bugs and such, but
-        // it represents a big improvement in rustbuild's reliability on
+        // it represents a big improvement in bootstrap's reliability on
         // rebuilds, so we're using it here.
         //
         // For some additional context, see #63470 (the PR originally adding
@@ -1642,7 +1643,7 @@ impl<'a> Builder<'a> {
                 // Restrict the allowed features so we don't depend on nightly
                 // accidentally.
                 //
-                // binary-dep-depinfo is used by rustbuild itself for all
+                // binary-dep-depinfo is used by bootstrap itself for all
                 // compilations.
                 //
                 // Lots of tools depend on proc_macro2 and proc-macro-error.
@@ -1997,6 +1998,9 @@ impl<'a> Builder<'a> {
         if mode == Mode::Rustc {
             rustflags.arg("-Zunstable-options");
             rustflags.arg("-Wrustc::internal");
+            // FIXME(edition_2024): Change this to `-Wrust_2024_idioms` when all
+            // of the individual lints are satisfied.
+            rustflags.arg("-Wkeyword_idents_2024");
         }
 
         if self.config.rust_frame_pointers {
@@ -2104,7 +2108,7 @@ impl<'a> Builder<'a> {
         // Try to use a sysroot-relative bindir, in case it was configured absolutely.
         cargo.env("RUSTC_INSTALL_BINDIR", self.config.bindir_relative());
 
-        self.ci_env.force_coloring_in_ci(&mut cargo.command);
+        cargo.force_coloring_in_ci(self.ci_env);
 
         // When we build Rust dylibs they're all intended for intermediate
         // usage, so make sure we pass the -Cprefer-dynamic flag instead of
diff --git a/src/bootstrap/src/core/builder/tests.rs b/src/bootstrap/src/core/builder/tests.rs
index aa119b8c699..97c9ece0036 100644
--- a/src/bootstrap/src/core/builder/tests.rs
+++ b/src/bootstrap/src/core/builder/tests.rs
@@ -266,7 +266,7 @@ mod defaults {
         // rustdoc/rustcc/std here (the user only requested a host=B build, so
         // there's not really a need for us to build for target A in this case
         // (since we're producing stage 1 libraries/binaries).  But currently
-        // rustbuild is just a bit buggy here; this should be fixed though.
+        // bootstrap is just a bit buggy here; this should be fixed though.
         assert_eq!(
             first(cache.all::<compile::Std>()),
             &[
diff --git a/src/bootstrap/src/core/config/config.rs b/src/bootstrap/src/core/config/config.rs
index 2d54a84331f..f96633b059a 100644
--- a/src/bootstrap/src/core/config/config.rs
+++ b/src/bootstrap/src/core/config/config.rs
@@ -33,7 +33,7 @@ macro_rules! check_ci_llvm {
         assert!(
             $name.is_none(),
             "setting {} is incompatible with download-ci-llvm.",
-            stringify!($name)
+            stringify!($name).replace("_", "-")
         );
     };
 }
@@ -658,8 +658,7 @@ impl Merge for TomlConfig {
     }
 }
 
-// We are using a decl macro instead of a derive proc macro here to reduce the compile time of
-// rustbuild.
+// We are using a decl macro instead of a derive proc macro here to reduce the compile time of bootstrap.
 macro_rules! define_config {
     ($(#[$attr:meta])* struct $name:ident {
         $($field:ident: Option<$field_ty:ty> = $field_key:literal,)*
@@ -704,7 +703,7 @@ macro_rules! define_config {
 
         // The following is a trimmed version of what serde_derive generates. All parts not relevant
         // for toml deserialization have been removed. This reduces the binary size and improves
-        // compile time of rustbuild.
+        // compile time of bootstrap.
         impl<'de> Deserialize<'de> for $name {
             fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
             where
@@ -1259,7 +1258,7 @@ impl Config {
         cmd.arg("rev-parse").arg("--show-cdup");
         // Discard stderr because we expect this to fail when building from a tarball.
         let output = cmd
-            .command
+            .as_command_mut()
             .stderr(std::process::Stdio::null())
             .output()
             .ok()
@@ -1569,7 +1568,15 @@ impl Config {
         let mut lld_enabled = None;
 
         let mut is_user_configured_rust_channel = false;
+
         if let Some(rust) = toml.rust {
+            config.download_rustc_commit =
+                config.download_ci_rustc_commit(rust.download_rustc.clone());
+
+            if config.download_rustc_commit.is_some() {
+                check_incompatible_options_for_ci_rustc(&rust);
+            }
+
             let Rust {
                 optimize: optimize_toml,
                 debug: debug_toml,
@@ -1617,7 +1624,7 @@ impl Config {
                 new_symbol_mangling,
                 profile_generate,
                 profile_use,
-                download_rustc,
+                download_rustc: _,
                 lto,
                 validate_mir_opts,
                 frame_pointers,
@@ -1627,11 +1634,7 @@ impl Config {
             } = rust;
 
             is_user_configured_rust_channel = channel.is_some();
-            set(&mut config.channel, channel);
-
-            config.download_rustc_commit = config.download_ci_rustc_commit(download_rustc);
-
-            // FIXME: handle download-rustc incompatible options.
+            set(&mut config.channel, channel.clone());
 
             debug = debug_toml;
             debug_assertions = debug_assertions_toml;
@@ -2163,7 +2166,7 @@ impl Config {
 
         let mut git = helpers::git(Some(&self.src));
         git.arg("show").arg(format!("{commit}:{}", file.to_str().unwrap()));
-        output(&mut git.command)
+        output(git.as_command_mut())
     }
 
     /// Bootstrap embeds a version number into the name of shared libraries it uploads in CI.
@@ -2466,22 +2469,14 @@ impl Config {
             }
         };
 
-        // Handle running from a directory other than the top level
-        let top_level = output(
-            &mut helpers::git(Some(&self.src)).args(["rev-parse", "--show-toplevel"]).command,
-        );
-        let top_level = top_level.trim_end();
-        let compiler = format!("{top_level}/compiler/");
-        let library = format!("{top_level}/library/");
-
         // Look for a version to compare to based on the current commit.
         // Only commits merged by bors will have CI artifacts.
         let merge_base = output(
-            &mut helpers::git(Some(&self.src))
+            helpers::git(Some(&self.src))
                 .arg("rev-list")
                 .arg(format!("--author={}", self.stage0_metadata.config.git_merge_commit_email))
                 .args(["-n1", "--first-parent", "HEAD"])
-                .command,
+                .as_command_mut(),
         );
         let commit = merge_base.trim_end();
         if commit.is_empty() {
@@ -2494,8 +2489,10 @@ impl Config {
 
         // Warn if there were changes to the compiler or standard library since the ancestor commit.
         let has_changes = !t!(helpers::git(Some(&self.src))
-            .args(["diff-index", "--quiet", commit, "--", &compiler, &library])
-            .command
+            .args(["diff-index", "--quiet", commit])
+            .arg("--")
+            .args([self.src.join("compiler"), self.src.join("library")])
+            .as_command_mut()
             .status())
         .success();
         if has_changes {
@@ -2566,20 +2563,14 @@ impl Config {
         option_name: &str,
         if_unchanged: bool,
     ) -> Option<String> {
-        // Handle running from a directory other than the top level
-        let top_level = output(
-            &mut helpers::git(Some(&self.src)).args(["rev-parse", "--show-toplevel"]).command,
-        );
-        let top_level = top_level.trim_end();
-
         // Look for a version to compare to based on the current commit.
         // Only commits merged by bors will have CI artifacts.
         let merge_base = output(
-            &mut helpers::git(Some(&self.src))
+            helpers::git(Some(&self.src))
                 .arg("rev-list")
                 .arg(format!("--author={}", self.stage0_metadata.config.git_merge_commit_email))
                 .args(["-n1", "--first-parent", "HEAD"])
-                .command,
+                .as_command_mut(),
         );
         let commit = merge_base.trim_end();
         if commit.is_empty() {
@@ -2594,11 +2585,14 @@ impl Config {
         let mut git = helpers::git(Some(&self.src));
         git.args(["diff-index", "--quiet", commit, "--"]);
 
+        // Handle running from a directory other than the top level
+        let top_level = &self.src;
+
         for path in modified_paths {
-            git.arg(format!("{top_level}/{path}"));
+            git.arg(top_level.join(path));
         }
 
-        let has_changes = !t!(git.command.status()).success();
+        let has_changes = !t!(git.as_command_mut().status()).success();
         if has_changes {
             if if_unchanged {
                 if self.verbose > 0 {
@@ -2618,6 +2612,113 @@ impl Config {
     }
 }
 
+/// Checks the CI rustc incompatible options by destructuring the `Rust` instance
+/// and makes sure that no rust options from config.toml are missed.
+fn check_incompatible_options_for_ci_rustc(rust: &Rust) {
+    macro_rules! err {
+        ($name:expr) => {
+            assert!(
+                $name.is_none(),
+                "ERROR: Setting `rust.{}` is incompatible with `rust.download-rustc`.",
+                stringify!($name).replace("_", "-")
+            );
+        };
+    }
+
+    macro_rules! warn {
+        ($name:expr) => {
+            if $name.is_some() {
+                println!(
+                    "WARNING: `rust.{}` has no effect with `rust.download-rustc`.",
+                    stringify!($name).replace("_", "-")
+                );
+            }
+        };
+    }
+
+    let Rust {
+        // Following options are the CI rustc incompatible ones.
+        optimize,
+        debug_logging,
+        debuginfo_level_rustc,
+        llvm_tools,
+        llvm_bitcode_linker,
+        lto,
+        stack_protector,
+        strip,
+        lld_mode,
+        jemalloc,
+        rpath,
+        channel,
+        description,
+        incremental,
+        default_linker,
+
+        // Rest of the options can simply be ignored.
+        debug: _,
+        codegen_units: _,
+        codegen_units_std: _,
+        debug_assertions: _,
+        debug_assertions_std: _,
+        overflow_checks: _,
+        overflow_checks_std: _,
+        debuginfo_level: _,
+        debuginfo_level_std: _,
+        debuginfo_level_tools: _,
+        debuginfo_level_tests: _,
+        split_debuginfo: _,
+        backtrace: _,
+        parallel_compiler: _,
+        musl_root: _,
+        verbose_tests: _,
+        optimize_tests: _,
+        codegen_tests: _,
+        omit_git_hash: _,
+        dist_src: _,
+        save_toolstates: _,
+        codegen_backends: _,
+        lld: _,
+        deny_warnings: _,
+        backtrace_on_ice: _,
+        verify_llvm_ir: _,
+        thin_lto_import_instr_limit: _,
+        remap_debuginfo: _,
+        test_compare_mode: _,
+        llvm_libunwind: _,
+        control_flow_guard: _,
+        ehcont_guard: _,
+        new_symbol_mangling: _,
+        profile_generate: _,
+        profile_use: _,
+        download_rustc: _,
+        validate_mir_opts: _,
+        frame_pointers: _,
+    } = rust;
+
+    // There are two kinds of checks for CI rustc incompatible options:
+    //    1. Checking an option that may change the compiler behaviour/output.
+    //    2. Checking an option that have no effect on the compiler behaviour/output.
+    //
+    // If the option belongs to the first category, we call `err` macro for a hard error;
+    // otherwise, we just print a warning with `warn` macro.
+    err!(optimize);
+    err!(debug_logging);
+    err!(debuginfo_level_rustc);
+    err!(default_linker);
+    err!(rpath);
+    err!(strip);
+    err!(stack_protector);
+    err!(lld_mode);
+    err!(llvm_tools);
+    err!(llvm_bitcode_linker);
+    err!(jemalloc);
+    err!(lto);
+
+    warn!(channel);
+    warn!(description);
+    warn!(incremental);
+}
+
 fn set<T>(field: &mut T, val: Option<T>) {
     if let Some(v) = val {
         *field = v;
diff --git a/src/bootstrap/src/core/config/flags.rs b/src/bootstrap/src/core/config/flags.rs
index aeb608a9ea2..19f752da81c 100644
--- a/src/bootstrap/src/core/config/flags.rs
+++ b/src/bootstrap/src/core/config/flags.rs
@@ -1,4 +1,4 @@
-//! Command-line interface of the rustbuild build system.
+//! Command-line interface of the bootstrap build system.
 //!
 //! This module implements the command-line parsing of the build system which
 //! has various flags to configure how it's run.
diff --git a/src/bootstrap/src/core/download.rs b/src/bootstrap/src/core/download.rs
index a7f4bb0cf14..d01a910e815 100644
--- a/src/bootstrap/src/core/download.rs
+++ b/src/bootstrap/src/core/download.rs
@@ -702,9 +702,13 @@ download-rustc = false
             // time `rustc_llvm` build script ran. However, the timestamps of the
             // files in the tarball are in the past, so it doesn't trigger a
             // rebuild.
-            let now = filetime::FileTime::from_system_time(std::time::SystemTime::now());
+            let now = std::time::SystemTime::now();
+            let file_times = fs::FileTimes::new().set_accessed(now).set_modified(now);
+
             let llvm_config = llvm_root.join("bin").join(exe("llvm-config", self.build));
-            t!(filetime::set_file_times(llvm_config, now, now));
+            let llvm_config_file = t!(File::open(llvm_config));
+
+            t!(llvm_config_file.set_times(file_times));
 
             if self.should_fix_bins_and_dylibs() {
                 let llvm_lib = llvm_root.join("lib");
diff --git a/src/bootstrap/src/core/sanity.rs b/src/bootstrap/src/core/sanity.rs
index 9995da3a1e5..2be819d52ea 100644
--- a/src/bootstrap/src/core/sanity.rs
+++ b/src/bootstrap/src/core/sanity.rs
@@ -1,4 +1,4 @@
-//! Sanity checking performed by rustbuild before actually executing anything.
+//! Sanity checking performed by bootstrap before actually executing anything.
 //!
 //! This module contains the implementation of ensuring that the build
 //! environment looks reasonable before progressing. This will verify that
@@ -209,7 +209,7 @@ than building it.
 
     #[cfg(not(feature = "bootstrap-self-test"))]
     let stage0_supported_target_list: HashSet<String> = crate::utils::helpers::output(
-        &mut command(&build.config.initial_rustc).args(["--print", "target-list"]).command,
+        command(&build.config.initial_rustc).args(["--print", "target-list"]).as_command_mut(),
     )
     .lines()
     .map(|s| s.to_string())
diff --git a/src/bootstrap/src/lib.rs b/src/bootstrap/src/lib.rs
index f16afb29796..db1fa05a82c 100644
--- a/src/bootstrap/src/lib.rs
+++ b/src/bootstrap/src/lib.rs
@@ -1,9 +1,9 @@
-//! Implementation of rustbuild, the Rust build system.
+//! Implementation of bootstrap, the Rust build system.
 //!
 //! This module, and its descendants, are the implementation of the Rust build
 //! system. Most of this build system is backed by Cargo but the outer layer
 //! here serves as the ability to orchestrate calling Cargo, sequencing Cargo
-//! builds, building artifacts like LLVM, etc. The goals of rustbuild are:
+//! builds, building artifacts like LLVM, etc. The goals of bootstrap are:
 //!
 //! * To be an easily understandable, easily extensible, and maintainable build
 //!   system.
@@ -23,21 +23,20 @@ use std::fmt::Display;
 use std::fs::{self, File};
 use std::io;
 use std::path::{Path, PathBuf};
-use std::process::{Command, Stdio};
+use std::process::Command;
 use std::str;
 use std::sync::OnceLock;
 use std::time::SystemTime;
 
 use build_helper::ci::{gha, CiEnv};
 use build_helper::exit;
-use filetime::FileTime;
 use sha2::digest::Digest;
 use termcolor::{ColorChoice, StandardStream, WriteColor};
 use utils::channel::GitInfo;
 use utils::helpers::hex_encode;
 
 use crate::core::builder;
-use crate::core::builder::Kind;
+use crate::core::builder::{Builder, Kind};
 use crate::core::config::{flags, LldMode};
 use crate::core::config::{DryRun, Target};
 use crate::core::config::{LlvmLibunwind, TargetSelection};
@@ -490,15 +489,29 @@ impl Build {
             return;
         }
 
-        let submodule_git = || helpers::git(Some(&absolute_path));
+        // Submodule updating actually happens during in the dry run mode. We need to make sure that
+        // all the git commands below are actually executed, because some follow-up code
+        // in bootstrap might depend on the submodules being checked out. Furthermore, not all
+        // the command executions below work with an empty output (produced during dry run).
+        // Therefore, all commands below are marked with `run_always()`, so that they also run in
+        // dry run mode.
+        let submodule_git = || {
+            let mut cmd = helpers::git(Some(&absolute_path)).capture_stdout();
+            cmd.run_always();
+            cmd
+        };
 
         // Determine commit checked out in submodule.
-        let checked_out_hash = output(&mut submodule_git().args(["rev-parse", "HEAD"]).command);
+        let checked_out_hash = submodule_git().args(["rev-parse", "HEAD"]).run(self).stdout();
         let checked_out_hash = checked_out_hash.trim_end();
         // Determine commit that the submodule *should* have.
-        let recorded = output(
-            &mut helpers::git(Some(&self.src)).args(["ls-tree", "HEAD"]).arg(relative_path).command,
-        );
+        let recorded = helpers::git(Some(&self.src))
+            .capture_stdout()
+            .run_always()
+            .args(["ls-tree", "HEAD"])
+            .arg(relative_path)
+            .run(self)
+            .stdout();
         let actual_hash = recorded
             .split_whitespace()
             .nth(2)
@@ -511,6 +524,7 @@ impl Build {
 
         println!("Updating submodule {}", relative_path.display());
         helpers::git(Some(&self.src))
+            .run_always()
             .args(["submodule", "-q", "sync"])
             .arg(relative_path)
             .run(self);
@@ -519,21 +533,16 @@ impl Build {
         let update = |progress: bool| {
             // Git is buggy and will try to fetch submodules from the tracking branch for *this* repository,
             // even though that has no relation to the upstream for the submodule.
-            let current_branch = {
-                let output = helpers::git(Some(&self.src))
-                    .args(["symbolic-ref", "--short", "HEAD"])
-                    .command
-                    .stderr(Stdio::inherit())
-                    .output();
-                let output = t!(output);
-                if output.status.success() {
-                    Some(String::from_utf8(output.stdout).unwrap().trim().to_owned())
-                } else {
-                    None
-                }
-            };
+            let current_branch = helpers::git(Some(&self.src))
+                .capture_stdout()
+                .run_always()
+                .args(["symbolic-ref", "--short", "HEAD"])
+                .run(self)
+                .stdout_if_ok()
+                .map(|s| s.trim().to_owned());
 
-            let mut git = helpers::git(Some(&self.src));
+            let mut git = helpers::git(Some(&self.src)).allow_failure();
+            git.run_always();
             if let Some(branch) = current_branch {
                 // If there is a tag named after the current branch, git will try to disambiguate by prepending `heads/` to the branch name.
                 // This syntax isn't accepted by `branch.{branch}`. Strip it.
@@ -547,8 +556,7 @@ impl Build {
             git.arg(relative_path);
             git
         };
-        // NOTE: doesn't use `try_run` because this shouldn't print an error if it fails.
-        if !update(true).command.status().map_or(false, |status| status.success()) {
+        if !update(true).run(self).is_success() {
             update(false).run(self);
         }
 
@@ -934,17 +942,26 @@ impl Build {
 
     /// Execute a command and return its output.
     /// This method should be used for all command executions in bootstrap.
+    #[track_caller]
     fn run(&self, command: &mut BootstrapCommand) -> CommandOutput {
+        command.mark_as_executed();
         if self.config.dry_run() && !command.run_always {
             return CommandOutput::default();
         }
 
-        self.verbose(|| println!("running: {command:?}"));
+        let created_at = command.get_created_location();
+        let executed_at = std::panic::Location::caller();
+
+        self.verbose(|| {
+            println!("running: {command:?} (created at {created_at}, executed at {executed_at})")
+        });
 
-        command.command.stdout(command.stdout.stdio());
-        command.command.stderr(command.stderr.stdio());
+        let stdout = command.stdout.stdio();
+        command.as_command_mut().stdout(stdout);
+        let stderr = command.stderr.stdio();
+        command.as_command_mut().stderr(stderr);
 
-        let output = command.command.output();
+        let output = command.as_command_mut().output();
 
         use std::fmt::Write;
 
@@ -956,8 +973,11 @@ impl Build {
             Ok(output) => {
                 writeln!(
                     message,
-                    "\n\nCommand {command:?} did not execute successfully.\
-            \nExpected success, got: {}",
+                    r#"
+Command {command:?} did not execute successfully.
+Expected success, got {}
+Created at: {created_at}
+Executed at: {executed_at}"#,
                     output.status,
                 )
                 .unwrap();
@@ -1693,9 +1713,13 @@ impl Build {
                 panic!("failed to copy `{}` to `{}`: {}", src.display(), dst.display(), e)
             }
             t!(fs::set_permissions(dst, metadata.permissions()));
-            let atime = FileTime::from_last_access_time(&metadata);
-            let mtime = FileTime::from_last_modification_time(&metadata);
-            t!(filetime::set_file_times(dst, atime, mtime));
+
+            let file_times = fs::FileTimes::new()
+                .set_accessed(t!(metadata.accessed()))
+                .set_modified(t!(metadata.modified()));
+
+            let dst_file = t!(fs::File::open(dst));
+            t!(dst_file.set_times(file_times));
         }
     }
 
@@ -1928,22 +1952,28 @@ fn envify(s: &str) -> String {
 ///
 /// In case of errors during `git` command execution (e.g., in tarball sources), default values
 /// are used to prevent panics.
-pub fn generate_smart_stamp_hash(dir: &Path, additional_input: &str) -> String {
+pub fn generate_smart_stamp_hash(
+    builder: &Builder<'_>,
+    dir: &Path,
+    additional_input: &str,
+) -> String {
     let diff = helpers::git(Some(dir))
+        .capture_stdout()
+        .allow_failure()
         .arg("diff")
-        .command
-        .output()
-        .map(|o| String::from_utf8(o.stdout).unwrap_or_default())
+        .run(builder)
+        .stdout_if_ok()
         .unwrap_or_default();
 
     let status = helpers::git(Some(dir))
+        .capture_stdout()
+        .allow_failure()
         .arg("status")
         .arg("--porcelain")
         .arg("-z")
         .arg("--untracked-files=normal")
-        .command
-        .output()
-        .map(|o| String::from_utf8(o.stdout).unwrap_or_default())
+        .run(builder)
+        .stdout_if_ok()
         .unwrap_or_default();
 
     let mut hasher = sha2::Sha256::new();
diff --git a/src/bootstrap/src/utils/channel.rs b/src/bootstrap/src/utils/channel.rs
index 2ca86bdb0ed..f8bcb584991 100644
--- a/src/bootstrap/src/utils/channel.rs
+++ b/src/bootstrap/src/utils/channel.rs
@@ -45,7 +45,7 @@ impl GitInfo {
         }
 
         // Make sure git commands work
-        match helpers::git(Some(dir)).arg("rev-parse").command.output() {
+        match helpers::git(Some(dir)).arg("rev-parse").as_command_mut().output() {
             Ok(ref out) if out.status.success() => {}
             _ => return GitInfo::Absent,
         }
@@ -58,16 +58,17 @@ impl GitInfo {
 
         // Ok, let's scrape some info
         let ver_date = output(
-            &mut helpers::git(Some(dir))
+            helpers::git(Some(dir))
                 .arg("log")
                 .arg("-1")
                 .arg("--date=short")
                 .arg("--pretty=format:%cd")
-                .command,
+                .as_command_mut(),
         );
-        let ver_hash = output(&mut helpers::git(Some(dir)).arg("rev-parse").arg("HEAD").command);
+        let ver_hash =
+            output(helpers::git(Some(dir)).arg("rev-parse").arg("HEAD").as_command_mut());
         let short_ver_hash = output(
-            &mut helpers::git(Some(dir)).arg("rev-parse").arg("--short=9").arg("HEAD").command,
+            helpers::git(Some(dir)).arg("rev-parse").arg("--short=9").arg("HEAD").as_command_mut(),
         );
         GitInfo::Present(Some(Info {
             commit_date: ver_date.trim().to_string(),
diff --git a/src/bootstrap/src/utils/exec.rs b/src/bootstrap/src/utils/exec.rs
index ba963f52dc2..a60c0084f3d 100644
--- a/src/bootstrap/src/utils/exec.rs
+++ b/src/bootstrap/src/utils/exec.rs
@@ -1,5 +1,8 @@
 use crate::Build;
+use build_helper::ci::CiEnv;
+use build_helper::drop_bomb::DropBomb;
 use std::ffi::OsStr;
+use std::fmt::{Debug, Formatter};
 use std::path::Path;
 use std::process::{Command, CommandArgs, CommandEnvs, ExitStatus, Output, Stdio};
 
@@ -53,17 +56,20 @@ impl OutputMode {
 ///
 /// [allow_failure]: BootstrapCommand::allow_failure
 /// [delay_failure]: BootstrapCommand::delay_failure
-#[derive(Debug)]
 pub struct BootstrapCommand {
-    pub command: Command,
+    command: Command,
     pub failure_behavior: BehaviorOnFailure,
     pub stdout: OutputMode,
     pub stderr: OutputMode,
     // Run the command even during dry run
     pub run_always: bool,
+    // This field makes sure that each command is executed (or disarmed) before it is dropped,
+    // to avoid forgetting to execute a command.
+    drop_bomb: DropBomb,
 }
 
 impl BootstrapCommand {
+    #[track_caller]
     pub fn new<S: AsRef<OsStr>>(program: S) -> Self {
         Command::new(program).into()
     }
@@ -142,19 +148,67 @@ impl BootstrapCommand {
     }
 
     /// Run the command, returning its output.
+    #[track_caller]
     pub fn run(&mut self, builder: &Build) -> CommandOutput {
         builder.run(self)
     }
+
+    /// Provides access to the stdlib Command inside.
+    /// FIXME: This function should be eventually removed from bootstrap.
+    pub fn as_command_mut(&mut self) -> &mut Command {
+        // We don't know what will happen with the returned command, so we need to mark this
+        // command as executed proactively.
+        self.mark_as_executed();
+        &mut self.command
+    }
+
+    /// Mark the command as being executed, disarming the drop bomb.
+    /// If this method is not called before the command is dropped, its drop will panic.
+    pub fn mark_as_executed(&mut self) {
+        self.drop_bomb.defuse();
+    }
+
+    /// Returns the source code location where this command was created.
+    pub fn get_created_location(&self) -> std::panic::Location<'static> {
+        self.drop_bomb.get_created_location()
+    }
+
+    /// If in a CI environment, forces the command to run with colors.
+    pub fn force_coloring_in_ci(&mut self, ci_env: CiEnv) {
+        if ci_env != CiEnv::None {
+            // Due to use of stamp/docker, the output stream of bootstrap is not
+            // a TTY in CI, so coloring is by-default turned off.
+            // The explicit `TERM=xterm` environment is needed for
+            // `--color always` to actually work. This env var was lost when
+            // compiling through the Makefile. Very strange.
+            self.env("TERM", "xterm").args(["--color", "always"]);
+        }
+    }
+}
+
+impl Debug for BootstrapCommand {
+    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
+        write!(f, "{:?}", self.command)?;
+        write!(
+            f,
+            " (failure_mode={:?}, stdout_mode={:?}, stderr_mode={:?})",
+            self.failure_behavior, self.stdout, self.stderr
+        )
+    }
 }
 
 impl From<Command> for BootstrapCommand {
+    #[track_caller]
     fn from(command: Command) -> Self {
+        let program = command.get_program().to_owned();
+
         Self {
             command,
             failure_behavior: BehaviorOnFailure::Exit,
             stdout: OutputMode::Print,
             stderr: OutputMode::Print,
             run_always: false,
+            drop_bomb: DropBomb::arm(program),
         }
     }
 }
@@ -169,6 +223,7 @@ enum CommandStatus {
 
 /// Create a new BootstrapCommand. This is a helper function to make command creation
 /// shorter than `BootstrapCommand::new`.
+#[track_caller]
 #[must_use]
 pub fn command<S: AsRef<OsStr>>(program: S) -> BootstrapCommand {
     BootstrapCommand::new(program)
diff --git a/src/bootstrap/src/utils/helpers.rs b/src/bootstrap/src/utils/helpers.rs
index 5dd3ba96786..3c82fa189be 100644
--- a/src/bootstrap/src/utils/helpers.rs
+++ b/src/bootstrap/src/utils/helpers.rs
@@ -1,4 +1,4 @@
-//! Various utility functions used throughout rustbuild.
+//! Various utility functions used throughout bootstrap.
 //!
 //! Simple things like testing the various filesystem operations here and there,
 //! not a lot of interesting happenings here unfortunately.
@@ -244,7 +244,7 @@ pub fn is_valid_test_suite_arg<'a, P: AsRef<Path>>(
 
 // FIXME: get rid of this function
 pub fn check_run(cmd: &mut BootstrapCommand, print_cmd_on_fail: bool) -> bool {
-    let status = match cmd.command.status() {
+    let status = match cmd.as_command_mut().status() {
         Ok(status) => status,
         Err(e) => {
             println!("failed to execute command: {cmd:?}\nERROR: {e}");
@@ -338,13 +338,13 @@ fn dir_up_to_date(src: &Path, threshold: SystemTime) -> bool {
 /// When `clang-cl` is used with instrumentation, we need to add clang's runtime library resource
 /// directory to the linker flags, otherwise there will be linker errors about the profiler runtime
 /// missing. This function returns the path to that directory.
-pub fn get_clang_cl_resource_dir(clang_cl_path: &str) -> PathBuf {
+pub fn get_clang_cl_resource_dir(builder: &Builder<'_>, clang_cl_path: &str) -> PathBuf {
     // Similar to how LLVM does it, to find clang's library runtime directory:
     // - we ask `clang-cl` to locate the `clang_rt.builtins` lib.
-    let mut builtins_locator = Command::new(clang_cl_path);
+    let mut builtins_locator = command(clang_cl_path);
     builtins_locator.args(["/clang:-print-libgcc-file-name", "/clang:--rtlib=compiler-rt"]);
 
-    let clang_rt_builtins = output(&mut builtins_locator);
+    let clang_rt_builtins = builtins_locator.capture_stdout().run(builder).stdout();
     let clang_rt_builtins = Path::new(clang_rt_builtins.trim());
     assert!(
         clang_rt_builtins.exists(),
@@ -501,6 +501,7 @@ pub fn check_cfg_arg(name: &str, values: Option<&[&str]>) -> String {
 /// bootstrap-specific needs/hacks from a single source, rather than applying them on next to every
 /// git command creation, which is painful to ensure that the required change is applied
 /// on each one of them correctly.
+#[track_caller]
 pub fn git(source_dir: Option<&Path>) -> BootstrapCommand {
     let mut git = command("git");
 
diff --git a/src/bootstrap/src/utils/render_tests.rs b/src/bootstrap/src/utils/render_tests.rs
index 2e99bc68a8b..a3d0d36e754 100644
--- a/src/bootstrap/src/utils/render_tests.rs
+++ b/src/bootstrap/src/utils/render_tests.rs
@@ -33,6 +33,7 @@ pub(crate) fn try_run_tests(
     stream: bool,
 ) -> bool {
     if builder.config.dry_run() {
+        cmd.mark_as_executed();
         return true;
     }
 
@@ -50,7 +51,7 @@ pub(crate) fn try_run_tests(
 }
 
 fn run_tests(builder: &Builder<'_>, cmd: &mut BootstrapCommand, stream: bool) -> bool {
-    let cmd = &mut cmd.command;
+    let cmd = cmd.as_command_mut();
     cmd.stdout(Stdio::piped());
 
     builder.verbose(|| println!("running: {cmd:?}"));
diff --git a/src/bootstrap/src/utils/tarball.rs b/src/bootstrap/src/utils/tarball.rs
index 4f0104e4bba..9378c35127f 100644
--- a/src/bootstrap/src/utils/tarball.rs
+++ b/src/bootstrap/src/utils/tarball.rs
@@ -369,13 +369,13 @@ impl<'a> Tarball<'a> {
         // gets the same timestamp.
         if self.builder.rust_info().is_managed_git_subrepository() {
             // %ct means committer date
-            let timestamp = helpers::output(
-                &mut helpers::git(Some(&self.builder.src))
-                    .arg("log")
-                    .arg("-1")
-                    .arg("--format=%ct")
-                    .command,
-            );
+            let timestamp = helpers::git(Some(&self.builder.src))
+                .capture_stdout()
+                .arg("log")
+                .arg("-1")
+                .arg("--format=%ct")
+                .run(self.builder)
+                .stdout();
             cmd.args(["--override-file-mtime", timestamp.trim()]);
         }
 
diff --git a/src/ci/docker/host-x86_64/dist-riscv64-linux/Dockerfile b/src/ci/docker/host-x86_64/dist-riscv64-linux/Dockerfile
index 426e601f5d3..4d9334dde8c 100644
--- a/src/ci/docker/host-x86_64/dist-riscv64-linux/Dockerfile
+++ b/src/ci/docker/host-x86_64/dist-riscv64-linux/Dockerfile
@@ -11,6 +11,7 @@ RUN sh /scripts/rustbuild-setup.sh
 WORKDIR /tmp
 
 COPY scripts/crosstool-ng-build.sh /scripts/
+COPY host-x86_64/dist-riscv64-linux/patches/ /tmp/patches/
 COPY host-x86_64/dist-riscv64-linux/riscv64-unknown-linux-gnu.defconfig /tmp/crosstool.defconfig
 RUN /scripts/crosstool-ng-build.sh
 
diff --git a/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0001-divdi3-div-zero.patch b/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0001-divdi3-div-zero.patch
new file mode 100644
index 00000000000..f688eaf8029
--- /dev/null
+++ b/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0001-divdi3-div-zero.patch
@@ -0,0 +1,37 @@
+From 4013baf99c38f7bca06a51f8301e8fb195ccfa33 Mon Sep 17 00:00:00 2001
+From: Jim Wilson <jimw@sifive.com>
+Date: Tue, 2 Jun 2020 11:19:39 -0700
+Subject: [PATCH] RISC-V: Make __divdi3 handle div by zero same as hardware.
+
+The ISA manual specifies that divide by zero always returns -1 as the result.
+We were failing to do that when the dividend was negative.
+
+Original patch from Virginie Moser.
+
+	libgcc/
+	* config/riscv/div.S (__divdi3): For negative arguments, change bgez
+	to bgtz.
+---
+ libgcc/config/riscv/div.S | 8 +++++---
+ 1 file changed, 5 insertions(+), 3 deletions(-)
+
+diff --git a/libgcc/config/riscv/div.S b/libgcc/config/riscv/div.S
+index 151f8e273ac77..17234324c1e41 100644
+--- a/libgcc/config/riscv/div.S
++++ b/libgcc/config/riscv/div.S
+@@ -107,10 +107,12 @@ FUNC_END (__umoddi3)
+   /* Handle negative arguments to __divdi3.  */
+ .L10:
+   neg   a0, a0
+-  bgez  a1, .L12      /* Compute __udivdi3(-a0, a1), then negate the result.  */
++  /* Zero is handled as a negative so that the result will not be inverted.  */
++  bgtz  a1, .L12     /* Compute __udivdi3(-a0, a1), then negate the result.  */
++
+   neg   a1, a1
+-  j     __udivdi3     /* Compute __udivdi3(-a0, -a1).  */
+-.L11:                 /* Compute __udivdi3(a0, -a1), then negate the result.  */
++  j     __udivdi3    /* Compute __udivdi3(-a0, -a1).  */
++.L11:                /* Compute __udivdi3(a0, -a1), then negate the result.  */
+   neg   a1, a1
+ .L12:
+   move  t0, ra
diff --git a/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0002-hidden-jump-target.patch b/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0002-hidden-jump-target.patch
new file mode 100644
index 00000000000..7ae4469428b
--- /dev/null
+++ b/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/gcc/8.5.0/0002-hidden-jump-target.patch
@@ -0,0 +1,117 @@
+From 45116f342057b7facecd3d05c2091ce3a77eda59 Mon Sep 17 00:00:00 2001
+From: Nelson Chu <nelson.chu@sifive.com>
+Date: Mon, 29 Nov 2021 04:48:20 -0800
+Subject: [PATCH] RISC-V: jal cannot refer to a default visibility symbol for
+ shared object.
+
+This is the original binutils bugzilla report,
+https://sourceware.org/bugzilla/show_bug.cgi?id=28509
+
+And this is the first version of the proposed binutils patch,
+https://sourceware.org/pipermail/binutils/2021-November/118398.html
+
+After applying the binutils patch, I get the the unexpected error when
+building libgcc,
+
+/scratch/nelsonc/riscv-gnu-toolchain/riscv-gcc/libgcc/config/riscv/div.S:42:
+/scratch/nelsonc/build-upstream/rv64gc-linux/build-install/riscv64-unknown-linux-gnu/bin/ld: relocation R_RISCV_JAL against `__udivdi3' which may bind externally can not be used when making a shared object; recompile with -fPIC
+
+Therefore, this patch add an extra hidden alias symbol for __udivdi3, and
+then use HIDDEN_JUMPTARGET to target a non-preemptible symbol instead.
+The solution is similar to glibc as follows,
+https://sourceware.org/git/?p=glibc.git;a=commit;h=68389203832ab39dd0dbaabbc4059e7fff51c29b
+
+libgcc/ChangeLog:
+
+	* config/riscv/div.S: Add the hidden alias symbol for __udivdi3, and
+	then use HIDDEN_JUMPTARGET to target it since it is non-preemptible.
+	* config/riscv/riscv-asm.h: Added new macros HIDDEN_JUMPTARGET and
+	HIDDEN_DEF.
+---
+ libgcc/config/riscv/div.S       | 15 ++++++++-------
+ libgcc/config/riscv/riscv-asm.h |  6 ++++++
+ 2 files changed, 14 insertions(+), 7 deletions(-)
+
+diff --git a/libgcc/config/riscv/div.S b/libgcc/config/riscv/div.S
+index c9bd7879c1e36..723c3b82e48c6 100644
+--- a/libgcc/config/riscv/div.S
++++ b/libgcc/config/riscv/div.S
+@@ -40,7 +40,7 @@ FUNC_BEGIN (__udivsi3)
+   sll    a0, a0, 32
+   sll    a1, a1, 32
+   move   t0, ra
+-  jal    __udivdi3
++  jal    HIDDEN_JUMPTARGET(__udivdi3)
+   sext.w a0, a0
+   jr     t0
+ FUNC_END (__udivsi3)
+@@ -52,7 +52,7 @@ FUNC_BEGIN (__umodsi3)
+   srl    a0, a0, 32
+   srl    a1, a1, 32
+   move   t0, ra
+-  jal    __udivdi3
++  jal    HIDDEN_JUMPTARGET(__udivdi3)
+   sext.w a0, a1
+   jr     t0
+ FUNC_END (__umodsi3)
+@@ -95,11 +95,12 @@ FUNC_BEGIN (__udivdi3)
+ .L5:
+   ret
+ FUNC_END (__udivdi3)
++HIDDEN_DEF (__udivdi3)
+ 
+ FUNC_BEGIN (__umoddi3)
+   /* Call __udivdi3(a0, a1), then return the remainder, which is in a1.  */
+   move  t0, ra
+-  jal   __udivdi3
++  jal   HIDDEN_JUMPTARGET(__udivdi3)
+   move  a0, a1
+   jr    t0
+ FUNC_END (__umoddi3)
+@@ -111,12 +112,12 @@ FUNC_END (__umoddi3)
+   bgtz  a1, .L12     /* Compute __udivdi3(-a0, a1), then negate the result.  */
+ 
+   neg   a1, a1
+-  j     __udivdi3    /* Compute __udivdi3(-a0, -a1).  */
++  j     HIDDEN_JUMPTARGET(__udivdi3)     /* Compute __udivdi3(-a0, -a1).  */
+ .L11:                /* Compute __udivdi3(a0, -a1), then negate the result.  */
+   neg   a1, a1
+ .L12:
+   move  t0, ra
+-  jal   __udivdi3
++  jal   HIDDEN_JUMPTARGET(__udivdi3)
+   neg   a0, a0
+   jr    t0
+ FUNC_END (__divdi3)
+@@ -126,7 +127,7 @@ FUNC_BEGIN (__moddi3)
+   bltz   a1, .L31
+   bltz   a0, .L32
+ .L30:
+-  jal    __udivdi3    /* The dividend is not negative.  */
++  jal    HIDDEN_JUMPTARGET(__udivdi3)    /* The dividend is not negative.  */
+   move   a0, a1
+   jr     t0
+ .L31:
+@@ -134,7 +135,7 @@ FUNC_BEGIN (__moddi3)
+   bgez   a0, .L30
+ .L32:
+   neg    a0, a0
+-  jal    __udivdi3    /* The dividend is hella negative.  */
++  jal    HIDDEN_JUMPTARGET(__udivdi3)    /* The dividend is hella negative.  */
+   neg    a0, a1
+   jr     t0
+ FUNC_END (__moddi3)
+diff --git a/libgcc/config/riscv/riscv-asm.h b/libgcc/config/riscv/riscv-asm.h
+index 8550707a4a26a..96dd85b0df2e5 100644
+--- a/libgcc/config/riscv/riscv-asm.h
++++ b/libgcc/config/riscv/riscv-asm.h
+@@ -33,3 +33,9 @@ see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
+ #define FUNC_ALIAS(X,Y)		\
+ 	.globl X;		\
+ 	X = Y
++
++#define CONCAT1(a, b)		CONCAT2(a, b)
++#define CONCAT2(a, b)		a ## b
++#define HIDDEN_JUMPTARGET(X)	CONCAT1(__hidden_, X)
++#define HIDDEN_DEF(X)		FUNC_ALIAS(HIDDEN_JUMPTARGET(X), X);     \
++				.hidden HIDDEN_JUMPTARGET(X)
diff --git a/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/glibc/2.29/0001-hidden-jump-target.patch b/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/glibc/2.29/0001-hidden-jump-target.patch
new file mode 100644
index 00000000000..d267b961d34
--- /dev/null
+++ b/src/ci/docker/host-x86_64/dist-riscv64-linux/patches/glibc/2.29/0001-hidden-jump-target.patch
@@ -0,0 +1,58 @@
+From 68389203832ab39dd0dbaabbc4059e7fff51c29b Mon Sep 17 00:00:00 2001
+From: Fangrui Song <maskray@google.com>
+Date: Thu, 28 Oct 2021 11:39:49 -0700
+Subject: [PATCH] riscv: Fix incorrect jal with HIDDEN_JUMPTARGET
+
+A non-local STV_DEFAULT defined symbol is by default preemptible in a
+shared object. j/jal cannot target a preemptible symbol. On other
+architectures, such a jump instruction either causes PLT [BZ #18822], or
+if short-ranged, sometimes rejected by the linker (but not by GNU ld's
+riscv port [ld PR/28509]).
+
+Use HIDDEN_JUMPTARGET to target a non-preemptible symbol instead.
+
+With this patch, ld.so and libc.so can be linked with LLD if source
+files are compiled/assembled with -mno-relax/-Wa,-mno-relax.
+
+Acked-by: Palmer Dabbelt <palmer@dabbelt.com>
+Reviewed-by: Adhemerval Zanella  <adhemerval.zanella@linaro.org>
+---
+ sysdeps/riscv/setjmp.S                     | 2 +-
+ sysdeps/unix/sysv/linux/riscv/setcontext.S | 5 +++--
+ 2 files changed, 4 insertions(+), 3 deletions(-)
+
+diff --git a/sysdeps/riscv/setjmp.S b/sysdeps/riscv/setjmp.S
+index 0b92016b311..bec7ff80f49 100644
+--- a/sysdeps/riscv/setjmp.S
++++ b/sysdeps/riscv/setjmp.S
+@@ -21,7 +21,7 @@
+ 
+ ENTRY (_setjmp)
+   li	a1, 0
+-  j	__sigsetjmp
++  j	HIDDEN_JUMPTARGET (__sigsetjmp)
+ END (_setjmp)
+ ENTRY (setjmp)
+   li	a1, 1
+diff --git a/sysdeps/unix/sysv/linux/riscv/setcontext.S b/sysdeps/unix/sysv/linux/riscv/setcontext.S
+index 9510518750a..e44a68aad47 100644
+--- a/sysdeps/unix/sysv/linux/riscv/setcontext.S
++++ b/sysdeps/unix/sysv/linux/riscv/setcontext.S
+@@ -95,6 +95,7 @@ LEAF (__setcontext)
+ 99:	j	__syscall_error
+ 
+ END (__setcontext)
++libc_hidden_def (__setcontext)
+ weak_alias (__setcontext, setcontext)
+ 
+ LEAF (__start_context)
+@@ -108,7 +109,7 @@ LEAF (__start_context)
+ 	/* Invoke subsequent context if present, else exit(0).  */
+ 	mv	a0, s2
+ 	beqz	s2, 1f
+-	jal	__setcontext
+-1:	j	exit
++	jal	HIDDEN_JUMPTARGET (__setcontext)
++1:	j	HIDDEN_JUMPTARGET (exit)
+ 
+ END (__start_context)
diff --git a/src/ci/docker/host-x86_64/dist-riscv64-linux/riscv64-unknown-linux-gnu.defconfig b/src/ci/docker/host-x86_64/dist-riscv64-linux/riscv64-unknown-linux-gnu.defconfig
index 470cef1a84e..f7c93a9d5fc 100644
--- a/src/ci/docker/host-x86_64/dist-riscv64-linux/riscv64-unknown-linux-gnu.defconfig
+++ b/src/ci/docker/host-x86_64/dist-riscv64-linux/riscv64-unknown-linux-gnu.defconfig
@@ -3,6 +3,8 @@ CT_EXPERIMENTAL=y
 CT_PREFIX_DIR="/x-tools/${CT_TARGET}"
 CT_USE_MIRROR=y
 CT_MIRROR_BASE_URL="https://ci-mirrors.rust-lang.org/rustc"
+CT_PATCH_BUNDLED_LOCAL=y
+CT_LOCAL_PATCH_DIR="/tmp/patches"
 CT_ARCH_RISCV=y
 # CT_DEMULTILIB is not set
 CT_ARCH_USE_MMU=y
@@ -10,7 +12,7 @@ CT_ARCH_64=y
 CT_ARCH_ARCH="rv64gc"
 CT_KERNEL_LINUX=y
 CT_LINUX_V_4_20=y
-CT_BINUTILS_V_2_36=y
+CT_BINUTILS_V_2_40=y
 CT_GLIBC_V_2_29=y
 CT_GCC_V_8=y
 CT_CC_LANG_CXX=y
diff --git a/src/ci/scripts/install-clang.sh b/src/ci/scripts/install-clang.sh
index 24b9904d65c..6103aa61248 100755
--- a/src/ci/scripts/install-clang.sh
+++ b/src/ci/scripts/install-clang.sh
@@ -40,7 +40,7 @@ if isMacOS; then
     # our own clang can figure out the correct include path on its own.
     ciCommandSetEnv SDKROOT "$(xcrun --sdk macosx --show-sdk-path)"
 
-    # Configure `AR` specifically so rustbuild doesn't try to infer it as
+    # Configure `AR` specifically so bootstrap doesn't try to infer it as
     # `clang-ar` by accident.
     ciCommandSetEnv AR "ar"
 elif isWindows && ! isKnownToBeMingwBuild; then
diff --git a/src/doc/book b/src/doc/book
-Subproject f1e49bf7a8ea6c31ce016a52b8a4f6e1ffcfbc6
+Subproject 67fa536768013d9d5a13f3a06790521d511ef71
diff --git a/src/doc/edition-guide b/src/doc/edition-guide
-Subproject 941db8b3df45fd46cd87b50a5c86714b91dcde9
+Subproject 5454de3d12b9ccc6375b629cf7ccda8264640aa
diff --git a/src/doc/embedded-book b/src/doc/embedded-book
-Subproject b10c6acaf0f43481f6600e95d4b5013446e29f7
+Subproject 019f3928d8b939ec71b63722dcc2e4633015644
diff --git a/src/doc/reference b/src/doc/reference
-Subproject 1ae3deebc3ac16e276b6558e01420f8e605def0
+Subproject e2f0bdc4031866734661dcdb548184bde1450ba
diff --git a/src/doc/rust-by-example b/src/doc/rust-by-example
-Subproject 658c6c27cb975b92227936024816986c2d3716f
+Subproject 89aecb6951b77bc746da73df8c9f2b2ceaad494
diff --git a/src/doc/rustc-dev-guide b/src/doc/rustc-dev-guide
-Subproject d6e3a32a557db5902e714604def8015d6bb7e0f
+Subproject 0c4d55cb59fe440d1a630e4e5774d043968edb3
diff --git a/src/doc/rustc/src/platform-support.md b/src/doc/rustc/src/platform-support.md
index f5cd4bd217a..370dbed50fa 100644
--- a/src/doc/rustc/src/platform-support.md
+++ b/src/doc/rustc/src/platform-support.md
@@ -41,10 +41,10 @@ target | notes
 `x86_64-pc-windows-msvc` | 64-bit MSVC (Windows 10+, Windows Server 2016+)
 `x86_64-unknown-linux-gnu` | 64-bit Linux (kernel 3.2+, glibc 2.17+)
 
-[^x86_32-floats-return-ABI]: Due to limitations of the C ABI, floating-point support on `i686` targets is non-compliant: floating-point return values are passed via an x87 register, so NaN payload bits can be lost. See [issue #114479][x86-32-float-issue].
+[^x86_32-floats-return-ABI]: Due to limitations of the C ABI, floating-point support on `i686` targets is non-compliant: floating-point return values are passed via an x87 register, so NaN payload bits can be lost. Functions with the default Rust ABI are not affected. See [issue #115567][x86-32-float-return-issue].
 
 [77071]: https://github.com/rust-lang/rust/issues/77071
-[x86-32-float-issue]: https://github.com/rust-lang/rust/issues/114479
+[x86-32-float-return-issue]: https://github.com/rust-lang/rust/issues/115567
 
 ## Tier 1
 
@@ -209,6 +209,8 @@ target | std | notes
 
 [^x86_32-floats-x87]: Floating-point support on `i586` targets is non-compliant: the `x87` registers and instructions used for these targets do not provide IEEE-754-compliant behavior, in particular when it comes to rounding and NaN payload bits. See [issue #114479][x86-32-float-issue].
 
+[x86-32-float-issue]: https://github.com/rust-lang/rust/issues/114479
+
 [wasi-rename]: https://github.com/rust-lang/compiler-team/issues/607
 
 [Fortanix ABI]: https://edp.fortanix.com/
diff --git a/src/doc/rustc/src/platform-support/x86_64-fortanix-unknown-sgx.md b/src/doc/rustc/src/platform-support/x86_64-fortanix-unknown-sgx.md
index 97b5827c144..33e1c44e6d3 100644
--- a/src/doc/rustc/src/platform-support/x86_64-fortanix-unknown-sgx.md
+++ b/src/doc/rustc/src/platform-support/x86_64-fortanix-unknown-sgx.md
@@ -46,7 +46,7 @@ on how to setup a development and runtime environment.
 
 As a tier 2 target, the target is built by the Rust project.
 
-You can configure rustbuild like so:
+You can configure bootstrap like so:
 
 ```toml
 [build]
diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs
index aa596897fc4..a0e28d2f55c 100644
--- a/src/librustdoc/clean/mod.rs
+++ b/src/librustdoc/clean/mod.rs
@@ -228,8 +228,9 @@ fn clean_generic_bound<'tcx>(
 
             GenericBound::TraitBound(clean_poly_trait_ref(t, cx), modifier)
         }
-        // FIXME(precise_capturing): Implement rustdoc support
-        hir::GenericBound::Use(..) => return None,
+        hir::GenericBound::Use(args, ..) => {
+            GenericBound::Use(args.iter().map(|arg| arg.name()).collect())
+        }
     })
 }
 
@@ -1051,12 +1052,12 @@ fn clean_fn_decl_legacy_const_generics(func: &mut Function, attrs: &[ast::Attrib
         for (pos, literal) in meta_item_list.iter().filter_map(|meta| meta.lit()).enumerate() {
             match literal.kind {
                 ast::LitKind::Int(a, _) => {
-                    let gen = func.generics.params.remove(0);
+                    let param = func.generics.params.remove(0);
                     if let GenericParamDef {
                         name,
                         kind: GenericParamDefKind::Const { ty, .. },
                         ..
-                    } = gen
+                    } = param
                     {
                         func.decl
                             .inputs
diff --git a/src/librustdoc/clean/simplify.rs b/src/librustdoc/clean/simplify.rs
index 58eef36677b..1b7d84add1f 100644
--- a/src/librustdoc/clean/simplify.rs
+++ b/src/librustdoc/clean/simplify.rs
@@ -79,7 +79,7 @@ pub(crate) fn merge_bounds(
     !bounds.iter_mut().any(|b| {
         let trait_ref = match *b {
             clean::GenericBound::TraitBound(ref mut tr, _) => tr,
-            clean::GenericBound::Outlives(..) => return false,
+            clean::GenericBound::Outlives(..) | clean::GenericBound::Use(_) => return false,
         };
         // If this QPath's trait `trait_did` is the same as, or a supertrait
         // of, the bound's trait `did` then we can keep going, otherwise
diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs
index fe01d17b08a..a31adc9949a 100644
--- a/src/librustdoc/clean/types.rs
+++ b/src/librustdoc/clean/types.rs
@@ -1244,6 +1244,8 @@ impl Eq for Attributes {}
 pub(crate) enum GenericBound {
     TraitBound(PolyTrait, hir::TraitBoundModifier),
     Outlives(Lifetime),
+    /// `use<'a, T>` precise-capturing bound syntax
+    Use(Vec<Symbol>),
 }
 
 impl GenericBound {
diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs
index 4268fadd6c5..9b0b2571ec1 100644
--- a/src/librustdoc/html/format.rs
+++ b/src/librustdoc/html/format.rs
@@ -412,6 +412,20 @@ impl clean::GenericBound {
                 })?;
                 ty.print(cx).fmt(f)
             }
+            clean::GenericBound::Use(args) => {
+                if f.alternate() {
+                    f.write_str("use<")?;
+                } else {
+                    f.write_str("use&lt;")?;
+                }
+                for (i, arg) in args.iter().enumerate() {
+                    if i > 0 {
+                        write!(f, ", ")?;
+                    }
+                    arg.fmt(f)?;
+                }
+                if f.alternate() { f.write_str(">") } else { f.write_str("&gt;") }
+            }
         })
     }
 }
diff --git a/src/librustdoc/html/render/search_index.rs b/src/librustdoc/html/render/search_index.rs
index bc8bdffc331..66d3f0fd8ce 100644
--- a/src/librustdoc/html/render/search_index.rs
+++ b/src/librustdoc/html/render/search_index.rs
@@ -1051,7 +1051,7 @@ fn simplify_fn_type<'tcx, 'a>(
         let mut ty_generics = Vec::new();
         let mut ty_constraints = Vec::new();
         if let Some(arg_generics) = arg.generic_args() {
-            for ty in arg_generics.into_iter().filter_map(|gen| match gen {
+            for ty in arg_generics.into_iter().filter_map(|param| match param {
                 clean::GenericArg::Type(ty) => Some(ty),
                 _ => None,
             }) {
@@ -1172,8 +1172,8 @@ fn simplify_fn_constraint<'tcx, 'a>(
 ) {
     let mut ty_constraints = Vec::new();
     let ty_constrained_assoc = RenderTypeId::AssociatedType(constraint.assoc.name);
-    for gen in &constraint.assoc.args {
-        match gen {
+    for param in &constraint.assoc.args {
+        match param {
             clean::GenericArg::Type(arg) => simplify_fn_type(
                 self_,
                 generics,
diff --git a/src/librustdoc/json/conversions.rs b/src/librustdoc/json/conversions.rs
index 5111e363c52..4ab0df36708 100644
--- a/src/librustdoc/json/conversions.rs
+++ b/src/librustdoc/json/conversions.rs
@@ -542,6 +542,7 @@ impl FromWithTcx<clean::GenericBound> for GenericBound {
                 }
             }
             Outlives(lifetime) => GenericBound::Outlives(convert_lifetime(lifetime)),
+            Use(args) => GenericBound::Use(args.into_iter().map(|arg| arg.to_string()).collect()),
         }
     }
 }
diff --git a/src/rustdoc-json-types/lib.rs b/src/rustdoc-json-types/lib.rs
index 89115d4d7d6..6fd23b60c8a 100644
--- a/src/rustdoc-json-types/lib.rs
+++ b/src/rustdoc-json-types/lib.rs
@@ -8,7 +8,7 @@ use serde::{Deserialize, Serialize};
 use std::path::PathBuf;
 
 /// rustdoc format-version.
-pub const FORMAT_VERSION: u32 = 31;
+pub const FORMAT_VERSION: u32 = 32;
 
 /// A `Crate` is the root of the emitted JSON blob. It contains all type/documentation information
 /// about the language items in the local crate, as well as info about external items to allow
@@ -538,6 +538,8 @@ pub enum GenericBound {
         modifier: TraitBoundModifier,
     },
     Outlives(String),
+    /// `use<'a, T>` precise-capturing bound syntax
+    Use(Vec<String>),
 }
 
 #[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
diff --git a/src/tools/build_helper/src/ci.rs b/src/tools/build_helper/src/ci.rs
index 09489b0d9b7..6d79c7c83ad 100644
--- a/src/tools/build_helper/src/ci.rs
+++ b/src/tools/build_helper/src/ci.rs
@@ -1,5 +1,3 @@
-use std::process::Command;
-
 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
 pub enum CiEnv {
     /// Not a CI environment.
@@ -21,18 +19,6 @@ impl CiEnv {
     pub fn is_ci() -> bool {
         Self::current() != CiEnv::None
     }
-
-    /// If in a CI environment, forces the command to run with colors.
-    pub fn force_coloring_in_ci(self, cmd: &mut Command) {
-        if self != CiEnv::None {
-            // Due to use of stamp/docker, the output stream of rustbuild is not
-            // a TTY in CI, so coloring is by-default turned off.
-            // The explicit `TERM=xterm` environment is needed for
-            // `--color always` to actually work. This env var was lost when
-            // compiling through the Makefile. Very strange.
-            cmd.env("TERM", "xterm").args(&["--color", "always"]);
-        }
-    }
 }
 
 pub mod gha {
diff --git a/src/tools/run-make-support/src/drop_bomb/mod.rs b/src/tools/build_helper/src/drop_bomb/mod.rs
index 2fc84892c1b..0a5bb04b55b 100644
--- a/src/tools/run-make-support/src/drop_bomb/mod.rs
+++ b/src/tools/build_helper/src/drop_bomb/mod.rs
@@ -12,27 +12,31 @@ use std::panic;
 mod tests;
 
 #[derive(Debug)]
-pub(crate) struct DropBomb {
+pub struct DropBomb {
     command: OsString,
     defused: bool,
-    armed_line: u32,
+    armed_location: panic::Location<'static>,
 }
 
 impl DropBomb {
     /// Arm a [`DropBomb`]. If the value is dropped without being [`defused`][Self::defused], then
     /// it will panic. It is expected that the command wrapper uses `#[track_caller]` to help
-    /// propagate the caller info from rmake.rs.
+    /// propagate the caller location.
     #[track_caller]
-    pub(crate) fn arm<S: AsRef<OsStr>>(command: S) -> DropBomb {
+    pub fn arm<S: AsRef<OsStr>>(command: S) -> DropBomb {
         DropBomb {
             command: command.as_ref().into(),
             defused: false,
-            armed_line: panic::Location::caller().line(),
+            armed_location: *panic::Location::caller(),
         }
     }
 
+    pub fn get_created_location(&self) -> panic::Location<'static> {
+        self.armed_location
+    }
+
     /// Defuse the [`DropBomb`]. This will prevent the drop bomb from panicking when dropped.
-    pub(crate) fn defuse(&mut self) {
+    pub fn defuse(&mut self) {
         self.defused = true;
     }
 }
@@ -41,8 +45,8 @@ impl Drop for DropBomb {
     fn drop(&mut self) {
         if !self.defused && !std::thread::panicking() {
             panic!(
-                "command constructed but not executed at line {}: `{}`",
-                self.armed_line,
+                "command constructed at `{}` was dropped without being executed: `{}`",
+                self.armed_location,
                 self.command.to_string_lossy()
             )
         }
diff --git a/src/tools/run-make-support/src/drop_bomb/tests.rs b/src/tools/build_helper/src/drop_bomb/tests.rs
index 4a488c0f670..4a488c0f670 100644
--- a/src/tools/run-make-support/src/drop_bomb/tests.rs
+++ b/src/tools/build_helper/src/drop_bomb/tests.rs
diff --git a/src/tools/build_helper/src/lib.rs b/src/tools/build_helper/src/lib.rs
index 15807d1c0d8..4a4f0ca2a9d 100644
--- a/src/tools/build_helper/src/lib.rs
+++ b/src/tools/build_helper/src/lib.rs
@@ -1,6 +1,7 @@
 //! Types and functions shared across tools in this workspace.
 
 pub mod ci;
+pub mod drop_bomb;
 pub mod git;
 pub mod metrics;
 pub mod stage0_parser;
diff --git a/src/tools/clippy/.github/workflows/lintcheck.yml b/src/tools/clippy/.github/workflows/lintcheck.yml
index 91c98b3a256..f016a770059 100644
--- a/src/tools/clippy/.github/workflows/lintcheck.yml
+++ b/src/tools/clippy/.github/workflows/lintcheck.yml
@@ -58,7 +58,7 @@ jobs:
 
     - name: Run lintcheck
       if: steps.cache-json.outputs.cache-hit != 'true'
-      run: ./target/debug/lintcheck --format json
+      run: ./target/debug/lintcheck --format json --warn-all
 
     - name: Upload base JSON
       uses: actions/upload-artifact@v4
@@ -86,7 +86,7 @@ jobs:
       run: cargo build --manifest-path=lintcheck/Cargo.toml
 
     - name: Run lintcheck
-      run: ./target/debug/lintcheck --format json
+      run: ./target/debug/lintcheck --format json --warn-all
 
     - name: Upload head JSON
       uses: actions/upload-artifact@v4
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 70ef2c79364..55281f3cbec 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -5236,6 +5236,7 @@ Released 2018-09-13
 [`boxed_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#boxed_local
 [`branches_sharing_code`]: https://rust-lang.github.io/rust-clippy/master/index.html#branches_sharing_code
 [`builtin_type_shadow`]: https://rust-lang.github.io/rust-clippy/master/index.html#builtin_type_shadow
+[`byte_char_slices`]: https://rust-lang.github.io/rust-clippy/master/index.html#byte_char_slices
 [`bytes_count_to_len`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_count_to_len
 [`bytes_nth`]: https://rust-lang.github.io/rust-clippy/master/index.html#bytes_nth
 [`cargo_common_metadata`]: https://rust-lang.github.io/rust-clippy/master/index.html#cargo_common_metadata
@@ -5253,6 +5254,7 @@ Released 2018-09-13
 [`cast_sign_loss`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_sign_loss
 [`cast_slice_different_sizes`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_different_sizes
 [`cast_slice_from_raw_parts`]: https://rust-lang.github.io/rust-clippy/master/index.html#cast_slice_from_raw_parts
+[`cfg_not_test`]: https://rust-lang.github.io/rust-clippy/master/index.html#cfg_not_test
 [`char_lit_as_u8`]: https://rust-lang.github.io/rust-clippy/master/index.html#char_lit_as_u8
 [`chars_last_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_last_cmp
 [`chars_next_cmp`]: https://rust-lang.github.io/rust-clippy/master/index.html#chars_next_cmp
@@ -5539,6 +5541,7 @@ Released 2018-09-13
 [`manual_range_patterns`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_range_patterns
 [`manual_rem_euclid`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rem_euclid
 [`manual_retain`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_retain
+[`manual_rotate`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_rotate
 [`manual_saturating_arithmetic`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_saturating_arithmetic
 [`manual_slice_size_calculation`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_slice_size_calculation
 [`manual_split_once`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_split_once
@@ -5587,6 +5590,7 @@ Released 2018-09-13
 [`missing_assert_message`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_assert_message
 [`missing_asserts_for_indexing`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_asserts_for_indexing
 [`missing_const_for_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn
+[`missing_const_for_thread_local`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_thread_local
 [`missing_docs_in_private_items`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items
 [`missing_enforced_import_renames`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_enforced_import_renames
 [`missing_errors_doc`]: https://rust-lang.github.io/rust-clippy/master/index.html#missing_errors_doc
@@ -5701,6 +5705,7 @@ Released 2018-09-13
 [`panic`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic
 [`panic_in_result_fn`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_in_result_fn
 [`panic_params`]: https://rust-lang.github.io/rust-clippy/master/index.html#panic_params
+[`panicking_overflow_checks`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_overflow_checks
 [`panicking_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#panicking_unwrap
 [`partial_pub_fields`]: https://rust-lang.github.io/rust-clippy/master/index.html#partial_pub_fields
 [`partialeq_ne_impl`]: https://rust-lang.github.io/rust-clippy/master/index.html#partialeq_ne_impl
@@ -5797,6 +5802,7 @@ Released 2018-09-13
 [`semicolon_outside_block`]: https://rust-lang.github.io/rust-clippy/master/index.html#semicolon_outside_block
 [`separated_literal_suffix`]: https://rust-lang.github.io/rust-clippy/master/index.html#separated_literal_suffix
 [`serde_api_misuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#serde_api_misuse
+[`set_contains_or_insert`]: https://rust-lang.github.io/rust-clippy/master/index.html#set_contains_or_insert
 [`shadow_reuse`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_reuse
 [`shadow_same`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_same
 [`shadow_unrelated`]: https://rust-lang.github.io/rust-clippy/master/index.html#shadow_unrelated
diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md
index cfd34c7d2a7..ad29339a84a 100644
--- a/src/tools/clippy/book/src/lint_configuration.md
+++ b/src/tools/clippy/book/src/lint_configuration.md
@@ -348,6 +348,7 @@ Suppress lints whenever the suggested change would cause breakage for other crat
 * [`enum_variant_names`](https://rust-lang.github.io/rust-clippy/master/index.html#enum_variant_names)
 * [`large_types_passed_by_value`](https://rust-lang.github.io/rust-clippy/master/index.html#large_types_passed_by_value)
 * [`linkedlist`](https://rust-lang.github.io/rust-clippy/master/index.html#linkedlist)
+* [`needless_pass_by_ref_mut`](https://rust-lang.github.io/rust-clippy/master/index.html#needless_pass_by_ref_mut)
 * [`option_option`](https://rust-lang.github.io/rust-clippy/master/index.html#option_option)
 * [`rc_buffer`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_buffer)
 * [`rc_mutex`](https://rust-lang.github.io/rust-clippy/master/index.html#rc_mutex)
@@ -454,7 +455,7 @@ default configuration of Clippy. By default, any configuration will replace the
 * `doc-valid-idents = ["ClipPy"]` would replace the default list with `["ClipPy"]`.
 * `doc-valid-idents = ["ClipPy", ".."]` would append `ClipPy` to the default list.
 
-**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DevOps", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "WebGL", "WebGL2", "WebGPU", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]`
+**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "DevOps", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "WebGL", "WebGL2", "WebGPU", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]`
 
 ---
 **Affected lints:**
diff --git a/src/tools/clippy/clippy.toml b/src/tools/clippy/clippy.toml
index 62ed55beb1f..319b72e8c5d 100644
--- a/src/tools/clippy/clippy.toml
+++ b/src/tools/clippy/clippy.toml
@@ -1,6 +1,10 @@
 avoid-breaking-exported-api = false
 
 [[disallowed-methods]]
+path = "rustc_lint::context::LintContext::lint"
+reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead"
+
+[[disallowed-methods]]
 path = "rustc_lint::context::LintContext::span_lint"
 reason = "this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead"
 
diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs
index dbab3b106a8..7f53aad6793 100644
--- a/src/tools/clippy/clippy_config/src/conf.rs
+++ b/src/tools/clippy/clippy_config/src/conf.rs
@@ -31,6 +31,7 @@ const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
     "OCaml",
     "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry",
     "WebGL", "WebGL2", "WebGPU",
+    "WebP", "OpenExr", "YCbCr", "sRGB",
     "TensorFlow",
     "TrueType",
     "iOS", "macOS", "FreeBSD",
@@ -262,7 +263,7 @@ define_Conf! {
     /// arithmetic-side-effects-allowed-unary = ["SomeType", "AnotherType"]
     /// ```
     (arithmetic_side_effects_allowed_unary: FxHashSet<String> = <_>::default()),
-    /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX, UNNECESSARY_BOX_RETURNS, SINGLE_CALL_FN.
+    /// Lint: ENUM_VARIANT_NAMES, LARGE_TYPES_PASSED_BY_VALUE, TRIVIALLY_COPY_PASS_BY_REF, UNNECESSARY_WRAPS, UNUSED_SELF, UPPER_CASE_ACRONYMS, WRONG_SELF_CONVENTION, BOX_COLLECTION, REDUNDANT_ALLOCATION, RC_BUFFER, VEC_BOX, OPTION_OPTION, LINKEDLIST, RC_MUTEX, UNNECESSARY_BOX_RETURNS, SINGLE_CALL_FN, NEEDLESS_PASS_BY_REF_MUT.
     ///
     /// Suppress lints whenever the suggested change would cause breakage for other crates.
     (avoid_breaking_exported_api: bool = true),
diff --git a/src/tools/clippy/clippy_config/src/msrvs.rs b/src/tools/clippy/clippy_config/src/msrvs.rs
index a5761d3270c..fc56ac51796 100644
--- a/src/tools/clippy/clippy_config/src/msrvs.rs
+++ b/src/tools/clippy/clippy_config/src/msrvs.rs
@@ -25,8 +25,8 @@ msrv_aliases! {
     1,68,0 { PATH_MAIN_SEPARATOR_STR }
     1,65,0 { LET_ELSE, POINTER_CAST_CONSTNESS }
     1,63,0 { CLONE_INTO }
-    1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE }
-    1,59,0 { THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST }
+    1,62,0 { BOOL_THEN_SOME, DEFAULT_ENUM_ATTRIBUTE, CONST_EXTERN_FN }
+    1,59,0 { THREAD_LOCAL_CONST_INIT }
     1,58,0 { FORMAT_ARGS_CAPTURE, PATTERN_TRAIT_CHAR_ARRAY, CONST_RAW_PTR_DEREF }
     1,56,0 { CONST_FN_UNION }
     1,55,0 { SEEK_REWIND }
diff --git a/src/tools/clippy/clippy_dev/src/new_lint.rs b/src/tools/clippy/clippy_dev/src/new_lint.rs
index 2e56eb8ec15..d762e30ef02 100644
--- a/src/tools/clippy/clippy_dev/src/new_lint.rs
+++ b/src/tools/clippy/clippy_dev/src/new_lint.rs
@@ -273,7 +273,7 @@ fn get_lint_file_contents(lint: &LintData<'_>, enable_msrv: bool) -> String {
     result.push_str(&if enable_msrv {
         formatdoc!(
             r#"
-            use clippy_utils::msrvs::{{self, Msrv}};
+            use clippy_config::msrvs::{{self, Msrv}};
             {pass_import}
             use rustc_lint::{{{context_import}, {pass_type}, LintContext}};
             use rustc_session::impl_lint_pass;
@@ -399,7 +399,7 @@ fn create_lint_for_ty(lint: &LintData<'_>, enable_msrv: bool, ty: &str) -> io::R
         let _: fmt::Result = writedoc!(
             lint_file_contents,
             r#"
-                use clippy_utils::msrvs::{{self, Msrv}};
+                use clippy_config::msrvs::{{self, Msrv}};
                 use rustc_lint::{{{context_import}, LintContext}};
 
                 use super::{name_upper};
diff --git a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
index 57a5cd8fba8..96e9c949b75 100644
--- a/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
+++ b/src/tools/clippy/clippy_lints/src/almost_complete_range.rs
@@ -6,7 +6,6 @@ use rustc_errors::Applicability;
 use rustc_lint::{EarlyContext, EarlyLintPass, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_session::impl_lint_pass;
-use rustc_span::Span;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -41,61 +40,80 @@ impl AlmostCompleteRange {
 }
 impl EarlyLintPass for AlmostCompleteRange {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, e: &Expr) {
-        if let ExprKind::Range(Some(start), Some(end), RangeLimits::HalfOpen) = &e.kind {
-            let ctxt = e.span.ctxt();
-            let sugg = if let Some(start) = walk_span_to_context(start.span, ctxt)
-                && let Some(end) = walk_span_to_context(end.span, ctxt)
-                && self.msrv.meets(msrvs::RANGE_INCLUSIVE)
-            {
-                Some((trim_span(cx.sess().source_map(), start.between(end)), "..="))
-            } else {
-                None
-            };
-            check_range(cx, e.span, start, end, sugg);
+        if let ExprKind::Range(Some(start), Some(end), RangeLimits::HalfOpen) = &e.kind
+            && is_incomplete_range(start, end)
+            && !in_external_macro(cx.sess(), e.span)
+        {
+            span_lint_and_then(
+                cx,
+                ALMOST_COMPLETE_RANGE,
+                e.span,
+                "almost complete ascii range",
+                |diag| {
+                    let ctxt = e.span.ctxt();
+                    if let Some(start) = walk_span_to_context(start.span, ctxt)
+                        && let Some(end) = walk_span_to_context(end.span, ctxt)
+                        && self.msrv.meets(msrvs::RANGE_INCLUSIVE)
+                    {
+                        diag.span_suggestion(
+                            trim_span(cx.sess().source_map(), start.between(end)),
+                            "use an inclusive range",
+                            "..=".to_owned(),
+                            Applicability::MaybeIncorrect,
+                        );
+                    }
+                },
+            );
         }
     }
 
     fn check_pat(&mut self, cx: &EarlyContext<'_>, p: &Pat) {
         if let PatKind::Range(Some(start), Some(end), kind) = &p.kind
             && matches!(kind.node, RangeEnd::Excluded)
+            && is_incomplete_range(start, end)
+            && !in_external_macro(cx.sess(), p.span)
         {
-            let sugg = if self.msrv.meets(msrvs::RANGE_INCLUSIVE) {
-                "..="
-            } else {
-                "..."
-            };
-            check_range(cx, p.span, start, end, Some((kind.span, sugg)));
+            span_lint_and_then(
+                cx,
+                ALMOST_COMPLETE_RANGE,
+                p.span,
+                "almost complete ascii range",
+                |diag| {
+                    diag.span_suggestion(
+                        kind.span,
+                        "use an inclusive range",
+                        if self.msrv.meets(msrvs::RANGE_INCLUSIVE) {
+                            "..=".to_owned()
+                        } else {
+                            "...".to_owned()
+                        },
+                        Applicability::MaybeIncorrect,
+                    );
+                },
+            );
         }
     }
 
     extract_msrv_attr!(EarlyContext);
 }
 
-fn check_range(cx: &EarlyContext<'_>, span: Span, start: &Expr, end: &Expr, sugg: Option<(Span, &str)>) {
-    if let ExprKind::Lit(start_token_lit) = start.peel_parens().kind
-        && let ExprKind::Lit(end_token_lit) = end.peel_parens().kind
-        && matches!(
-            (
-                LitKind::from_token_lit(start_token_lit),
-                LitKind::from_token_lit(end_token_lit),
-            ),
-            (
-                Ok(LitKind::Byte(b'a') | LitKind::Char('a')),
-                Ok(LitKind::Byte(b'z') | LitKind::Char('z'))
-            ) | (
-                Ok(LitKind::Byte(b'A') | LitKind::Char('A')),
-                Ok(LitKind::Byte(b'Z') | LitKind::Char('Z')),
-            ) | (
-                Ok(LitKind::Byte(b'0') | LitKind::Char('0')),
-                Ok(LitKind::Byte(b'9') | LitKind::Char('9')),
+fn is_incomplete_range(start: &Expr, end: &Expr) -> bool {
+    match (&start.peel_parens().kind, &end.peel_parens().kind) {
+        (&ExprKind::Lit(start_lit), &ExprKind::Lit(end_lit)) => {
+            matches!(
+                (LitKind::from_token_lit(start_lit), LitKind::from_token_lit(end_lit),),
+                (
+                    Ok(LitKind::Byte(b'a') | LitKind::Char('a')),
+                    Ok(LitKind::Byte(b'z') | LitKind::Char('z'))
+                ) | (
+                    Ok(LitKind::Byte(b'A') | LitKind::Char('A')),
+                    Ok(LitKind::Byte(b'Z') | LitKind::Char('Z')),
+                ) | (
+                    Ok(LitKind::Byte(b'0') | LitKind::Char('0')),
+                    Ok(LitKind::Byte(b'9') | LitKind::Char('9')),
+                )
             )
-        )
-        && !in_external_macro(cx.sess(), span)
-    {
-        span_lint_and_then(cx, ALMOST_COMPLETE_RANGE, span, "almost complete ascii range", |diag| {
-            if let Some((span, sugg)) = sugg {
-                diag.span_suggestion(span, "use an inclusive range", sugg, Applicability::MaybeIncorrect);
-            }
-        });
+        },
+        _ => false,
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
index 2003dd1fb0e..ed4cdce8cb8 100644
--- a/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
+++ b/src/tools/clippy/clippy_lints/src/assertions_on_constants.rs
@@ -1,7 +1,8 @@
-use clippy_utils::consts::{constant_with_source, Constant, ConstantSource};
+use clippy_utils::consts::{constant, Constant};
 use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_inside_always_const_context;
 use clippy_utils::macros::{find_assert_args, root_macro_call_first_node, PanicExpn};
-use rustc_hir::{Expr, Item, ItemKind, Node};
+use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::sym;
@@ -42,17 +43,16 @@ impl<'tcx> LateLintPass<'tcx> for AssertionsOnConstants {
         let Some((condition, panic_expn)) = find_assert_args(cx, e, macro_call.expn) else {
             return;
         };
-        let Some((Constant::Bool(val), source)) = constant_with_source(cx, cx.typeck_results(), condition) else {
+        let Some(Constant::Bool(val)) = constant(cx, cx.typeck_results(), condition) else {
             return;
         };
-        if let ConstantSource::Constant = source
-            && let Node::Item(Item {
-                kind: ItemKind::Const(..),
-                ..
-            }) = cx.tcx.parent_hir_node(e.hir_id)
-        {
-            return;
+
+        match condition.kind {
+            ExprKind::Path(..) | ExprKind::Lit(_) => {},
+            _ if is_inside_always_const_context(cx.tcx, e.hir_id) => return,
+            _ => {},
         }
+
         if val {
             span_lint_and_help(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs
index 406f38f411e..0de0031ed24 100644
--- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs
+++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs
@@ -1,18 +1,16 @@
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::macros::HirNode;
 use clippy_utils::mir::{enclosing_mir, PossibleBorrowerMap};
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{is_trait_method, local_is_initialized, path_to_local};
+use clippy_utils::{is_diag_trait_item, last_path_segment, local_is_initialized, path_to_local};
 use rustc_errors::Applicability;
 use rustc_hir::{self as hir, Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::mir;
 use rustc_middle::ty::{self, Instance, Mutability};
 use rustc_session::impl_lint_pass;
-use rustc_span::def_id::DefId;
 use rustc_span::symbol::sym;
-use rustc_span::{ExpnKind, Span, SyntaxContext};
+use rustc_span::{Span, SyntaxContext};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -68,165 +66,82 @@ impl AssigningClones {
 impl_lint_pass!(AssigningClones => [ASSIGNING_CLONES]);
 
 impl<'tcx> LateLintPass<'tcx> for AssigningClones {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, assign_expr: &'tcx Expr<'_>) {
-        // Do not fire the lint in macros
-        let ctxt = assign_expr.span().ctxt();
-        let expn_data = ctxt.outer_expn_data();
-        match expn_data.kind {
-            ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) | ExpnKind::Macro(..) => return,
-            ExpnKind::Root => {},
-        }
-
-        let ExprKind::Assign(lhs, rhs, _span) = assign_expr.kind else {
-            return;
-        };
-
-        let Some(call) = extract_call(cx, rhs) else {
-            return;
-        };
-
-        if is_ok_to_suggest(cx, lhs, &call, &self.msrv) {
-            suggest(cx, ctxt, assign_expr, lhs, &call);
-        }
-    }
-
-    extract_msrv_attr!(LateContext);
-}
-
-// Try to resolve the call to `Clone::clone` or `ToOwned::to_owned`.
-fn extract_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) -> Option<CallCandidate<'tcx>> {
-    let fn_def_id = clippy_utils::fn_def_id(cx, expr)?;
-
-    // Fast paths to only check method calls without arguments or function calls with a single argument
-    let (target, kind, resolved_method) = match expr.kind {
-        ExprKind::MethodCall(path, receiver, [], _span) => {
-            let args = cx.typeck_results().node_args(expr.hir_id);
-
-            // If we could not resolve the method, don't apply the lint
-            let Ok(Some(resolved_method)) = Instance::try_resolve(cx.tcx, cx.param_env, fn_def_id, args) else {
-                return None;
-            };
-            if is_trait_method(cx, expr, sym::Clone) && path.ident.name == sym::clone {
-                (TargetTrait::Clone, CallKind::MethodCall { receiver }, resolved_method)
-            } else if is_trait_method(cx, expr, sym::ToOwned) && path.ident.name.as_str() == "to_owned" {
-                (TargetTrait::ToOwned, CallKind::MethodCall { receiver }, resolved_method)
-            } else {
-                return None;
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
+        if let ExprKind::Assign(lhs, rhs, _) = e.kind
+            && let typeck = cx.typeck_results()
+            && let (call_kind, fn_name, fn_id, fn_arg, fn_gen_args) = match rhs.kind {
+                ExprKind::Call(f, [arg])
+                    if let ExprKind::Path(fn_path) = &f.kind
+                        && let Some(id) = typeck.qpath_res(fn_path, f.hir_id).opt_def_id() =>
+                {
+                    (CallKind::Ufcs, last_path_segment(fn_path).ident.name, id, arg, typeck.node_args(f.hir_id))
+                },
+                ExprKind::MethodCall(name, recv, [], _) if let Some(id) = typeck.type_dependent_def_id(rhs.hir_id) => {
+                    (CallKind::Method, name.ident.name, id, recv, typeck.node_args(rhs.hir_id))
+                },
+                _ => return,
             }
-        },
-        ExprKind::Call(function, [arg]) => {
-            let kind = cx.typeck_results().node_type(function.hir_id).kind();
-
-            // If we could not resolve the method, don't apply the lint
-            let Ok(Some(resolved_method)) = (match kind {
-                ty::FnDef(_, args) => Instance::try_resolve(cx.tcx, cx.param_env, fn_def_id, args),
-                _ => Ok(None),
-            }) else {
-                return None;
-            };
-            if cx.tcx.is_diagnostic_item(sym::to_owned_method, fn_def_id) {
-                (
-                    TargetTrait::ToOwned,
-                    CallKind::FunctionCall { self_arg: arg },
-                    resolved_method,
-                )
-            } else if let Some(trait_did) = cx.tcx.trait_of_item(fn_def_id)
-                && cx.tcx.is_diagnostic_item(sym::Clone, trait_did)
-            {
-                (
-                    TargetTrait::Clone,
-                    CallKind::FunctionCall { self_arg: arg },
-                    resolved_method,
-                )
-            } else {
-                return None;
+            && let ctxt = e.span.ctxt()
+            // Don't lint in macros.
+            && ctxt.is_root()
+            && let which_trait = match fn_name {
+                sym::clone if is_diag_trait_item(cx, fn_id, sym::Clone) => CloneTrait::Clone,
+                _ if fn_name.as_str() == "to_owned"
+                    && is_diag_trait_item(cx, fn_id, sym::ToOwned)
+                    && self.msrv.meets(msrvs::CLONE_INTO) =>
+                {
+                    CloneTrait::ToOwned
+                },
+                _ => return,
             }
-        },
-        _ => return None,
-    };
-
-    Some(CallCandidate {
-        span: expr.span,
-        target,
-        kind,
-        method_def_id: resolved_method.def_id(),
-    })
-}
-
-// Return true if we find that the called method has a custom implementation and isn't derived or
-// provided by default by the corresponding trait.
-fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallCandidate<'tcx>, msrv: &Msrv) -> bool {
-    // For calls to .to_owned we suggest using .clone_into(), which was only stablilized in 1.63.
-    // If the current MSRV is below that, don't suggest the lint.
-    if !msrv.meets(msrvs::CLONE_INTO) && matches!(call.target, TargetTrait::ToOwned) {
-        return false;
-    }
-
-    // If the left-hand side is a local variable, it might be uninitialized at this point.
-    // In that case we do not want to suggest the lint.
-    if let Some(local) = path_to_local(lhs) {
-        // TODO: This check currently bails if the local variable has no initializer.
-        // That is overly conservative - the lint should fire even if there was no initializer,
-        // but the variable has been initialized before `lhs` was evaluated.
-        if !local_is_initialized(cx, local) {
-            return false;
-        }
-    }
-
-    let Some(impl_block) = cx.tcx.impl_of_method(call.method_def_id) else {
-        return false;
-    };
-
-    // If the method implementation comes from #[derive(Clone)], then don't suggest the lint.
-    // Automatically generated Clone impls do not currently override `clone_from`.
-    // See e.g. https://github.com/rust-lang/rust/pull/98445#issuecomment-1190681305 for more details.
-    if cx.tcx.is_builtin_derived(impl_block) {
-        return false;
-    }
-
-    // If the call expression is inside an impl block that contains the method invoked by the
-    // call expression, we bail out to avoid suggesting something that could result in endless
-    // recursion.
-    if let Some(local_block_id) = impl_block.as_local()
-        && let Some(block) = cx.tcx.hir_node_by_def_id(local_block_id).as_owner()
-    {
-        let impl_block_owner = block.def_id();
-        if cx
-            .tcx
-            .hir()
-            .parent_id_iter(lhs.hir_id)
-            .any(|parent| parent.owner == impl_block_owner)
+            && let Ok(Some(resolved_fn)) = Instance::try_resolve(cx.tcx, cx.param_env, fn_id, fn_gen_args)
+            // TODO: This check currently bails if the local variable has no initializer.
+            // That is overly conservative - the lint should fire even if there was no initializer,
+            // but the variable has been initialized before `lhs` was evaluated.
+            && path_to_local(lhs).map_or(true, |lhs| local_is_initialized(cx, lhs))
+            && let Some(resolved_impl) = cx.tcx.impl_of_method(resolved_fn.def_id())
+            // Derived forms don't implement `clone_from`/`clone_into`.
+            // See https://github.com/rust-lang/rust/pull/98445#issuecomment-1190681305
+            && !cx.tcx.is_builtin_derived(resolved_impl)
+            // Don't suggest calling a function we're implementing.
+            && resolved_impl.as_local().map_or(true, |block_id| {
+                cx.tcx.hir().parent_owner_iter(e.hir_id).all(|(id, _)| id.def_id != block_id)
+            })
+            && let resolved_assoc_items = cx.tcx.associated_items(resolved_impl)
+            // Only suggest if `clone_from`/`clone_into` is explicitly implemented
+            && resolved_assoc_items.in_definition_order().any(|assoc|
+                match which_trait {
+                    CloneTrait::Clone => assoc.name == sym::clone_from,
+                    CloneTrait::ToOwned => assoc.name.as_str() == "clone_into",
+                }
+            )
+            && !clone_source_borrows_from_dest(cx, lhs, rhs.span)
         {
-            return false;
+            span_lint_and_then(
+                cx,
+                ASSIGNING_CLONES,
+                e.span,
+                match which_trait {
+                    CloneTrait::Clone => "assigning the result of `Clone::clone()` may be inefficient",
+                    CloneTrait::ToOwned => "assigning the result of `ToOwned::to_owned()` may be inefficient",
+                },
+                |diag| {
+                    let mut app = Applicability::Unspecified;
+                    diag.span_suggestion(
+                        e.span,
+                        match which_trait {
+                            CloneTrait::Clone => "use `clone_from()`",
+                            CloneTrait::ToOwned => "use `clone_into()`",
+                        },
+                        build_sugg(cx, ctxt, lhs, fn_arg, which_trait, call_kind, &mut app),
+                        app,
+                    );
+                },
+            );
         }
     }
 
-    // Find the function for which we want to check that it is implemented.
-    let provided_fn = match call.target {
-        TargetTrait::Clone => cx.tcx.get_diagnostic_item(sym::Clone).and_then(|clone| {
-            cx.tcx
-                .provided_trait_methods(clone)
-                .find(|item| item.name == sym::clone_from)
-        }),
-        TargetTrait::ToOwned => cx.tcx.get_diagnostic_item(sym::ToOwned).and_then(|to_owned| {
-            cx.tcx
-                .provided_trait_methods(to_owned)
-                .find(|item| item.name.as_str() == "clone_into")
-        }),
-    };
-    let Some(provided_fn) = provided_fn else {
-        return false;
-    };
-
-    if clone_source_borrows_from_dest(cx, lhs, call.span) {
-        return false;
-    }
-
-    // Now take a look if the impl block defines an implementation for the method that we're interested
-    // in. If not, then we're using a default implementation, which is not interesting, so we will
-    // not suggest the lint.
-    let implemented_fns = cx.tcx.impl_item_implementor_ids(impl_block);
-    implemented_fns.contains_key(&provided_fn.def_id)
+    extract_msrv_attr!(LateContext);
 }
 
 /// Checks if the data being cloned borrows from the place that is being assigned to:
@@ -239,16 +154,6 @@ fn is_ok_to_suggest<'tcx>(cx: &LateContext<'tcx>, lhs: &Expr<'tcx>, call: &CallC
 ///
 /// This cannot be written `s2.clone_into(&mut s)` because it has conflicting borrows.
 fn clone_source_borrows_from_dest(cx: &LateContext<'_>, lhs: &Expr<'_>, call_span: Span) -> bool {
-    /// If this basic block only exists to drop a local as part of an assignment, returns its
-    /// successor. Otherwise returns the basic block that was passed in.
-    fn skip_drop_block(mir: &mir::Body<'_>, bb: mir::BasicBlock) -> mir::BasicBlock {
-        if let mir::TerminatorKind::Drop { target, .. } = mir.basic_blocks[bb].terminator().kind {
-            target
-        } else {
-            bb
-        }
-    }
-
     let Some(mir) = enclosing_mir(cx.tcx, lhs.hir_id) else {
         return false;
     };
@@ -267,172 +172,123 @@ fn clone_source_borrows_from_dest(cx: &LateContext<'_>, lhs: &Expr<'_>, call_spa
     //
     // bb2:
     //  s = s_temp
-    for bb in mir.basic_blocks.iter() {
-        let terminator = bb.terminator();
-
-        // Look for the to_owned/clone call.
-        if terminator.source_info.span != call_span {
-            continue;
+    if let Some(terminator) = mir.basic_blocks.iter()
+            .map(mir::BasicBlockData::terminator)
+            .find(|term| term.source_info.span == call_span)
+        && let mir::TerminatorKind::Call { ref args, target: Some(assign_bb), .. } = terminator.kind
+        && let [source] = &**args
+        && let mir::Operand::Move(source) = &source.node
+        && let assign_bb = &mir.basic_blocks[assign_bb]
+        && let assign_bb = match assign_bb.terminator().kind {
+            // Skip the drop of the assignment's destination.
+            mir::TerminatorKind::Drop { target, .. } => &mir.basic_blocks[target],
+            _ => assign_bb,
         }
-
-        if let mir::TerminatorKind::Call { ref args, target: Some(assign_bb), .. } = terminator.kind
-            && let [source] = &**args
-            && let mir::Operand::Move(source) = &source.node
-            && let assign_bb = skip_drop_block(mir, assign_bb)
-            // Skip any storage statements as they are just noise
-            && let Some(assignment) = mir.basic_blocks[assign_bb].statements
-                .iter()
-                .find(|stmt| {
-                    !matches!(stmt.kind, mir::StatementKind::StorageDead(_) | mir::StatementKind::StorageLive(_))
-                })
-            && let mir::StatementKind::Assign(box (borrowed, _)) = &assignment.kind
-            && let Some(borrowers) = borrow_map.get(&borrowed.local)
-            && borrowers.contains(source.local)
-        {
-            return true;
-        }
-
-        return false;
+        // Skip any storage statements as they are just noise
+        && let Some(assignment) = assign_bb.statements
+            .iter()
+            .find(|stmt| {
+                !matches!(stmt.kind, mir::StatementKind::StorageDead(_) | mir::StatementKind::StorageLive(_))
+            })
+        && let mir::StatementKind::Assign(box (borrowed, _)) = &assignment.kind
+        && let Some(borrowers) = borrow_map.get(&borrowed.local)
+    {
+        borrowers.contains(source.local)
+    } else {
+        false
     }
-    false
-}
-
-fn suggest<'tcx>(
-    cx: &LateContext<'tcx>,
-    ctxt: SyntaxContext,
-    assign_expr: &Expr<'tcx>,
-    lhs: &Expr<'tcx>,
-    call: &CallCandidate<'tcx>,
-) {
-    span_lint_and_then(cx, ASSIGNING_CLONES, assign_expr.span, call.message(), |diag| {
-        let mut applicability = Applicability::Unspecified;
-
-        diag.span_suggestion(
-            assign_expr.span,
-            call.suggestion_msg(),
-            call.suggested_replacement(cx, ctxt, lhs, &mut applicability),
-            applicability,
-        );
-    });
 }
 
-#[derive(Copy, Clone, Debug)]
-enum CallKind<'tcx> {
-    MethodCall { receiver: &'tcx Expr<'tcx> },
-    FunctionCall { self_arg: &'tcx Expr<'tcx> },
-}
-
-#[derive(Copy, Clone, Debug)]
-enum TargetTrait {
+#[derive(Clone, Copy)]
+enum CloneTrait {
     Clone,
     ToOwned,
 }
 
-#[derive(Debug)]
-struct CallCandidate<'tcx> {
-    span: Span,
-    target: TargetTrait,
-    kind: CallKind<'tcx>,
-    // DefId of the called method from an impl block that implements the target trait
-    method_def_id: DefId,
+#[derive(Copy, Clone)]
+enum CallKind {
+    Ufcs,
+    Method,
 }
 
-impl<'tcx> CallCandidate<'tcx> {
-    #[inline]
-    fn message(&self) -> &'static str {
-        match self.target {
-            TargetTrait::Clone => "assigning the result of `Clone::clone()` may be inefficient",
-            TargetTrait::ToOwned => "assigning the result of `ToOwned::to_owned()` may be inefficient",
-        }
-    }
-
-    #[inline]
-    fn suggestion_msg(&self) -> &'static str {
-        match self.target {
-            TargetTrait::Clone => "use `clone_from()`",
-            TargetTrait::ToOwned => "use `clone_into()`",
-        }
-    }
-
-    fn suggested_replacement(
-        &self,
-        cx: &LateContext<'tcx>,
-        ctxt: SyntaxContext,
-        lhs: &Expr<'tcx>,
-        applicability: &mut Applicability,
-    ) -> String {
-        match self.target {
-            TargetTrait::Clone => {
-                match self.kind {
-                    CallKind::MethodCall { receiver } => {
-                        let receiver_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind {
-                            // `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)`
-                            Sugg::hir_with_applicability(cx, ref_expr, "_", applicability)
-                        } else {
-                            // `lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)`
-                            Sugg::hir_with_applicability(cx, lhs, "_", applicability)
-                        }
-                        .maybe_par();
-
-                        // Determine whether we need to reference the argument to clone_from().
-                        let clone_receiver_type = cx.typeck_results().expr_ty(receiver);
-                        let clone_receiver_adj_type = cx.typeck_results().expr_ty_adjusted(receiver);
-                        let mut arg_sugg = Sugg::hir_with_context(cx, receiver, ctxt, "_", applicability);
-                        if clone_receiver_type != clone_receiver_adj_type {
-                            // The receiver may have been a value type, so we need to add an `&` to
-                            // be sure the argument to clone_from will be a reference.
-                            arg_sugg = arg_sugg.addr();
-                        };
-
-                        format!("{receiver_sugg}.clone_from({arg_sugg})")
-                    },
-                    CallKind::FunctionCall { self_arg, .. } => {
-                        let self_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind {
-                            // `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)`
-                            Sugg::hir_with_applicability(cx, ref_expr, "_", applicability)
-                        } else {
-                            // `lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut lhs, self_expr)`
-                            Sugg::hir_with_applicability(cx, lhs, "_", applicability).mut_addr()
-                        };
-                        // The RHS had to be exactly correct before the call, there is no auto-deref for function calls.
-                        let rhs_sugg = Sugg::hir_with_context(cx, self_arg, ctxt, "_", applicability);
-
-                        format!("Clone::clone_from({self_sugg}, {rhs_sugg})")
-                    },
-                }
-            },
-            TargetTrait::ToOwned => {
-                let rhs_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind {
-                    // `*lhs = rhs.to_owned()` -> `rhs.clone_into(lhs)`
-                    // `*lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, lhs)`
-                    let sugg = Sugg::hir_with_applicability(cx, ref_expr, "_", applicability).maybe_par();
-                    let inner_type = cx.typeck_results().expr_ty(ref_expr);
-                    // If after unwrapping the dereference, the type is not a mutable reference, we add &mut to make it
-                    // deref to a mutable reference.
-                    if matches!(inner_type.kind(), ty::Ref(_, _, Mutability::Mut)) {
-                        sugg
+fn build_sugg<'tcx>(
+    cx: &LateContext<'tcx>,
+    ctxt: SyntaxContext,
+    lhs: &'tcx Expr<'_>,
+    fn_arg: &'tcx Expr<'_>,
+    which_trait: CloneTrait,
+    call_kind: CallKind,
+    app: &mut Applicability,
+) -> String {
+    match which_trait {
+        CloneTrait::Clone => {
+            match call_kind {
+                CallKind::Method => {
+                    let receiver_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind {
+                        // `*lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)`
+                        Sugg::hir_with_applicability(cx, ref_expr, "_", app)
                     } else {
-                        sugg.mut_addr()
+                        // `lhs = self_expr.clone();` -> `lhs.clone_from(self_expr)`
+                        Sugg::hir_with_applicability(cx, lhs, "_", app)
                     }
+                    .maybe_par();
+
+                    // Determine whether we need to reference the argument to clone_from().
+                    let clone_receiver_type = cx.typeck_results().expr_ty(fn_arg);
+                    let clone_receiver_adj_type = cx.typeck_results().expr_ty_adjusted(fn_arg);
+                    let mut arg_sugg = Sugg::hir_with_context(cx, fn_arg, ctxt, "_", app);
+                    if clone_receiver_type != clone_receiver_adj_type {
+                        // The receiver may have been a value type, so we need to add an `&` to
+                        // be sure the argument to clone_from will be a reference.
+                        arg_sugg = arg_sugg.addr();
+                    };
+
+                    format!("{receiver_sugg}.clone_from({arg_sugg})")
+                },
+                CallKind::Ufcs => {
+                    let self_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind {
+                        // `*lhs = Clone::clone(self_expr);` -> `Clone::clone_from(lhs, self_expr)`
+                        Sugg::hir_with_applicability(cx, ref_expr, "_", app)
+                    } else {
+                        // `lhs = Clone::clone(self_expr);` -> `Clone::clone_from(&mut lhs, self_expr)`
+                        Sugg::hir_with_applicability(cx, lhs, "_", app).mut_addr()
+                    };
+                    // The RHS had to be exactly correct before the call, there is no auto-deref for function calls.
+                    let rhs_sugg = Sugg::hir_with_context(cx, fn_arg, ctxt, "_", app);
+
+                    format!("Clone::clone_from({self_sugg}, {rhs_sugg})")
+                },
+            }
+        },
+        CloneTrait::ToOwned => {
+            let rhs_sugg = if let ExprKind::Unary(hir::UnOp::Deref, ref_expr) = lhs.kind {
+                // `*lhs = rhs.to_owned()` -> `rhs.clone_into(lhs)`
+                // `*lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, lhs)`
+                let sugg = Sugg::hir_with_applicability(cx, ref_expr, "_", app).maybe_par();
+                let inner_type = cx.typeck_results().expr_ty(ref_expr);
+                // If after unwrapping the dereference, the type is not a mutable reference, we add &mut to make it
+                // deref to a mutable reference.
+                if matches!(inner_type.kind(), ty::Ref(_, _, Mutability::Mut)) {
+                    sugg
                 } else {
-                    // `lhs = rhs.to_owned()` -> `rhs.clone_into(&mut lhs)`
-                    // `lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, &mut lhs)`
-                    Sugg::hir_with_applicability(cx, lhs, "_", applicability)
-                        .maybe_par()
-                        .mut_addr()
-                };
-
-                match self.kind {
-                    CallKind::MethodCall { receiver } => {
-                        let receiver_sugg = Sugg::hir_with_context(cx, receiver, ctxt, "_", applicability);
-                        format!("{receiver_sugg}.clone_into({rhs_sugg})")
-                    },
-                    CallKind::FunctionCall { self_arg, .. } => {
-                        let self_sugg = Sugg::hir_with_context(cx, self_arg, ctxt, "_", applicability);
-                        format!("ToOwned::clone_into({self_sugg}, {rhs_sugg})")
-                    },
+                    sugg.mut_addr()
                 }
-            },
-        }
+            } else {
+                // `lhs = rhs.to_owned()` -> `rhs.clone_into(&mut lhs)`
+                // `lhs = ToOwned::to_owned(rhs)` -> `ToOwned::clone_into(rhs, &mut lhs)`
+                Sugg::hir_with_applicability(cx, lhs, "_", app).maybe_par().mut_addr()
+            };
+
+            match call_kind {
+                CallKind::Method => {
+                    let receiver_sugg = Sugg::hir_with_context(cx, fn_arg, ctxt, "_", app);
+                    format!("{receiver_sugg}.clone_into({rhs_sugg})")
+                },
+                CallKind::Ufcs => {
+                    let self_sugg = Sugg::hir_with_context(cx, fn_arg, ctxt, "_", app);
+                    format!("ToOwned::clone_into({self_sugg}, {rhs_sugg})")
+                },
+            }
+        },
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
index f25a474d9bb..d4a1e2780d0 100644
--- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
+++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
@@ -11,21 +11,25 @@ use rustc_span::{sym, Span};
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for calls to await while holding a non-async-aware MutexGuard.
+    /// Checks for calls to `await` while holding a non-async-aware
+    /// `MutexGuard`.
     ///
     /// ### Why is this bad?
-    /// The Mutex types found in std::sync and parking_lot
-    /// are not designed to operate in an async context across await points.
+    /// The Mutex types found in [`std::sync`][https://doc.rust-lang.org/stable/std/sync/] and
+    /// [`parking_lot`](https://docs.rs/parking_lot/latest/parking_lot/) are
+    /// not designed to operate in an async context across await points.
     ///
-    /// There are two potential solutions. One is to use an async-aware Mutex
-    /// type. Many asynchronous foundation crates provide such a Mutex type. The
-    /// other solution is to ensure the mutex is unlocked before calling await,
-    /// either by introducing a scope or an explicit call to Drop::drop.
+    /// There are two potential solutions. One is to use an async-aware `Mutex`
+    /// type. Many asynchronous foundation crates provide such a `Mutex` type.
+    /// The other solution is to ensure the mutex is unlocked before calling
+    /// `await`, either by introducing a scope or an explicit call to
+    /// [`Drop::drop`](https://doc.rust-lang.org/std/ops/trait.Drop.html).
     ///
     /// ### Known problems
     /// Will report false positive for explicitly dropped guards
-    /// ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)). A workaround for this is
-    /// to wrap the `.lock()` call in a block instead of explicitly dropping the guard.
+    /// ([#6446](https://github.com/rust-lang/rust-clippy/issues/6446)). A
+    /// workaround for this is to wrap the `.lock()` call in a block instead of
+    /// explicitly dropping the guard.
     ///
     /// ### Example
     /// ```no_run
@@ -73,11 +77,11 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for calls to await while holding a `RefCell` `Ref` or `RefMut`.
+    /// Checks for calls to `await` while holding a `RefCell`, `Ref`, or `RefMut`.
     ///
     /// ### Why is this bad?
     /// `RefCell` refs only check for exclusive mutable access
-    /// at runtime. Holding onto a `RefCell` ref across an `await` suspension point
+    /// at runtime. Holding a `RefCell` ref across an await suspension point
     /// risks panics from a mutable ref shared while other refs are outstanding.
     ///
     /// ### Known problems
@@ -131,13 +135,13 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Allows users to configure types which should not be held across `await`
+    /// Allows users to configure types which should not be held across await
     /// suspension points.
     ///
     /// ### Why is this bad?
-    /// There are some types which are perfectly "safe" to be used concurrently
-    /// from a memory access perspective but will cause bugs at runtime if they
-    /// are held in such a way.
+    /// There are some types which are perfectly safe to use concurrently from
+    /// a memory access perspective, but that will cause bugs at runtime if
+    /// they are held in such a way.
     ///
     /// ### Example
     ///
@@ -228,15 +232,15 @@ impl AwaitHolding {
                         cx,
                         AWAIT_HOLDING_LOCK,
                         ty_cause.source_info.span,
-                        "this `MutexGuard` is held across an `await` point",
+                        "this `MutexGuard` is held across an await point",
                         |diag| {
                             diag.help(
                                 "consider using an async-aware `Mutex` type or ensuring the \
-                                `MutexGuard` is dropped before calling await",
+                                `MutexGuard` is dropped before calling `await`",
                             );
                             diag.span_note(
                                 await_points(),
-                                "these are all the `await` points this lock is held through",
+                                "these are all the await points this lock is held through",
                             );
                         },
                     );
@@ -245,12 +249,12 @@ impl AwaitHolding {
                         cx,
                         AWAIT_HOLDING_REFCELL_REF,
                         ty_cause.source_info.span,
-                        "this `RefCell` reference is held across an `await` point",
+                        "this `RefCell` reference is held across an await point",
                         |diag| {
                             diag.help("ensure the reference is dropped before calling `await`");
                             diag.span_note(
                                 await_points(),
-                                "these are all the `await` points this reference is held through",
+                                "these are all the await points this reference is held through",
                             );
                         },
                     );
@@ -268,7 +272,7 @@ fn emit_invalid_type(cx: &LateContext<'_>, span: Span, disallowed: &DisallowedPa
         AWAIT_HOLDING_INVALID_TYPE,
         span,
         format!(
-            "`{}` may not be held across an `await` point per `clippy.toml`",
+            "`{}` may not be held across an await point per `clippy.toml`",
             disallowed.path()
         ),
         |diag| {
diff --git a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs
index cfb76cab6dc..561ca9bd986 100644
--- a/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs
+++ b/src/tools/clippy/clippy_lints/src/bool_to_int_with_if.rs
@@ -1,13 +1,11 @@
-use clippy_utils::higher::If;
-use rustc_ast::LitKind;
-use rustc_hir::{Block, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::declare_lint_pass;
-
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::sugg::Sugg;
-use clippy_utils::{in_constant, is_else_clause, is_integer_literal};
+use clippy_utils::{in_constant, is_else_clause};
+use rustc_ast::LitKind;
 use rustc_errors::Applicability;
+use rustc_hir::{Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::declare_lint_pass;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -47,80 +45,64 @@ declare_clippy_lint! {
 declare_lint_pass!(BoolToIntWithIf => [BOOL_TO_INT_WITH_IF]);
 
 impl<'tcx> LateLintPass<'tcx> for BoolToIntWithIf {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
-        if !expr.span.from_expansion() && !in_constant(cx, expr.hir_id) {
-            check_if_else(cx, expr);
-        }
-    }
-}
-
-fn check_if_else<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
-    if let Some(If {
-        cond,
-        then,
-        r#else: Some(r#else),
-    }) = If::hir(expr)
-        && let Some(then_lit) = int_literal(then)
-        && let Some(else_lit) = int_literal(r#else)
-    {
-        let inverted = if is_integer_literal(then_lit, 1) && is_integer_literal(else_lit, 0) {
-            false
-        } else if is_integer_literal(then_lit, 0) && is_integer_literal(else_lit, 1) {
-            true
-        } else {
-            // Expression isn't boolean, exit
-            return;
-        };
-        let mut applicability = Applicability::MachineApplicable;
-        let snippet = {
-            let mut sugg = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability);
-            if inverted {
-                sugg = !sugg;
-            }
-            sugg
-        };
-
-        let ty = cx.typeck_results().expr_ty(then_lit); // then and else must be of same type
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+        if let ExprKind::If(cond, then, Some(else_)) = expr.kind
+            && matches!(cond.kind, ExprKind::DropTemps(_))
+            && let Some(then_lit) = as_int_bool_lit(then)
+            && let Some(else_lit) = as_int_bool_lit(else_)
+            && then_lit != else_lit
+            && !expr.span.from_expansion()
+            && !in_constant(cx, expr.hir_id)
+        {
+            let ty = cx.typeck_results().expr_ty(then);
+            let mut applicability = Applicability::MachineApplicable;
+            let snippet = {
+                let mut sugg = Sugg::hir_with_applicability(cx, cond, "..", &mut applicability);
+                if !then_lit {
+                    sugg = !sugg;
+                }
+                sugg
+            };
+            let suggestion = {
+                let mut s = Sugg::NonParen(format!("{ty}::from({snippet})").into());
+                // when used in else clause if statement should be wrapped in curly braces
+                if is_else_clause(cx.tcx, expr) {
+                    s = s.blockify();
+                }
+                s
+            };
 
-        let suggestion = {
-            let wrap_in_curly = is_else_clause(cx.tcx, expr);
-            let mut s = Sugg::NonParen(format!("{ty}::from({snippet})").into());
-            if wrap_in_curly {
-                s = s.blockify();
-            }
-            s
-        }; // when used in else clause if statement should be wrapped in curly braces
+            let into_snippet = snippet.clone().maybe_par();
+            let as_snippet = snippet.as_ty(ty);
 
-        let into_snippet = snippet.clone().maybe_par();
-        let as_snippet = snippet.as_ty(ty);
-
-        span_lint_and_then(
-            cx,
-            BOOL_TO_INT_WITH_IF,
-            expr.span,
-            "boolean to int conversion using if",
-            |diag| {
-                diag.span_suggestion(expr.span, "replace with from", suggestion, applicability);
-                diag.note(format!(
-                    "`{as_snippet}` or `{into_snippet}.into()` can also be valid options"
-                ));
-            },
-        );
-    };
+            span_lint_and_then(
+                cx,
+                BOOL_TO_INT_WITH_IF,
+                expr.span,
+                "boolean to int conversion using if",
+                |diag| {
+                    diag.span_suggestion(expr.span, "replace with from", suggestion, applicability);
+                    diag.note(format!(
+                        "`{as_snippet}` or `{into_snippet}.into()` can also be valid options"
+                    ));
+                },
+            );
+        }
+    }
 }
 
-// If block contains only a int literal expression, return literal expression
-fn int_literal<'tcx>(expr: &'tcx rustc_hir::Expr<'tcx>) -> Option<&'tcx rustc_hir::Expr<'tcx>> {
-    if let ExprKind::Block(block, _) = expr.kind
-        && let Block {
-            stmts: [], // Shouldn't lint if statements with side effects
-            expr: Some(expr),
-            ..
-        } = block
-        && let ExprKind::Lit(lit) = &expr.kind
-        && let LitKind::Int(_, _) = lit.node
+fn as_int_bool_lit(e: &Expr<'_>) -> Option<bool> {
+    if let ExprKind::Block(b, _) = e.kind
+        && b.stmts.is_empty()
+        && let Some(e) = b.expr
+        && let ExprKind::Lit(lit) = e.kind
+        && let LitKind::Int(x, _) = lit.node
     {
-        Some(expr)
+        match x.get() {
+            0 => Some(false),
+            1 => Some(true),
+            _ => None,
+        }
     } else {
         None
     }
diff --git a/src/tools/clippy/clippy_lints/src/byte_char_slices.rs b/src/tools/clippy/clippy_lints/src/byte_char_slices.rs
new file mode 100644
index 00000000000..a9fe190f177
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/byte_char_slices.rs
@@ -0,0 +1,80 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use rustc_ast::ast::{BorrowKind, Expr, ExprKind, Mutability};
+use rustc_ast::token::{Lit, LitKind};
+use rustc_errors::Applicability;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::declare_lint_pass;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for hard to read slices of byte characters, that could be more easily expressed as a
+    /// byte string.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// Potentially makes the string harder to read.
+    ///
+    /// ### Example
+    /// ```ignore
+    /// &[b'H', b'e', b'l', b'l', b'o'];
+    /// ```
+    /// Use instead:
+    /// ```ignore
+    /// b"Hello"
+    /// ```
+    #[clippy::version = "1.68.0"]
+    pub BYTE_CHAR_SLICES,
+    style,
+    "hard to read byte char slice"
+}
+declare_lint_pass!(ByteCharSlice => [BYTE_CHAR_SLICES]);
+
+impl EarlyLintPass for ByteCharSlice {
+    fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
+        if let Some(slice) = is_byte_char_slices(expr)
+            && !expr.span.from_expansion()
+        {
+            span_lint_and_sugg(
+                cx,
+                BYTE_CHAR_SLICES,
+                expr.span,
+                "can be more succinctly written as a byte str",
+                "try",
+                format!("b\"{slice}\""),
+                Applicability::MaybeIncorrect,
+            );
+        }
+    }
+}
+
+fn is_byte_char_slices(expr: &Expr) -> Option<String> {
+    if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, expr) = &expr.kind {
+        match &expr.kind {
+            ExprKind::Array(members) => {
+                if members.is_empty() {
+                    return None;
+                }
+
+                members
+                    .iter()
+                    .map(|member| match &member.kind {
+                        ExprKind::Lit(Lit {
+                            kind: LitKind::Byte,
+                            symbol,
+                            ..
+                        }) => Some(symbol.as_str()),
+                        _ => None,
+                    })
+                    .map(|maybe_quote| match maybe_quote {
+                        Some("\"") => Some("\\\""),
+                        Some("\\'") => Some("'"),
+                        other => other,
+                    })
+                    .collect::<Option<String>>()
+            },
+            _ => None,
+        }
+    } else {
+        None
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/casts/mod.rs b/src/tools/clippy/clippy_lints/src/casts/mod.rs
index e60c36ced75..54f0c7c4687 100644
--- a/src/tools/clippy/clippy_lints/src/casts/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/mod.rs
@@ -32,7 +32,7 @@ use rustc_session::impl_lint_pass;
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for casts from any numerical to a float type where
+    /// Checks for casts from any numeric type to a float type where
     /// the receiving type cannot store all values from the original type without
     /// rounding errors. This possible rounding is to be expected, so this lint is
     /// `Allow` by default.
@@ -58,14 +58,14 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for casts from a signed to an unsigned numerical
+    /// Checks for casts from a signed to an unsigned numeric
     /// type. In this case, negative values wrap around to large positive values,
-    /// which can be quite surprising in practice. However, as the cast works as
+    /// which can be quite surprising in practice. However, since the cast works as
     /// defined, this lint is `Allow` by default.
     ///
     /// ### Why is this bad?
     /// Possibly surprising results. You can activate this lint
-    /// as a one-time check to see where numerical wrapping can arise.
+    /// as a one-time check to see where numeric wrapping can arise.
     ///
     /// ### Example
     /// ```no_run
@@ -80,7 +80,7 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for casts between numerical types that may
+    /// Checks for casts between numeric types that may
     /// truncate large values. This is expected behavior, so the cast is `Allow` by
     /// default. It suggests user either explicitly ignore the lint,
     /// or use `try_from()` and handle the truncation, default, or panic explicitly.
@@ -120,17 +120,16 @@ declare_clippy_lint! {
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for casts from an unsigned type to a signed type of
-    /// the same size, or possibly smaller due to target dependent integers.
-    /// Performing such a cast is a 'no-op' for the compiler, i.e., nothing is
-    /// changed at the bit level, and the binary representation of the value is
+    /// the same size, or possibly smaller due to target-dependent integers.
+    /// Performing such a cast is a no-op for the compiler (that is, nothing is
+    /// changed at the bit level), and the binary representation of the value is
     /// reinterpreted. This can cause wrapping if the value is too big
     /// for the target signed type. However, the cast works as defined, so this lint
     /// is `Allow` by default.
     ///
     /// ### Why is this bad?
     /// While such a cast is not bad in itself, the results can
-    /// be surprising when this is not the intended behavior, as demonstrated by the
-    /// example below.
+    /// be surprising when this is not the intended behavior:
     ///
     /// ### Example
     /// ```no_run
@@ -144,16 +143,16 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for casts between numerical types that may
-    /// be replaced by safe conversion functions.
+    /// Checks for casts between numeric types that can be replaced by safe
+    /// conversion functions.
     ///
     /// ### Why is this bad?
-    /// Rust's `as` keyword will perform many kinds of
-    /// conversions, including silently lossy conversions. Conversion functions such
-    /// as `i32::from` will only perform lossless conversions. Using the conversion
-    /// functions prevents conversions from turning into silent lossy conversions if
-    /// the types of the input expressions ever change, and make it easier for
-    /// people reading the code to know that the conversion is lossless.
+    /// Rust's `as` keyword will perform many kinds of conversions, including
+    /// silently lossy conversions. Conversion functions such as `i32::from`
+    /// will only perform lossless conversions. Using the conversion functions
+    /// prevents conversions from becoming silently lossy if the input types
+    /// ever change, and makes it clear for people reading the code that the
+    /// conversion is lossless.
     ///
     /// ### Example
     /// ```no_run
@@ -177,19 +176,21 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for casts to the same type, casts of int literals to integer types, casts of float
-    /// literals to float types and casts between raw pointers without changing type or constness.
+    /// Checks for casts to the same type, casts of int literals to integer
+    /// types, casts of float literals to float types, and casts between raw
+    /// pointers that don't change type or constness.
     ///
     /// ### Why is this bad?
     /// It's just unnecessary.
     ///
     /// ### Known problems
-    /// When the expression on the left is a function call, the lint considers the return type to be
-    /// a type alias if it's aliased through a `use` statement
-    /// (like `use std::io::Result as IoResult`). It will not lint such cases.
+    /// When the expression on the left is a function call, the lint considers
+    /// the return type to be a type alias if it's aliased through a `use`
+    /// statement (like `use std::io::Result as IoResult`). It will not lint
+    /// such cases.
     ///
-    /// This check is also rather primitive. It will only work on primitive types without any
-    /// intermediate references, raw pointers and trait objects may or may not work.
+    /// This check will only work on primitive types without any intermediate
+    /// references: raw pointers and trait objects may or may not work.
     ///
     /// ### Example
     /// ```no_run
@@ -211,17 +212,17 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for casts, using `as` or `pointer::cast`,
-    /// from a less-strictly-aligned pointer to a more-strictly-aligned pointer
+    /// Checks for casts, using `as` or `pointer::cast`, from a
+    /// less strictly aligned pointer to a more strictly aligned pointer.
     ///
     /// ### Why is this bad?
-    /// Dereferencing the resulting pointer may be undefined
-    /// behavior.
+    /// Dereferencing the resulting pointer may be undefined behavior.
     ///
     /// ### Known problems
-    /// Using `std::ptr::read_unaligned` and `std::ptr::write_unaligned` or similar
-    /// on the resulting pointer is fine. Is over-zealous: Casts with manual alignment checks or casts like
-    /// u64-> u8 -> u16 can be fine. Miri is able to do a more in-depth analysis.
+    /// Using [`std::ptr::read_unaligned`](https://doc.rust-lang.org/std/ptr/fn.read_unaligned.html) and [`std::ptr::write_unaligned`](https://doc.rust-lang.org/std/ptr/fn.write_unaligned.html) or
+    /// similar on the resulting pointer is fine. Is over-zealous: casts with
+    /// manual alignment checks or casts like `u64` -> `u8` -> `u16` can be
+    /// fine. Miri is able to do a more in-depth analysis.
     ///
     /// ### Example
     /// ```no_run
@@ -234,20 +235,21 @@ declare_clippy_lint! {
     #[clippy::version = "pre 1.29.0"]
     pub CAST_PTR_ALIGNMENT,
     pedantic,
-    "cast from a pointer to a more-strictly-aligned pointer"
+    "cast from a pointer to a more strictly aligned pointer"
 }
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for casts of function pointers to something other than usize
+    /// Checks for casts of function pointers to something other than `usize`.
     ///
     /// ### Why is this bad?
-    /// Casting a function pointer to anything other than usize/isize is not portable across
-    /// architectures, because you end up losing bits if the target type is too small or end up with a
-    /// bunch of extra bits that waste space and add more instructions to the final binary than
-    /// strictly necessary for the problem
+    /// Casting a function pointer to anything other than `usize`/`isize` is
+    /// not portable across architectures. If the target type is too small the
+    /// address would be truncated, and target types larger than `usize` are
+    /// unnecessary.
     ///
-    /// Casting to isize also doesn't make sense since there are no signed addresses.
+    /// Casting to `isize` also doesn't make sense, since addresses are never
+    /// signed.
     ///
     /// ### Example
     /// ```no_run
@@ -263,17 +265,17 @@ declare_clippy_lint! {
     #[clippy::version = "pre 1.29.0"]
     pub FN_TO_NUMERIC_CAST,
     style,
-    "casting a function pointer to a numeric type other than usize"
+    "casting a function pointer to a numeric type other than `usize`"
 }
 
 declare_clippy_lint! {
     /// ### What it does
     /// Checks for casts of a function pointer to a numeric type not wide enough to
-    /// store address.
+    /// store an address.
     ///
     /// ### Why is this bad?
     /// Such a cast discards some bits of the function's address. If this is intended, it would be more
-    /// clearly expressed by casting to usize first, then casting the usize to the intended type (with
+    /// clearly expressed by casting to `usize` first, then casting the `usize` to the intended type (with
     /// a comment) to perform the truncation.
     ///
     /// ### Example
@@ -306,7 +308,7 @@ declare_clippy_lint! {
     /// ### Why restrict this?
     /// Casting a function pointer to an integer can have surprising results and can occur
     /// accidentally if parentheses are omitted from a function call. If you aren't doing anything
-    /// low-level with function pointers then you can opt-out of casting functions to integers in
+    /// low-level with function pointers then you can opt out of casting functions to integers in
     /// order to avoid mistakes. Alternatively, you can use this lint to audit all uses of function
     /// pointer casts in your code.
     ///
@@ -349,8 +351,8 @@ declare_clippy_lint! {
     /// ### Why is this bad?
     /// In general, casting values to smaller types is
     /// error-prone and should be avoided where possible. In the particular case of
-    /// converting a character literal to u8, it is easy to avoid by just using a
-    /// byte literal instead. As an added bonus, `b'a'` is even slightly shorter
+    /// converting a character literal to `u8`, it is easy to avoid by just using a
+    /// byte literal instead. As an added bonus, `b'a'` is also slightly shorter
     /// than `'a' as u8`.
     ///
     /// ### Example
@@ -371,12 +373,13 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for `as` casts between raw pointers without changing its mutability,
-    /// namely `*const T` to `*const U` and `*mut T` to `*mut U`.
+    /// Checks for `as` casts between raw pointers that don't change their
+    /// constness, namely `*const T` to `*const U` and `*mut T` to `*mut U`.
     ///
     /// ### Why is this bad?
-    /// Though `as` casts between raw pointers are not terrible, `pointer::cast` is safer because
-    /// it cannot accidentally change the pointer's mutability nor cast the pointer to other types like `usize`.
+    /// Though `as` casts between raw pointers are not terrible,
+    /// `pointer::cast` is safer because it cannot accidentally change the
+    /// pointer's mutability, nor cast the pointer to other types like `usize`.
     ///
     /// ### Example
     /// ```no_run
@@ -395,12 +398,12 @@ declare_clippy_lint! {
     #[clippy::version = "1.51.0"]
     pub PTR_AS_PTR,
     pedantic,
-    "casting using `as` from and to raw pointers that doesn't change its mutability, where `pointer::cast` could take the place of `as`"
+    "casting using `as` between raw pointers that doesn't change their constness, where `pointer::cast` could take the place of `as`"
 }
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for `as` casts between raw pointers which change its constness, namely `*const T` to
+    /// Checks for `as` casts between raw pointers that change their constness, namely `*const T` to
     /// `*mut T` and `*mut T` to `*const T`.
     ///
     /// ### Why is this bad?
@@ -423,12 +426,12 @@ declare_clippy_lint! {
     #[clippy::version = "1.72.0"]
     pub PTR_CAST_CONSTNESS,
     pedantic,
-    "casting using `as` from and to raw pointers to change constness when specialized methods apply"
+    "casting using `as` on raw pointers to change constness when specialized methods apply"
 }
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for casts from an enum type to an integral type which will definitely truncate the
+    /// Checks for casts from an enum type to an integral type that will definitely truncate the
     /// value.
     ///
     /// ### Why is this bad?
@@ -442,7 +445,7 @@ declare_clippy_lint! {
     #[clippy::version = "1.61.0"]
     pub CAST_ENUM_TRUNCATION,
     suspicious,
-    "casts from an enum type to an integral type which will truncate the value"
+    "casts from an enum type to an integral type that will truncate the value"
 }
 
 declare_clippy_lint! {
@@ -621,7 +624,7 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer
+    /// Checks for the result of a `&self`-taking `as_ptr` being cast to a mutable pointer.
     ///
     /// ### Why is this bad?
     /// Since `as_ptr` takes a `&self`, the pointer won't have write permissions unless interior
diff --git a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs
index 2c168405ee2..86c5f6b9f0b 100644
--- a/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs
@@ -92,7 +92,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) {
             cx,
             PTR_AS_PTR,
             expr.span,
-            "`as` casting between raw pointers without changing its mutability",
+            "`as` casting between raw pointers without changing their constness",
             help,
             final_suggestion,
             app,
diff --git a/src/tools/clippy/clippy_lints/src/cfg_not_test.rs b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs
new file mode 100644
index 00000000000..b54f392bf2f
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/cfg_not_test.rs
@@ -0,0 +1,60 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use rustc_ast::NestedMetaItem;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::declare_lint_pass;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for usage of `cfg` that excludes code from `test` builds. (i.e., `#{cfg(not(test))]`)
+    ///
+    /// ### Why is this bad?
+    /// This may give the false impression that a codebase has 100% coverage, yet actually has untested code.
+    /// Enabling this also guards against excessive mockery as well, which is an anti-pattern.
+    ///
+    /// ### Example
+    /// ```rust
+    /// # fn important_check() {}
+    /// #[cfg(not(test))]
+    /// important_check(); // I'm not actually tested, but not including me will falsely increase coverage!
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// # fn important_check() {}
+    /// important_check();
+    /// ```
+    #[clippy::version = "1.73.0"]
+    pub CFG_NOT_TEST,
+    restriction,
+    "enforce against excluding code from test builds"
+}
+
+declare_lint_pass!(CfgNotTest => [CFG_NOT_TEST]);
+
+impl EarlyLintPass for CfgNotTest {
+    fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &rustc_ast::Attribute) {
+        if attr.has_name(rustc_span::sym::cfg) && contains_not_test(attr.meta_item_list().as_deref(), false) {
+            span_lint_and_then(
+                cx,
+                CFG_NOT_TEST,
+                attr.span,
+                "code is excluded from test builds",
+                |diag| {
+                    diag.help("consider not excluding any code from test builds");
+                    diag.note_once("this could increase code coverage despite not actually being tested");
+                },
+            );
+        }
+    }
+}
+
+fn contains_not_test(list: Option<&[NestedMetaItem]>, not: bool) -> bool {
+    list.is_some_and(|list| {
+        list.iter().any(|item| {
+            item.ident().is_some_and(|ident| match ident.name {
+                rustc_span::sym::not => contains_not_test(item.meta_item_list(), !not),
+                rustc_span::sym::test => not,
+                _ => contains_not_test(item.meta_item_list(), not),
+            })
+        })
+    })
+}
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index 638de5e818c..eabc67601a2 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -8,8 +8,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     #[cfg(feature = "internal")]
     crate::utils::internal_lints::collapsible_calls::COLLAPSIBLE_SPAN_LINT_CALLS_INFO,
     #[cfg(feature = "internal")]
-    crate::utils::internal_lints::compiler_lint_functions::COMPILER_LINT_FUNCTIONS_INFO,
-    #[cfg(feature = "internal")]
     crate::utils::internal_lints::interning_defined_symbol::INTERNING_DEFINED_SYMBOL_INFO,
     #[cfg(feature = "internal")]
     crate::utils::internal_lints::interning_defined_symbol::UNNECESSARY_SYMBOL_STR_INFO,
@@ -73,6 +71,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::booleans::OVERLY_COMPLEX_BOOL_EXPR_INFO,
     crate::borrow_deref_ref::BORROW_DEREF_REF_INFO,
     crate::box_default::BOX_DEFAULT_INFO,
+    crate::byte_char_slices::BYTE_CHAR_SLICES_INFO,
     crate::cargo::CARGO_COMMON_METADATA_INFO,
     crate::cargo::LINT_GROUPS_PRIORITY_INFO,
     crate::cargo::MULTIPLE_CRATE_VERSIONS_INFO,
@@ -103,6 +102,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::casts::REF_AS_PTR_INFO,
     crate::casts::UNNECESSARY_CAST_INFO,
     crate::casts::ZERO_PTR_INFO,
+    crate::cfg_not_test::CFG_NOT_TEST_INFO,
     crate::checked_conversions::CHECKED_CONVERSIONS_INFO,
     crate::cognitive_complexity::COGNITIVE_COMPLEXITY_INFO,
     crate::collapsible_if::COLLAPSIBLE_ELSE_IF_INFO,
@@ -313,6 +313,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::manual_range_patterns::MANUAL_RANGE_PATTERNS_INFO,
     crate::manual_rem_euclid::MANUAL_REM_EUCLID_INFO,
     crate::manual_retain::MANUAL_RETAIN_INFO,
+    crate::manual_rotate::MANUAL_ROTATE_INFO,
     crate::manual_slice_size_calculation::MANUAL_SLICE_SIZE_CALCULATION_INFO,
     crate::manual_string_new::MANUAL_STRING_NEW_INFO,
     crate::manual_strip::MANUAL_STRIP_INFO,
@@ -503,6 +504,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::missing_assert_message::MISSING_ASSERT_MESSAGE_INFO,
     crate::missing_asserts_for_indexing::MISSING_ASSERTS_FOR_INDEXING_INFO,
     crate::missing_const_for_fn::MISSING_CONST_FOR_FN_INFO,
+    crate::missing_const_for_thread_local::MISSING_CONST_FOR_THREAD_LOCAL_INFO,
     crate::missing_doc::MISSING_DOCS_IN_PRIVATE_ITEMS_INFO,
     crate::missing_enforced_import_rename::MISSING_ENFORCED_IMPORT_RENAMES_INFO,
     crate::missing_fields_in_debug::MISSING_FIELDS_IN_DEBUG_INFO,
@@ -585,12 +587,12 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::operators::VERBOSE_BIT_MASK_INFO,
     crate::option_env_unwrap::OPTION_ENV_UNWRAP_INFO,
     crate::option_if_let_else::OPTION_IF_LET_ELSE_INFO,
-    crate::overflow_check_conditional::OVERFLOW_CHECK_CONDITIONAL_INFO,
     crate::panic_in_result_fn::PANIC_IN_RESULT_FN_INFO,
     crate::panic_unimplemented::PANIC_INFO,
     crate::panic_unimplemented::TODO_INFO,
     crate::panic_unimplemented::UNIMPLEMENTED_INFO,
     crate::panic_unimplemented::UNREACHABLE_INFO,
+    crate::panicking_overflow_checks::PANICKING_OVERFLOW_CHECKS_INFO,
     crate::partial_pub_fields::PARTIAL_PUB_FIELDS_INFO,
     crate::partialeq_ne_impl::PARTIALEQ_NE_IMPL_INFO,
     crate::partialeq_to_none::PARTIALEQ_TO_NONE_INFO,
@@ -644,6 +646,7 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::semicolon_block::SEMICOLON_OUTSIDE_BLOCK_INFO,
     crate::semicolon_if_nothing_returned::SEMICOLON_IF_NOTHING_RETURNED_INFO,
     crate::serde_api::SERDE_API_MISUSE_INFO,
+    crate::set_contains_or_insert::SET_CONTAINS_OR_INSERT_INFO,
     crate::shadow::SHADOW_REUSE_INFO,
     crate::shadow::SHADOW_SAME_INFO,
     crate::shadow::SHADOW_UNRELATED_INFO,
@@ -679,7 +682,6 @@ pub(crate) static LINTS: &[&crate::LintInfo] = &[
     crate::tabs_in_doc_comments::TABS_IN_DOC_COMMENTS_INFO,
     crate::temporary_assignment::TEMPORARY_ASSIGNMENT_INFO,
     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,
diff --git a/src/tools/clippy/clippy_lints/src/default.rs b/src/tools/clippy/clippy_lints/src/default.rs
index 2b3f4854255..72fa05be3cc 100644
--- a/src/tools/clippy/clippy_lints/src/default.rs
+++ b/src/tools/clippy/clippy_lints/src/default.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_sugg};
 use clippy_utils::source::snippet_with_context;
 use clippy_utils::ty::{has_drop, is_copy};
-use clippy_utils::{any_parent_is_automatically_derived, contains_name, get_parent_expr, is_from_proc_macro};
+use clippy_utils::{contains_name, get_parent_expr, in_automatically_derived, is_from_proc_macro};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
@@ -84,7 +84,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
             // Avoid cases already linted by `field_reassign_with_default`
             && !self.reassigned_linted.contains(&expr.span)
             && let ExprKind::Call(path, ..) = expr.kind
-            && !any_parent_is_automatically_derived(cx.tcx, expr.hir_id)
+            && !in_automatically_derived(cx.tcx, expr.hir_id)
             && let ExprKind::Path(ref qpath) = path.kind
             && let Some(def_id) = cx.qpath_res(qpath, path.hir_id).opt_def_id()
             && cx.tcx.is_diagnostic_item(sym::default_fn, def_id)
@@ -113,9 +113,9 @@ impl<'tcx> LateLintPass<'tcx> for Default {
         // start from the `let mut _ = _::default();` and look at all the following
         // statements, see if they re-assign the fields of the binding
         let stmts_head = match block.stmts {
+            [] | [_] => return,
             // Skip the last statement since there cannot possibly be any following statements that re-assign fields.
-            [head @ .., _] if !head.is_empty() => head,
-            _ => return,
+            [head @ .., _] => head,
         };
         for (stmt_idx, stmt) in stmts_head.iter().enumerate() {
             // find all binding statements like `let mut _ = T::default()` where `T::default()` is the
@@ -124,7 +124,7 @@ impl<'tcx> LateLintPass<'tcx> for Default {
             let (local, variant, binding_name, binding_type, span) = if let StmtKind::Let(local) = stmt.kind
                 // only take `let ...` statements
                 && let Some(expr) = local.init
-                && !any_parent_is_automatically_derived(cx.tcx, expr.hir_id)
+                && !in_automatically_derived(cx.tcx, expr.hir_id)
                 && !expr.span.from_expansion()
                 // only take bindings to identifiers
                 && let PatKind::Binding(_, binding_id, ident, _) = local.pat.kind
diff --git a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
index ff631909bcb..9af73db6849 100644
--- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
+++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
@@ -50,6 +50,8 @@ declare_lint_pass!(DefaultNumericFallback => [DEFAULT_NUMERIC_FALLBACK]);
 impl<'tcx> LateLintPass<'tcx> for DefaultNumericFallback {
     fn check_body(&mut self, cx: &LateContext<'tcx>, body: &Body<'tcx>) {
         let hir = cx.tcx.hir();
+        // NOTE: this is different from `clippy_utils::is_inside_always_const_context`.
+        // Inline const supports type inference.
         let is_parent_const = matches!(
             hir.body_const_context(hir.body_owner_def_id(body.id())),
             Some(ConstContext::Const { inline: false } | ConstContext::Static(_))
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index a115f8d0631..253f9959e13 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -3,7 +3,8 @@ use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use clippy_utils::sugg::has_enclosing_paren;
 use clippy_utils::ty::{implements_trait, is_manually_drop, peel_mid_ty_refs};
 use clippy_utils::{
-    expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, DefinedTy, ExprUseNode,
+    expr_use_ctxt, get_parent_expr, is_block_like, is_lint_allowed, path_to_local, peel_middle_ty_refs, DefinedTy,
+    ExprUseNode,
 };
 use core::mem;
 use rustc_ast::util::parser::{PREC_PREFIX, PREC_UNAMBIGUOUS};
@@ -175,6 +176,7 @@ struct StateData<'tcx> {
     adjusted_ty: Ty<'tcx>,
 }
 
+#[derive(Debug)]
 struct DerefedBorrow {
     count: usize,
     msg: &'static str,
@@ -182,6 +184,7 @@ struct DerefedBorrow {
     for_field_access: Option<Symbol>,
 }
 
+#[derive(Debug)]
 enum State {
     // Any number of deref method calls.
     DerefMethod {
@@ -744,7 +747,7 @@ fn in_postfix_position<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> boo
     }
 }
 
-#[derive(Clone, Copy)]
+#[derive(Clone, Copy, Debug)]
 enum TyCoercionStability {
     Deref,
     Reborrow,
@@ -1042,16 +1045,28 @@ fn report<'tcx>(
                 return;
             }
 
-            let (prefix, precedence) = if let Some(mutability) = mutability
-                && !typeck.expr_ty(expr).is_ref()
+            let ty = typeck.expr_ty(expr);
+
+            // `&&[T; N]`, or `&&..&[T; N]` (src) cannot coerce to `&[T]` (dst).
+            if let ty::Ref(_, dst, _) = data.adjusted_ty.kind()
+                && dst.is_slice()
             {
-                let prefix = match mutability {
-                    Mutability::Not => "&",
-                    Mutability::Mut => "&mut ",
-                };
-                (prefix, PREC_PREFIX)
-            } else {
-                ("", 0)
+                let (src, n_src_refs) = peel_middle_ty_refs(ty);
+                if n_src_refs >= 2 && src.is_array() {
+                    return;
+                }
+            }
+
+            let (prefix, precedence) = match mutability {
+                Some(mutability) if !ty.is_ref() => {
+                    let prefix = match mutability {
+                        Mutability::Not => "&",
+                        Mutability::Mut => "&mut ",
+                    };
+                    (prefix, PREC_PREFIX)
+                },
+                None if !ty.is_ref() && data.adjusted_ty.is_ref() => ("&", 0),
+                _ => ("", 0),
             };
             span_lint_hir_and_then(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs
index 9de879604e2..38fe687f7cc 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_methods.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_methods.rs
@@ -1,6 +1,6 @@
 use clippy_config::types::DisallowedPath;
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::{fn_def_id, get_parent_expr, path_def_id};
+use rustc_hir::def::{CtorKind, DefKind, Res};
 use rustc_hir::def_id::DefIdMap;
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -83,26 +83,26 @@ impl<'tcx> LateLintPass<'tcx> for DisallowedMethods {
     }
 
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        let uncalled_path = if let Some(parent) = get_parent_expr(cx, expr)
-            && let ExprKind::Call(receiver, _) = parent.kind
-            && receiver.hir_id == expr.hir_id
-        {
-            None
-        } else {
-            path_def_id(cx, expr)
+        let (id, span) = match &expr.kind {
+            ExprKind::Path(path)
+                if let Res::Def(DefKind::Fn | DefKind::Ctor(_, CtorKind::Fn) | DefKind::AssocFn, id) =
+                    cx.qpath_res(path, expr.hir_id) =>
+            {
+                (id, expr.span)
+            },
+            ExprKind::MethodCall(name, ..) if let Some(id) = cx.typeck_results().type_dependent_def_id(expr.hir_id) => {
+                (id, name.ident.span)
+            },
+            _ => return,
         };
-        let Some(def_id) = uncalled_path.or_else(|| fn_def_id(cx, expr)) else {
-            return;
-        };
-        let conf = match self.disallowed.get(&def_id) {
-            Some(&index) => &self.conf_disallowed[index],
-            None => return,
-        };
-        let msg = format!("use of a disallowed method `{}`", conf.path());
-        span_lint_and_then(cx, DISALLOWED_METHODS, expr.span, msg, |diag| {
-            if let Some(reason) = conf.reason() {
-                diag.note(reason);
-            }
-        });
+        if let Some(&index) = self.disallowed.get(&id) {
+            let conf = &self.conf_disallowed[index];
+            let msg = format!("use of a disallowed method `{}`", conf.path());
+            span_lint_and_then(cx, DISALLOWED_METHODS, span, msg, |diag| {
+                if let Some(reason) = conf.reason() {
+                    diag.note(reason);
+                }
+            });
+        }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_names.rs b/src/tools/clippy/clippy_lints/src/disallowed_names.rs
index 2afbf184117..58809604c37 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_names.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_names.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::is_test_module_or_function;
+use clippy_utils::is_in_test;
 use rustc_data_structures::fx::FxHashSet;
-use rustc_hir::{Item, Pat, PatKind};
+use rustc_hir::{Pat, PatKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
 
@@ -27,52 +27,30 @@ declare_clippy_lint! {
 #[derive(Clone, Debug)]
 pub struct DisallowedNames {
     disallow: FxHashSet<String>,
-    test_modules_deep: u32,
 }
 
 impl DisallowedNames {
     pub fn new(disallowed_names: &[String]) -> Self {
         Self {
             disallow: disallowed_names.iter().cloned().collect(),
-            test_modules_deep: 0,
         }
     }
-
-    fn in_test_module(&self) -> bool {
-        self.test_modules_deep != 0
-    }
 }
 
 impl_lint_pass!(DisallowedNames => [DISALLOWED_NAMES]);
 
 impl<'tcx> LateLintPass<'tcx> for DisallowedNames {
-    fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
-        if is_test_module_or_function(cx.tcx, item) {
-            self.test_modules_deep = self.test_modules_deep.saturating_add(1);
-        }
-    }
-
     fn check_pat(&mut self, cx: &LateContext<'tcx>, pat: &'tcx Pat<'_>) {
-        // Check whether we are under the `test` attribute.
-        if self.in_test_module() {
-            return;
-        }
-
-        if let PatKind::Binding(.., ident, _) = pat.kind {
-            if self.disallow.contains(&ident.name.to_string()) {
-                span_lint(
-                    cx,
-                    DISALLOWED_NAMES,
-                    ident.span,
-                    format!("use of a disallowed/placeholder name `{}`", ident.name),
-                );
-            }
-        }
-    }
-
-    fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
-        if is_test_module_or_function(cx.tcx, item) {
-            self.test_modules_deep = self.test_modules_deep.saturating_sub(1);
+        if let PatKind::Binding(.., ident, _) = pat.kind
+            && self.disallow.contains(&ident.name.to_string())
+            && !is_in_test(cx.tcx, pat.hir_id)
+        {
+            span_lint(
+                cx,
+                DISALLOWED_NAMES,
+                ident.span,
+                format!("use of a disallowed/placeholder name `{}`", ident.name),
+            );
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
index a995f06fb73..5ce11900adf 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
@@ -82,30 +82,25 @@ impl EarlyLintPass for DisallowedScriptIdents {
             // Note: `symbol.as_str()` is an expensive operation, thus should not be called
             // more than once for a single symbol.
             let symbol_str = symbol.as_str();
-            if symbol_str.is_ascii() {
-                continue;
-            }
 
-            for c in symbol_str.chars() {
-                // We want to iterate through all the scripts associated with this character
-                // and check whether at least of one scripts is in the whitelist.
-                let forbidden_script = c
-                    .script_extension()
-                    .iter()
-                    .find(|script| !self.whitelist.contains(script));
-                if let Some(script) = forbidden_script {
-                    span_lint(
-                        cx,
-                        DISALLOWED_SCRIPT_IDENTS,
-                        span,
-                        format!(
-                            "identifier `{symbol_str}` has a Unicode script that is not allowed by configuration: {}",
-                            script.full_name()
-                        ),
-                    );
-                    // We don't want to spawn warning multiple times over a single identifier.
-                    break;
-                }
+            // Check if any character in the symbol is not part of any allowed script.
+            // Fast path for ascii-only idents.
+            if !symbol_str.is_ascii()
+                && let Some(script) = symbol_str.chars().find_map(|c| {
+                    c.script_extension()
+                        .iter()
+                        .find(|script| !self.whitelist.contains(script))
+                })
+            {
+                span_lint(
+                    cx,
+                    DISALLOWED_SCRIPT_IDENTS,
+                    span,
+                    format!(
+                        "identifier `{symbol_str}` has a Unicode script that is not allowed by configuration: {}",
+                        script.full_name()
+                    ),
+                );
             }
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs
index 38bc58a5501..bd1cc46e185 100644
--- a/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/lazy_continuation.rs
@@ -22,6 +22,7 @@ pub(super) fn check(
     range: Range<usize>,
     mut span: Span,
     containers: &[super::Container],
+    line_break_span: Span,
 ) {
     if doc[range.clone()].contains('\t') {
         // We don't do tab stops correctly.
@@ -46,11 +47,35 @@ pub(super) fn check(
         .sum();
     if ccount < blockquote_level || lcount < list_indentation {
         let msg = if ccount < blockquote_level {
-            "doc quote missing `>` marker"
+            "doc quote line without `>` marker"
         } else {
-            "doc list item missing indentation"
+            "doc list item without indentation"
         };
         span_lint_and_then(cx, DOC_LAZY_CONTINUATION, span, msg, |diag| {
+            let snippet = clippy_utils::source::snippet(cx, line_break_span, "");
+            if snippet.chars().filter(|&c| c == '\n').count() > 1
+                && let Some(doc_comment_start) = snippet.rfind('\n')
+                && let doc_comment = snippet[doc_comment_start..].trim()
+                && (doc_comment == "///" || doc_comment == "//!")
+            {
+                // suggest filling in a blank line
+                diag.span_suggestion_with_style(
+                    line_break_span.shrink_to_lo(),
+                    "if this should be its own paragraph, add a blank doc comment line",
+                    format!("\n{doc_comment}"),
+                    Applicability::MaybeIncorrect,
+                    SuggestionStyle::ShowAlways,
+                );
+                if ccount > 0 || blockquote_level > 0 {
+                    diag.help("if this not intended to be a quote at all, escape it with `\\>`");
+                } else {
+                    let indent = list_indentation - lcount;
+                    diag.help(format!(
+                        "if this is intended to be part of the list, indent {indent} spaces"
+                    ));
+                }
+                return;
+            }
             if ccount == 0 && blockquote_level == 0 {
                 // simpler suggestion style for indentation
                 let indent = list_indentation - lcount;
diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs
index 3e210fd153b..a2a1a51920f 100644
--- a/src/tools/clippy/clippy_lints/src/doc/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs
@@ -6,7 +6,8 @@ use clippy_utils::ty::is_type_diagnostic_item;
 use clippy_utils::visitors::Visitable;
 use clippy_utils::{in_constant, is_entrypoint_fn, is_trait_impl_item, method_chain_args};
 use pulldown_cmark::Event::{
-    Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start, TaskListMarker, Text,
+    Code, DisplayMath, End, FootnoteReference, HardBreak, Html, InlineHtml, InlineMath, Rule, SoftBreak, Start,
+    TaskListMarker, Text,
 };
 use pulldown_cmark::Tag::{BlockQuote, CodeBlock, FootnoteDefinition, Heading, Item, Link, Paragraph};
 use pulldown_cmark::{BrokenLink, CodeBlockKind, CowStr, Options, TagEnd};
@@ -747,7 +748,8 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
             },
             Start(FootnoteDefinition(..)) => in_footnote_definition = true,
             End(TagEnd::FootnoteDefinition) => in_footnote_definition = false,
-            Start(_) | End(_) => (), // We don't care about other tags
+            Start(_) | End(_)  // We don't care about other tags
+            | TaskListMarker(_) | Code(_) | Rule | InlineMath(..) | DisplayMath(..) => (),
             SoftBreak | HardBreak => {
                 if !containers.is_empty()
                     && let Some((next_event, next_range)) = events.peek()
@@ -762,13 +764,24 @@ fn check_doc<'a, Events: Iterator<Item = (pulldown_cmark::Event<'a>, Range<usize
                         range.end..next_range.start,
                         Span::new(span.hi(), next_span.lo(), span.ctxt(), span.parent()),
                         &containers[..],
+                        span,
                     );
                 }
             },
-            TaskListMarker(_) | Code(_) | Rule | InlineMath(..) | DisplayMath(..) => (),
             FootnoteReference(text) | Text(text) => {
                 paragraph_range.end = range.end;
-                ticks_unbalanced |= text.contains('`') && !in_code;
+                let range_ = range.clone();
+                ticks_unbalanced |= text.contains('`')
+                    && !in_code
+                    && doc[range.clone()].bytes().enumerate().any(|(i, c)| {
+                        // scan the markdown source code bytes for backquotes that aren't preceded by backslashes
+                        // - use bytes, instead of chars, to avoid utf8 decoding overhead (special chars are ascii)
+                        // - relevant backquotes are within doc[range], but backslashes are not, because they're not
+                        //   actually part of the rendered text (pulldown-cmark doesn't emit any events for escapes)
+                        // - if `range_.start + i == 0`, then `range_.start + i - 1 == -1`, and since we're working in
+                        //   usize, that would underflow and maybe panic
+                        c == b'`' && (range_.start + i == 0 || doc.as_bytes().get(range_.start + i - 1) != Some(&b'\\'))
+                    });
                 if Some(&text) == in_link.as_ref() || ticks_unbalanced {
                     // Probably a link of the form `<http://example.com>`
                     // Which are represented as a link to "http://example.com" with
diff --git a/src/tools/clippy/clippy_lints/src/endian_bytes.rs b/src/tools/clippy/clippy_lints/src/endian_bytes.rs
index bb766e96338..99328e3e643 100644
--- a/src/tools/clippy/clippy_lints/src/endian_bytes.rs
+++ b/src/tools/clippy/clippy_lints/src/endian_bytes.rs
@@ -33,7 +33,7 @@ declare_clippy_lint! {
     /// Checks for the usage of the `to_le_bytes` method and/or the function `from_le_bytes`.
     ///
     /// ### Why restrict this?
-    /// To ensure use of big endian or the target’s endianness rather than little endian.
+    /// To ensure use of big-endian or the target’s endianness rather than little-endian.
     ///
     /// ### Example
     /// ```rust,ignore
@@ -51,7 +51,7 @@ declare_clippy_lint! {
     /// Checks for the usage of the `to_be_bytes` method and/or the function `from_be_bytes`.
     ///
     /// ### Why restrict this?
-    /// To ensure use of little endian or the target’s endianness rather than big endian.
+    /// To ensure use of little-endian or the target’s endianness rather than big-endian.
     ///
     /// ### Example
     /// ```rust,ignore
diff --git a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
index 6fb38a0d6dd..cf85c74e688 100644
--- a/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/impl_trait_in_params.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::is_in_test_function;
+use clippy_utils::is_in_test;
 
 use rustc_hir as hir;
 use rustc_hir::intravisit::FnKind;
@@ -41,7 +41,7 @@ fn report(cx: &LateContext<'_>, param: &GenericParam<'_>, generics: &Generics<'_
 pub(super) fn check_fn<'tcx>(cx: &LateContext<'_>, kind: &'tcx FnKind<'_>, body: &'tcx Body<'_>, hir_id: HirId) {
     if let FnKind::ItemFn(_, generics, _) = kind
         && cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public()
-        && !is_in_test_function(cx.tcx, hir_id)
+        && !is_in_test(cx.tcx, hir_id)
     {
         for param in generics.params {
             if param.is_impl_trait() {
@@ -59,7 +59,7 @@ pub(super) fn check_impl_item(cx: &LateContext<'_>, impl_item: &ImplItem<'_>) {
         && of_trait.is_none()
         && let body = cx.tcx.hir().body(body_id)
         && cx.tcx.visibility(cx.tcx.hir().body_owner_def_id(body.id())).is_public()
-        && !is_in_test_function(cx.tcx, impl_item.hir_id())
+        && !is_in_test(cx.tcx, impl_item.hir_id())
     {
         for param in impl_item.generics.params {
             if param.is_impl_trait() {
@@ -75,7 +75,7 @@ pub(super) fn check_trait_item(cx: &LateContext<'_>, trait_item: &TraitItem<'_>,
         && let hir::Node::Item(item) = cx.tcx.parent_hir_node(trait_item.hir_id())
         // ^^ (Will always be a trait)
         && !item.vis_span.is_empty() // Is public
-        && !is_in_test_function(cx.tcx, trait_item.hir_id())
+        && !is_in_test(cx.tcx, trait_item.hir_id())
     {
         for param in trait_item.generics.params {
             if param.is_impl_trait() {
diff --git a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs
index 35b4481bfee..5c63d48adaf 100644
--- a/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs
+++ b/src/tools/clippy/clippy_lints/src/incompatible_msrv.rs
@@ -1,6 +1,6 @@
 use clippy_config::msrvs::Msrv;
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::is_in_test_function;
+use clippy_utils::is_in_test;
 use rustc_attr::{StabilityLevel, StableSince};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::{Expr, ExprKind, HirId};
@@ -88,7 +88,7 @@ impl IncompatibleMsrv {
             return;
         }
         let version = self.get_def_id_version(cx.tcx, def_id);
-        if self.msrv.meets(version) || is_in_test_function(cx.tcx, node) {
+        if self.msrv.meets(version) || is_in_test(cx.tcx, node) {
             return;
         }
         if let ExpnKind::AstPass(_) | ExpnKind::Desugaring(_) = span.ctxt().outer_expn_data().kind {
diff --git a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs
index 9aedf5ec7e8..ec6174bc030 100644
--- a/src/tools/clippy/clippy_lints/src/inherent_to_string.rs
+++ b/src/tools/clippy/clippy_lints/src/inherent_to_string.rs
@@ -91,10 +91,6 @@ declare_lint_pass!(InherentToString => [INHERENT_TO_STRING, INHERENT_TO_STRING_S
 
 impl<'tcx> LateLintPass<'tcx> for InherentToString {
     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx ImplItem<'_>) {
-        if impl_item.span.from_expansion() {
-            return;
-        }
-
         // Check if item is a method called `to_string` and has a parameter 'self'
         if let ImplItemKind::Fn(ref signature, _) = impl_item.kind
             // #11201
@@ -106,6 +102,7 @@ impl<'tcx> LateLintPass<'tcx> for InherentToString {
             && decl.implicit_self.has_implicit_self()
             && decl.inputs.len() == 1
             && impl_item.generics.params.iter().all(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
+            && !impl_item.span.from_expansion()
             // Check if return type is String
             && is_type_lang_item(cx, return_ty(cx, impl_item.owner_id), LangItem::String)
             // Filters instances of to_string which are required by a trait
diff --git a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
index 1c8fd0a27f9..7f183bb601e 100644
--- a/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
+++ b/src/tools/clippy/clippy_lints/src/init_numbered_fields.rs
@@ -1,13 +1,12 @@
-use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::{snippet_with_applicability, snippet_with_context};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
+use rustc_span::SyntaxContext;
 use std::borrow::Cow;
-use std::cmp::Reverse;
-use std::collections::BinaryHeap;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -44,38 +43,56 @@ declare_lint_pass!(NumberedFields => [INIT_NUMBERED_FIELDS]);
 
 impl<'tcx> LateLintPass<'tcx> for NumberedFields {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) {
-        if let ExprKind::Struct(path, fields, None) = e.kind {
-            if !fields.is_empty()
-                && !e.span.from_expansion()
-                && fields
-                    .iter()
-                    .all(|f| f.ident.as_str().as_bytes().iter().all(u8::is_ascii_digit))
-                && !matches!(cx.qpath_res(path, e.hir_id), Res::Def(DefKind::TyAlias, ..))
-            {
-                let expr_spans = fields
-                    .iter()
-                    .map(|f| (Reverse(f.ident.as_str().parse::<usize>().unwrap()), f.expr.span))
-                    .collect::<BinaryHeap<_>>();
-                let mut appl = Applicability::MachineApplicable;
-                let snippet = format!(
-                    "{}({})",
-                    snippet_with_applicability(cx, path.span(), "..", &mut appl),
-                    expr_spans
-                        .into_iter_sorted()
-                        .map(|(_, span)| snippet_with_context(cx, span, path.span().ctxt(), "..", &mut appl).0)
-                        .intersperse(Cow::Borrowed(", "))
-                        .collect::<String>()
-                );
-                span_lint_and_sugg(
-                    cx,
-                    INIT_NUMBERED_FIELDS,
-                    e.span,
-                    "used a field initializer for a tuple struct",
-                    "try",
-                    snippet,
-                    appl,
-                );
-            }
+        if let ExprKind::Struct(path, fields @ [field, ..], None) = e.kind
+            // If the first character of any field is a digit it has to be a tuple.
+            && field.ident.as_str().as_bytes().first().is_some_and(u8::is_ascii_digit)
+            // Type aliases can't be used as functions.
+            && !matches!(
+                cx.qpath_res(path, e.hir_id),
+                Res::Def(DefKind::TyAlias | DefKind::AssocTy, _)
+            )
+            // This is the only syntax macros can use that works for all struct types.
+            && !e.span.from_expansion()
+            && let mut has_side_effects = false
+            && let Ok(mut expr_spans) = fields
+                .iter()
+                .map(|f| {
+                    has_side_effects |= f.expr.can_have_side_effects();
+                    f.ident.as_str().parse::<usize>().map(|x| (x, f.expr.span))
+                })
+                .collect::<Result<Vec<_>, _>>()
+            // We can only reorder the expressions if there are no side effects.
+            && (!has_side_effects || expr_spans.is_sorted_by_key(|&(idx, _)| idx))
+        {
+            span_lint_and_then(
+                cx,
+                INIT_NUMBERED_FIELDS,
+                e.span,
+                "used a field initializer for a tuple struct",
+                |diag| {
+                    if !has_side_effects {
+                        // We already checked the order if there are side effects.
+                        expr_spans.sort_by_key(|&(idx, _)| idx);
+                    }
+                    let mut app = Applicability::MachineApplicable;
+                    diag.span_suggestion(
+                        e.span,
+                        "use tuple initialization",
+                        format!(
+                            "{}({})",
+                            snippet_with_applicability(cx, path.span(), "..", &mut app),
+                            expr_spans
+                                .into_iter()
+                                .map(
+                                    |(_, span)| snippet_with_context(cx, span, SyntaxContext::root(), "..", &mut app).0
+                                )
+                                .intersperse(Cow::Borrowed(", "))
+                                .collect::<String>()
+                        ),
+                        app,
+                    );
+                },
+            );
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
index 860258fd030..5657c58bb0a 100644
--- a/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
+++ b/src/tools/clippy/clippy_lints/src/inline_fn_without_body.rs
@@ -2,12 +2,11 @@
 
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::sugg::DiagExt;
-use rustc_ast::ast::Attribute;
 use rustc_errors::Applicability;
 use rustc_hir::{TraitFn, TraitItem, TraitItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::{sym, Symbol};
+use rustc_span::sym;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -34,27 +33,23 @@ declare_lint_pass!(InlineFnWithoutBody => [INLINE_FN_WITHOUT_BODY]);
 
 impl<'tcx> LateLintPass<'tcx> for InlineFnWithoutBody {
     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
-        if let TraitItemKind::Fn(_, TraitFn::Required(_)) = item.kind {
-            let attrs = cx.tcx.hir().attrs(item.hir_id());
-            check_attrs(cx, item.ident.name, attrs);
+        if let TraitItemKind::Fn(_, TraitFn::Required(_)) = item.kind
+            && let Some(attr) = cx
+                .tcx
+                .hir()
+                .attrs(item.hir_id())
+                .iter()
+                .find(|a| a.has_name(sym::inline))
+        {
+            span_lint_and_then(
+                cx,
+                INLINE_FN_WITHOUT_BODY,
+                attr.span,
+                format!("use of `#[inline]` on trait method `{}` which has no body", item.ident),
+                |diag| {
+                    diag.suggest_remove_item(cx, attr.span, "remove", Applicability::MachineApplicable);
+                },
+            );
         }
     }
 }
-
-fn check_attrs(cx: &LateContext<'_>, name: Symbol, attrs: &[Attribute]) {
-    for attr in attrs {
-        if !attr.has_name(sym::inline) {
-            continue;
-        }
-
-        span_lint_and_then(
-            cx,
-            INLINE_FN_WITHOUT_BODY,
-            attr.span,
-            format!("use of `#[inline]` on trait method `{name}` which has no body"),
-            |diag| {
-                diag.suggest_remove_item(cx, attr.span, "remove", Applicability::MachineApplicable);
-            },
-        );
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
index 10b00f632bb..5fe152d1e30 100644
--- a/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
+++ b/src/tools/clippy/clippy_lints/src/instant_subtraction.rs
@@ -85,16 +85,19 @@ impl LateLintPass<'_> for InstantSubtraction {
             lhs,
             rhs,
         ) = expr.kind
+            && let typeck = cx.typeck_results()
+            && ty::is_type_diagnostic_item(cx, typeck.expr_ty(lhs), sym::Instant)
         {
+            let rhs_ty = typeck.expr_ty(rhs);
+
             if is_instant_now_call(cx, lhs)
-                && is_an_instant(cx, rhs)
+                && ty::is_type_diagnostic_item(cx, rhs_ty, sym::Instant)
                 && let Some(sugg) = Sugg::hir_opt(cx, rhs)
             {
                 print_manual_instant_elapsed_sugg(cx, expr, sugg);
-            } else if !expr.span.from_expansion()
+            } else if ty::is_type_diagnostic_item(cx, rhs_ty, sym::Duration)
+                && !expr.span.from_expansion()
                 && self.msrv.meets(msrvs::TRY_FROM)
-                && is_an_instant(cx, lhs)
-                && is_a_duration(cx, rhs)
             {
                 print_unchecked_duration_subtraction_sugg(cx, lhs, rhs, expr);
             }
@@ -115,16 +118,6 @@ fn is_instant_now_call(cx: &LateContext<'_>, expr_block: &'_ Expr<'_>) -> bool {
     }
 }
 
-fn is_an_instant(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    let expr_ty = cx.typeck_results().expr_ty(expr);
-    ty::is_type_diagnostic_item(cx, expr_ty, sym::Instant)
-}
-
-fn is_a_duration(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
-    let expr_ty = cx.typeck_results().expr_ty(expr);
-    ty::is_type_diagnostic_item(cx, expr_ty, sym::Duration)
-}
-
 fn print_manual_instant_elapsed_sugg(cx: &LateContext<'_>, expr: &Expr<'_>, sugg: Sugg<'_>) {
     span_lint_and_sugg(
         cx,
diff --git a/src/tools/clippy/clippy_lints/src/items_after_statements.rs b/src/tools/clippy/clippy_lints/src/items_after_statements.rs
index 39223c20470..a88d8e24fda 100644
--- a/src/tools/clippy/clippy_lints/src/items_after_statements.rs
+++ b/src/tools/clippy/clippy_lints/src/items_after_statements.rs
@@ -54,36 +54,35 @@ declare_lint_pass!(ItemsAfterStatements => [ITEMS_AFTER_STATEMENTS]);
 
 impl LateLintPass<'_> for ItemsAfterStatements {
     fn check_block(&mut self, cx: &LateContext<'_>, block: &Block<'_>) {
-        if in_external_macro(cx.sess(), block.span) {
-            return;
-        }
-
-        // skip initial items
-        let stmts = block
-            .stmts
-            .iter()
-            .skip_while(|stmt| matches!(stmt.kind, StmtKind::Item(..)));
-
-        // lint on all further items
-        for stmt in stmts {
-            if let StmtKind::Item(item_id) = stmt.kind {
-                let item = cx.tcx.hir().item(item_id);
-                if in_external_macro(cx.sess(), item.span) || !item.span.eq_ctxt(block.span) {
-                    return;
-                }
-                if let ItemKind::Macro(..) = item.kind {
-                    // do not lint `macro_rules`, but continue processing further statements
-                    continue;
-                }
-                span_lint_hir(
-                    cx,
-                    ITEMS_AFTER_STATEMENTS,
-                    item.hir_id(),
-                    item.span,
-                    "adding items after statements is confusing, since items exist from the \
-                     start of the scope",
-                );
-            }
+        if block.stmts.len() > 1 {
+            let ctxt = block.span.ctxt();
+            let mut in_external = None;
+            block
+                .stmts
+                .iter()
+                .skip_while(|stmt| matches!(stmt.kind, StmtKind::Item(..)))
+                .filter_map(|stmt| match stmt.kind {
+                    StmtKind::Item(id) => Some(cx.tcx.hir().item(id)),
+                    _ => None,
+                })
+                // Ignore macros since they can only see previously defined locals.
+                .filter(|item| !matches!(item.kind, ItemKind::Macro(..)))
+                // Stop linting if macros define items.
+                .take_while(|item| item.span.ctxt() == ctxt)
+                // Don't use `next` due to the complex filter chain.
+                .for_each(|item| {
+                    // Only do the macro check once, but delay it until it's needed.
+                    if !*in_external.get_or_insert_with(|| in_external_macro(cx.sess(), block.span)) {
+                        span_lint_hir(
+                            cx,
+                            ITEMS_AFTER_STATEMENTS,
+                            item.hir_id(),
+                            item.span,
+                            "adding items after statements is confusing, since items exist from the \
+                                start of the scope",
+                        );
+                    }
+                });
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs
index 1b5f1b49947..ba0cd5d6eb3 100644
--- a/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs
+++ b/src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs
@@ -4,7 +4,7 @@ use rustc_hir::def_id::LocalDefId;
 use rustc_hir::{FnSig, ImplItem, ImplItemKind, Item, ItemKind, Node, TraitItem, TraitItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
-use rustc_span::symbol::sym;
+use rustc_span::{sym, Symbol};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -43,30 +43,27 @@ declare_lint_pass!(IterNotReturningIterator => [ITER_NOT_RETURNING_ITERATOR]);
 
 impl<'tcx> LateLintPass<'tcx> for IterNotReturningIterator {
     fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) {
-        let name = item.ident.name.as_str();
-        if matches!(name, "iter" | "iter_mut") {
-            if let TraitItemKind::Fn(fn_sig, _) = &item.kind {
-                check_sig(cx, name, fn_sig, item.owner_id.def_id);
-            }
+        if let TraitItemKind::Fn(fn_sig, _) = &item.kind
+            && matches!(item.ident.name, sym::iter | sym::iter_mut)
+        {
+            check_sig(cx, item.ident.name, fn_sig, item.owner_id.def_id);
         }
     }
 
     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'tcx>) {
-        let name = item.ident.name.as_str();
-        if matches!(name, "iter" | "iter_mut")
+        if let ImplItemKind::Fn(fn_sig, _) = &item.kind
+            && matches!(item.ident.name, sym::iter | sym::iter_mut)
             && !matches!(
                 cx.tcx.parent_hir_node(item.hir_id()),
                 Node::Item(Item { kind: ItemKind::Impl(i), .. }) if i.of_trait.is_some()
             )
         {
-            if let ImplItemKind::Fn(fn_sig, _) = &item.kind {
-                check_sig(cx, name, fn_sig, item.owner_id.def_id);
-            }
+            check_sig(cx, item.ident.name, fn_sig, item.owner_id.def_id);
         }
     }
 }
 
-fn check_sig(cx: &LateContext<'_>, name: &str, sig: &FnSig<'_>, fn_id: LocalDefId) {
+fn check_sig(cx: &LateContext<'_>, name: Symbol, sig: &FnSig<'_>, fn_id: LocalDefId) {
     if sig.decl.implicit_self.has_implicit_self() {
         let ret_ty = cx
             .tcx
diff --git a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs
index 6b03f2597b0..1e6404190d3 100644
--- a/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs
+++ b/src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs
@@ -125,13 +125,13 @@ fn is_ty_exported(cx: &LateContext<'_>, ty: Ty<'_>) -> bool {
 
 impl LateLintPass<'_> for IterWithoutIntoIter {
     fn check_item(&mut self, cx: &LateContext<'_>, item: &rustc_hir::Item<'_>) {
-        if !in_external_macro(cx.sess(), item.span)
-            && let ItemKind::Impl(imp) = item.kind
+        if let ItemKind::Impl(imp) = item.kind
             && let TyKind::Ref(_, self_ty_without_ref) = &imp.self_ty.kind
             && let Some(trait_ref) = imp.of_trait
             && trait_ref
                 .trait_def_id()
                 .is_some_and(|did| cx.tcx.is_diagnostic_item(sym::IntoIterator, did))
+            && !in_external_macro(cx.sess(), item.span)
             && let &ty::Ref(_, ty, mtbl) = cx.tcx.type_of(item.owner_id).instantiate_identity().kind()
             && let expected_method_name = match mtbl {
                 Mutability::Mut => sym::iter_mut,
diff --git a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
index 7f8197c0cc0..b18ab625e60 100644
--- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
+++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
@@ -46,12 +46,12 @@ impl_lint_pass!(LargeConstArrays => [LARGE_CONST_ARRAYS]);
 
 impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-        if !item.span.from_expansion()
-            && let ItemKind::Const(_, generics, _) = &item.kind
+        if let ItemKind::Const(_, generics, _) = &item.kind
             // Since static items may not have generics, skip generic const items.
             // FIXME(generic_const_items): I don't think checking `generics.hwcp` suffices as it
             // doesn't account for empty where-clauses that only consist of keyword `where` IINM.
             && generics.params.is_empty() && !generics.has_where_clause_predicates
+            && !item.span.from_expansion()
             && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
             && let ty::Array(element_type, cst) = ty.kind()
             && let ConstKind::Value(_, ty::ValTree::Leaf(element_count)) = cst.kind()
diff --git a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
index 0bf7389ef9c..85daadcc537 100644
--- a/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
+++ b/src/tools/clippy/clippy_lints/src/large_enum_variant.rs
@@ -77,17 +77,12 @@ impl_lint_pass!(LargeEnumVariant => [LARGE_ENUM_VARIANT]);
 
 impl<'tcx> LateLintPass<'tcx> for LargeEnumVariant {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &Item<'tcx>) {
-        if in_external_macro(cx.tcx.sess, item.span) {
-            return;
-        }
-        if let ItemKind::Enum(ref def, _) = item.kind {
-            let ty = cx.tcx.type_of(item.owner_id).instantiate_identity();
-            let ty::Adt(adt, subst) = ty.kind() else {
-                panic!("already checked whether this is an enum")
-            };
-            if adt.variants().len() <= 1 {
-                return;
-            }
+        if let ItemKind::Enum(ref def, _) = item.kind
+            && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
+            && let ty::Adt(adt, subst) = ty.kind()
+            && adt.variants().len() > 1
+            && !in_external_macro(cx.tcx.sess, item.span)
+        {
             let variants_size = AdtVariantInfo::new(cx, *adt, subst);
 
             let mut difference = variants_size[0].size - variants_size[1].size;
diff --git a/src/tools/clippy/clippy_lints/src/large_futures.rs b/src/tools/clippy/clippy_lints/src/large_futures.rs
index 07488a512a3..602227e4249 100644
--- a/src/tools/clippy/clippy_lints/src/large_futures.rs
+++ b/src/tools/clippy/clippy_lints/src/large_futures.rs
@@ -54,29 +54,26 @@ impl_lint_pass!(LargeFuture => [LARGE_FUTURES]);
 
 impl<'tcx> LateLintPass<'tcx> for LargeFuture {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        if matches!(expr.span.ctxt().outer_expn_data().kind, rustc_span::ExpnKind::Macro(..)) {
-            return;
-        }
-        if let ExprKind::Match(expr, _, MatchSource::AwaitDesugar) = expr.kind {
-            if let ExprKind::Call(func, [expr, ..]) = expr.kind
-                && let ExprKind::Path(QPath::LangItem(LangItem::IntoFutureIntoFuture, ..)) = func.kind
-                && let ty = cx.typeck_results().expr_ty(expr)
-                && let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait()
-                && implements_trait(cx, ty, future_trait_def_id, &[])
-                && let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty))
-                && let size = layout.layout.size()
-                && size >= Size::from_bytes(self.future_size_threshold)
-            {
-                span_lint_and_sugg(
-                    cx,
-                    LARGE_FUTURES,
-                    expr.span,
-                    format!("large future with a size of {} bytes", size.bytes()),
-                    "consider `Box::pin` on it",
-                    format!("Box::pin({})", snippet(cx, expr.span, "..")),
-                    Applicability::Unspecified,
-                );
-            }
+        if let ExprKind::Match(scrutinee, _, MatchSource::AwaitDesugar) = expr.kind
+            && let ExprKind::Call(func, [arg, ..]) = scrutinee.kind
+            && let ExprKind::Path(QPath::LangItem(LangItem::IntoFutureIntoFuture, ..)) = func.kind
+            && !expr.span.from_expansion()
+            && let ty = cx.typeck_results().expr_ty(arg)
+            && let Some(future_trait_def_id) = cx.tcx.lang_items().future_trait()
+            && implements_trait(cx, ty, future_trait_def_id, &[])
+            && let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty))
+            && let size = layout.layout.size()
+            && size >= Size::from_bytes(self.future_size_threshold)
+        {
+            span_lint_and_sugg(
+                cx,
+                LARGE_FUTURES,
+                arg.span,
+                format!("large future with a size of {} bytes", size.bytes()),
+                "consider `Box::pin` on it",
+                format!("Box::pin({})", snippet(cx, arg.span, "..")),
+                Applicability::Unspecified,
+            );
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/large_include_file.rs b/src/tools/clippy/clippy_lints/src/large_include_file.rs
index 07efee159aa..2688283a6ce 100644
--- a/src/tools/clippy/clippy_lints/src/large_include_file.rs
+++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs
@@ -1,5 +1,4 @@
 use clippy_utils::diagnostics::span_lint_and_note;
-use clippy_utils::is_lint_allowed;
 use clippy_utils::macros::root_macro_call_first_node;
 use rustc_ast::LitKind;
 use rustc_hir::{Expr, ExprKind};
@@ -52,24 +51,19 @@ impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]);
 
 impl LateLintPass<'_> for LargeIncludeFile {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
-        if let Some(macro_call) = root_macro_call_first_node(cx, expr)
-            && !is_lint_allowed(cx, LARGE_INCLUDE_FILE, expr.hir_id)
-            && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id)
-                || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id))
-            && let ExprKind::Lit(lit) = &expr.kind
-        {
-            let len = match &lit.node {
+        if let ExprKind::Lit(lit) = &expr.kind
+            && let len = match &lit.node {
                 // include_bytes
                 LitKind::ByteStr(bstr, _) => bstr.len(),
                 // include_str
                 LitKind::Str(sym, _) => sym.as_str().len(),
                 _ => return,
-            };
-
-            if len as u64 <= self.max_file_size {
-                return;
             }
-
+            && len as u64 > self.max_file_size
+            && let Some(macro_call) = root_macro_call_first_node(cx, expr)
+            && (cx.tcx.is_diagnostic_item(sym::include_bytes_macro, macro_call.def_id)
+                || cx.tcx.is_diagnostic_item(sym::include_str_macro, macro_call.def_id))
+        {
             span_lint_and_note(
                 cx,
                 LARGE_INCLUDE_FILE,
diff --git a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs
index eadfeb6e341..a08b40bef37 100644
--- a/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs
+++ b/src/tools/clippy/clippy_lints/src/legacy_numeric_constants.rs
@@ -48,15 +48,11 @@ impl_lint_pass!(LegacyNumericConstants => [LEGACY_NUMERIC_CONSTANTS]);
 
 impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
-        let Self { msrv } = self;
-
-        if !msrv.meets(NUMERIC_ASSOCIATED_CONSTANTS) || in_external_macro(cx.sess(), item.span) {
-            return;
-        }
-
         // Integer modules are "TBD" deprecated, and the contents are too,
         // so lint on the `use` statement directly.
         if let ItemKind::Use(path, kind @ (UseKind::Single | UseKind::Glob)) = item.kind
+            && self.msrv.meets(NUMERIC_ASSOCIATED_CONSTANTS)
+            && !in_external_macro(cx.sess(), item.span)
             && let Some(def_id) = path.res[0].opt_def_id()
         {
             let module = if is_integer_module(cx, def_id) {
@@ -103,12 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants {
     }
 
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx rustc_hir::Expr<'tcx>) {
-        let Self { msrv } = self;
-
-        if !msrv.meets(NUMERIC_ASSOCIATED_CONSTANTS) || in_external_macro(cx.sess(), expr.span) {
-            return;
-        }
-        let ExprKind::Path(qpath) = expr.kind else {
+        let ExprKind::Path(qpath) = &expr.kind else {
             return;
         };
 
@@ -129,10 +120,10 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants {
             )
         // `<integer>::xxx_value` check
         } else if let QPath::TypeRelative(_, last_segment) = qpath
-            && let Some(def_id) = cx.qpath_res(&qpath, expr.hir_id).opt_def_id()
-            && is_integer_method(cx, def_id)
+            && let Some(def_id) = cx.qpath_res(qpath, expr.hir_id).opt_def_id()
             && let Some(par_expr) = get_parent_expr(cx, expr)
-            && let ExprKind::Call(_, _) = par_expr.kind
+            && let ExprKind::Call(_, []) = par_expr.kind
+            && is_integer_method(cx, def_id)
         {
             let name = last_segment.ident.name.as_str();
 
@@ -145,19 +136,20 @@ impl<'tcx> LateLintPass<'tcx> for LegacyNumericConstants {
             return;
         };
 
-        if is_from_proc_macro(cx, expr) {
-            return;
+        if self.msrv.meets(NUMERIC_ASSOCIATED_CONSTANTS)
+            && !in_external_macro(cx.sess(), expr.span)
+            && !is_from_proc_macro(cx, expr)
+        {
+            span_lint_hir_and_then(cx, LEGACY_NUMERIC_CONSTANTS, expr.hir_id, span, msg, |diag| {
+                diag.span_suggestion_with_style(
+                    span,
+                    "use the associated constant instead",
+                    sugg,
+                    Applicability::MaybeIncorrect,
+                    SuggestionStyle::ShowAlways,
+                );
+            });
         }
-
-        span_lint_hir_and_then(cx, LEGACY_NUMERIC_CONSTANTS, expr.hir_id, span, msg, |diag| {
-            diag.span_suggestion_with_style(
-                span,
-                "use the associated constant instead",
-                sugg,
-                Applicability::MaybeIncorrect,
-                SuggestionStyle::ShowAlways,
-            );
-        });
     }
 
     extract_msrv_attr!(LateContext);
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index 57e0a7aa2c7..4c737371bd2 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -121,11 +121,9 @@ declare_lint_pass!(LenZero => [LEN_ZERO, LEN_WITHOUT_IS_EMPTY, COMPARISON_TO_EMP
 
 impl<'tcx> LateLintPass<'tcx> for LenZero {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
-        if item.span.from_expansion() {
-            return;
-        }
-
-        if let ItemKind::Trait(_, _, _, _, trait_items) = item.kind {
+        if let ItemKind::Trait(_, _, _, _, trait_items) = item.kind
+            && !item.span.from_expansion()
+        {
             check_trait_items(cx, item, trait_items);
         }
     }
@@ -162,17 +160,14 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
     }
 
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if expr.span.from_expansion() {
-            return;
-        }
-
         if let ExprKind::Let(lt) = expr.kind
-            && has_is_empty(cx, lt.init)
             && match lt.pat.kind {
                 PatKind::Slice([], None, []) => true,
                 PatKind::Lit(lit) if is_empty_string(lit) => true,
                 _ => false,
             }
+            && !expr.span.from_expansion()
+            && has_is_empty(cx, lt.init)
         {
             let mut applicability = Applicability::MachineApplicable;
 
@@ -190,7 +185,9 @@ impl<'tcx> LateLintPass<'tcx> for LenZero {
             );
         }
 
-        if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind {
+        if let ExprKind::Binary(Spanned { node: cmp, .. }, left, right) = expr.kind
+            && !expr.span.from_expansion()
+        {
             // expr.span might contains parenthesis, see issue #10529
             let actual_span = span_without_enclosing_paren(cx, expr.span);
             match cmp {
diff --git a/src/tools/clippy/clippy_lints/src/let_if_seq.rs b/src/tools/clippy/clippy_lints/src/let_if_seq.rs
index a65cb3f4dfb..0e488cee6b7 100644
--- a/src/tools/clippy/clippy_lints/src/let_if_seq.rs
+++ b/src/tools/clippy/clippy_lints/src/let_if_seq.rs
@@ -58,12 +58,10 @@ declare_lint_pass!(LetIfSeq => [USELESS_LET_IF_SEQ]);
 
 impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
     fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'_>) {
-        let mut it = block.stmts.iter().peekable();
-        while let Some(stmt) = it.next() {
-            if let Some(expr) = it.peek()
-                && let hir::StmtKind::Let(local) = stmt.kind
+        for [stmt, next] in block.stmts.array_windows::<2>() {
+            if let hir::StmtKind::Let(local) = stmt.kind
                 && let hir::PatKind::Binding(mode, canonical_id, ident, None) = local.pat.kind
-                && let hir::StmtKind::Expr(if_) = expr.kind
+                && let hir::StmtKind::Expr(if_) = next.kind
                 && let hir::ExprKind::If(
                     hir::Expr {
                         kind: hir::ExprKind::DropTemps(cond),
diff --git a/src/tools/clippy/clippy_lints/src/let_underscore.rs b/src/tools/clippy/clippy_lints/src/let_underscore.rs
index 9fd4f509aa4..8fa63f3e8fd 100644
--- a/src/tools/clippy/clippy_lints/src/let_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/let_underscore.rs
@@ -139,9 +139,9 @@ const SYNC_GUARD_PATHS: [&[&str]; 3] = [
 impl<'tcx> LateLintPass<'tcx> for LetUnderscore {
     fn check_local(&mut self, cx: &LateContext<'tcx>, local: &LetStmt<'tcx>) {
         if matches!(local.source, LocalSource::Normal)
-            && !in_external_macro(cx.tcx.sess, local.span)
             && let PatKind::Wild = local.pat.kind
             && let Some(init) = local.init
+            && !in_external_macro(cx.tcx.sess, local.span)
         {
             let init_ty = cx.typeck_results().expr_ty(init);
             let contains_sync_guard = init_ty.walk().any(|inner| match inner.unpack() {
diff --git a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
index 593b29154b4..5a11702d7ce 100644
--- a/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
+++ b/src/tools/clippy/clippy_lints/src/let_with_type_underscore.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::source::snippet;
+use clippy_utils::is_from_proc_macro;
 use rustc_hir::{LetStmt, TyKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
@@ -25,19 +25,14 @@ declare_clippy_lint! {
 }
 declare_lint_pass!(UnderscoreTyped => [LET_WITH_TYPE_UNDERSCORE]);
 
-impl LateLintPass<'_> for UnderscoreTyped {
-    fn check_local(&mut self, cx: &LateContext<'_>, local: &LetStmt<'_>) {
-        if !in_external_macro(cx.tcx.sess, local.span)
-            && let Some(ty) = local.ty // Ensure that it has a type defined
+impl<'tcx> LateLintPass<'tcx> for UnderscoreTyped {
+    fn check_local(&mut self, cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) {
+        if let Some(ty) = local.ty // Ensure that it has a type defined
             && let TyKind::Infer = &ty.kind // that type is '_'
             && local.span.eq_ctxt(ty.span)
+            && !in_external_macro(cx.tcx.sess, local.span)
+            && !is_from_proc_macro(cx, ty)
         {
-            // NOTE: Using `is_from_proc_macro` on `init` will require that it's initialized,
-            // this doesn't. Alternatively, `WithSearchPat` can be implemented for `Ty`
-            if snippet(cx, ty.span, "_").trim() != "_" {
-                return;
-            }
-
             span_lint_and_help(
                 cx,
                 LET_WITH_TYPE_UNDERSCORE,
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 32fad0f02ce..c2dc26d6605 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -4,7 +4,9 @@
 #![feature(f128)]
 #![feature(f16)]
 #![feature(if_let_guard)]
+#![feature(is_sorted)]
 #![feature(iter_intersperse)]
+#![feature(iter_partition_in_place)]
 #![feature(let_chains)]
 #![cfg_attr(bootstrap, feature(lint_reasons))]
 #![feature(never_type)]
@@ -90,8 +92,10 @@ mod bool_to_int_with_if;
 mod booleans;
 mod borrow_deref_ref;
 mod box_default;
+mod byte_char_slices;
 mod cargo;
 mod casts;
+mod cfg_not_test;
 mod checked_conversions;
 mod cognitive_complexity;
 mod collapsible_if;
@@ -212,6 +216,7 @@ mod manual_non_exhaustive;
 mod manual_range_patterns;
 mod manual_rem_euclid;
 mod manual_retain;
+mod manual_rotate;
 mod manual_slice_size_calculation;
 mod manual_string_new;
 mod manual_strip;
@@ -229,6 +234,7 @@ mod mismatching_type_param_order;
 mod missing_assert_message;
 mod missing_asserts_for_indexing;
 mod missing_const_for_fn;
+mod missing_const_for_thread_local;
 mod missing_doc;
 mod missing_enforced_import_rename;
 mod missing_fields_in_debug;
@@ -275,9 +281,9 @@ mod only_used_in_recursion;
 mod operators;
 mod option_env_unwrap;
 mod option_if_let_else;
-mod overflow_check_conditional;
 mod panic_in_result_fn;
 mod panic_unimplemented;
+mod panicking_overflow_checks;
 mod partial_pub_fields;
 mod partialeq_ne_impl;
 mod partialeq_to_none;
@@ -318,6 +324,7 @@ mod self_named_constructors;
 mod semicolon_block;
 mod semicolon_if_nothing_returned;
 mod serde_api;
+mod set_contains_or_insert;
 mod shadow;
 mod significant_drop_tightening;
 mod single_call_fn;
@@ -339,7 +346,6 @@ mod swap_ptr_to_ref;
 mod tabs_in_doc_comments;
 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;
@@ -639,9 +645,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
         });
         store.register_early_pass(|| Box::new(utils::internal_lints::produce_ice::ProduceIce));
         store.register_late_pass(|_| Box::new(utils::internal_lints::collapsible_calls::CollapsibleCalls));
-        store.register_late_pass(|_| {
-            Box::new(utils::internal_lints::compiler_lint_functions::CompilerLintFunctions::new())
-        });
         store.register_late_pass(|_| Box::new(utils::internal_lints::invalid_paths::InvalidPaths));
         store.register_late_pass(|_| {
             Box::<utils::internal_lints::interning_defined_symbol::InterningDefinedSymbol>::default()
@@ -788,7 +791,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     let format_args = format_args_storage.clone();
     store.register_late_pass(move |_| Box::new(format::UselessFormat::new(format_args.clone())));
     store.register_late_pass(|_| Box::new(swap::Swap));
-    store.register_late_pass(|_| Box::new(overflow_check_conditional::OverflowCheckConditional));
+    store.register_late_pass(|_| Box::new(panicking_overflow_checks::PanickingOverflowChecks));
     store.register_late_pass(|_| Box::<new_without_default::NewWithoutDefault>::default());
     store.register_late_pass(move |_| Box::new(disallowed_names::DisallowedNames::new(disallowed_names)));
     store.register_late_pass(move |_| {
@@ -1024,6 +1027,7 @@ 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(manual_rotate::ManualRotate));
     store.register_late_pass(move |_| {
         Box::new(operators::Operators::new(
             verbose_bit_mask_threshold,
@@ -1153,9 +1157,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
             behavior: pub_underscore_fields_behavior,
         })
     });
-    store.register_late_pass(move |_| {
-        Box::new(thread_local_initializer_can_be_made_const::ThreadLocalInitializerCanBeMadeConst::new(msrv()))
-    });
+    store
+        .register_late_pass(move |_| Box::new(missing_const_for_thread_local::MissingConstForThreadLocal::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));
     store.register_early_pass(|| Box::new(multiple_bound_locations::MultipleBoundLocations));
@@ -1171,6 +1174,9 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     });
     store.register_late_pass(move |_| Box::new(string_patterns::StringPatterns::new(msrv())));
     store.register_early_pass(|| Box::new(field_scoped_visibility_modifiers::FieldScopedVisibilityModifiers));
+    store.register_late_pass(|_| Box::new(set_contains_or_insert::HashsetInsertAfterContains));
+    store.register_early_pass(|| Box::new(byte_char_slices::ByteCharSlice));
+    store.register_early_pass(|| Box::new(cfg_not_test::CfgNotTest));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/literal_representation.rs b/src/tools/clippy/clippy_lints/src/literal_representation.rs
index d2a140a36a8..7481543941a 100644
--- a/src/tools/clippy/clippy_lints/src/literal_representation.rs
+++ b/src/tools/clippy/clippy_lints/src/literal_representation.rs
@@ -233,11 +233,9 @@ impl_lint_pass!(LiteralDigitGrouping => [
 
 impl EarlyLintPass for LiteralDigitGrouping {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
-        if in_external_macro(cx.sess(), expr.span) {
-            return;
-        }
-
-        if let ExprKind::Lit(lit) = expr.kind {
+        if let ExprKind::Lit(lit) = expr.kind
+            && !in_external_macro(cx.sess(), expr.span)
+        {
             self.check_lit(cx, lit, expr.span);
         }
     }
@@ -448,11 +446,9 @@ impl_lint_pass!(DecimalLiteralRepresentation => [DECIMAL_LITERAL_REPRESENTATION]
 
 impl EarlyLintPass for DecimalLiteralRepresentation {
     fn check_expr(&mut self, cx: &EarlyContext<'_>, expr: &Expr) {
-        if in_external_macro(cx.sess(), expr.span) {
-            return;
-        }
-
-        if let ExprKind::Lit(lit) = expr.kind {
+        if let ExprKind::Lit(lit) = expr.kind
+            && !in_external_macro(cx.sess(), expr.span)
+        {
             self.check_lit(cx, lit, expr.span);
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs
index 24fc2b4faea..d9f6be6dc2b 100644
--- a/src/tools/clippy/clippy_lints/src/manual_bits.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs
@@ -50,13 +50,10 @@ impl_lint_pass!(ManualBits => [MANUAL_BITS]);
 
 impl<'tcx> LateLintPass<'tcx> for ManualBits {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !self.msrv.meets(msrvs::MANUAL_BITS) {
-            return;
-        }
-
         if let ExprKind::Binary(bin_op, left_expr, right_expr) = expr.kind
             && let BinOpKind::Mul = &bin_op.node
             && !in_external_macro(cx.sess(), expr.span)
+            && self.msrv.meets(msrvs::MANUAL_BITS)
             && let ctxt = expr.span.ctxt()
             && left_expr.span.ctxt() == ctxt
             && right_expr.span.ctxt() == ctxt
diff --git a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
index 89eea0b4456..03416ba96de 100644
--- a/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_float_methods.rs
@@ -82,29 +82,26 @@ impl Variant {
 
 impl<'tcx> LateLintPass<'tcx> for ManualFloatMethods {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        if !in_external_macro(cx.sess(), expr.span)
-            && (
-                matches!(cx.tcx.constness(cx.tcx.hir().enclosing_body_owner(expr.hir_id)), Constness::NotConst)
-                    || cx.tcx.features().declared(sym!(const_float_classify))
-            ) && let ExprKind::Binary(kind, lhs, rhs) = expr.kind
+        if let ExprKind::Binary(kind, lhs, rhs) = expr.kind
             && let ExprKind::Binary(lhs_kind, lhs_lhs, lhs_rhs) = lhs.kind
             && let ExprKind::Binary(rhs_kind, rhs_lhs, rhs_rhs) = rhs.kind
             // Checking all possible scenarios using a function would be a hopeless task, as we have
             // 16 possible alignments of constants/operands. For now, let's use `partition`.
-            && let (operands, constants) = [lhs_lhs, lhs_rhs, rhs_lhs, rhs_rhs]
-                .into_iter()
-                .partition::<Vec<&Expr<'_>>, _>(|i| path_to_local(i).is_some())
-            && let [first, second] = &*operands
-            && let Some([const_1, const_2]) = constants
-                .into_iter()
-                .map(|i| constant(cx, cx.typeck_results(), i))
-                .collect::<Option<Vec<_>>>()
-                .as_deref()
+            && let mut exprs = [lhs_lhs, lhs_rhs, rhs_lhs, rhs_rhs]
+            && exprs.iter_mut().partition_in_place(|i| path_to_local(i).is_some()) == 2
+            && !in_external_macro(cx.sess(), expr.span)
+            && (
+                matches!(cx.tcx.constness(cx.tcx.hir().enclosing_body_owner(expr.hir_id)), Constness::NotConst)
+                    || cx.tcx.features().declared(sym!(const_float_classify))
+            )
+            && let [first, second, const_1, const_2] = exprs
+            && let Some(const_1) = constant(cx, cx.typeck_results(), const_1)
+            && let Some(const_2) = constant(cx, cx.typeck_results(), const_2)
             && path_to_local(first).is_some_and(|f| path_to_local(second).is_some_and(|s| f == s))
             // The actual infinity check, we also allow `NEG_INFINITY` before` INFINITY` just in
             // case somebody does that for some reason
-            && (is_infinity(const_1) && is_neg_infinity(const_2)
-                || is_neg_infinity(const_1) && is_infinity(const_2))
+            && (is_infinity(&const_1) && is_neg_infinity(&const_2)
+                || is_neg_infinity(&const_1) && is_infinity(&const_2))
             && let Some(local_snippet) = snippet_opt(cx, first.span)
         {
             let variant = match (kind.node, lhs_kind.node, rhs_kind.node) {
diff --git a/src/tools/clippy/clippy_lints/src/manual_let_else.rs b/src/tools/clippy/clippy_lints/src/manual_let_else.rs
index 03e4d668dd8..ebebdf679a8 100644
--- a/src/tools/clippy/clippy_lints/src/manual_let_else.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_let_else.rs
@@ -49,16 +49,14 @@ declare_clippy_lint! {
 
 impl<'tcx> QuestionMark {
     pub(crate) fn check_manual_let_else(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'tcx>) {
-        if !self.msrv.meets(msrvs::LET_ELSE) || in_external_macro(cx.sess(), stmt.span) {
-            return;
-        }
-
         if let StmtKind::Let(local) = stmt.kind
             && let Some(init) = local.init
             && local.els.is_none()
             && local.ty.is_none()
             && init.span.eq_ctxt(stmt.span)
             && let Some(if_let_or_match) = IfLetOrMatch::parse(cx, init)
+            && self.msrv.meets(msrvs::LET_ELSE)
+            && !in_external_macro(cx.sess(), stmt.span)
         {
             match if_let_or_match {
                 IfLetOrMatch::IfLet(if_let_expr, let_pat, if_then, if_else, ..) => {
diff --git a/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs b/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs
index 5732bdda7f2..8e8cdc3fb07 100644
--- a/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_main_separator_str.rs
@@ -47,13 +47,13 @@ impl_lint_pass!(ManualMainSeparatorStr => [MANUAL_MAIN_SEPARATOR_STR]);
 
 impl LateLintPass<'_> for ManualMainSeparatorStr {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
-        if self.msrv.meets(msrvs::PATH_MAIN_SEPARATOR_STR)
-            && let (target, _) = peel_hir_expr_refs(expr)
-            && is_trait_method(cx, target, sym::ToString)
-            && let ExprKind::MethodCall(path, receiver, &[], _) = target.kind
+        let (target, _) = peel_hir_expr_refs(expr);
+        if let ExprKind::MethodCall(path, receiver, &[], _) = target.kind
             && path.ident.name == sym::to_string
             && let ExprKind::Path(QPath::Resolved(None, path)) = receiver.kind
             && let Res::Def(DefKind::Const, receiver_def_id) = path.res
+            && is_trait_method(cx, target, sym::ToString)
+            && self.msrv.meets(msrvs::PATH_MAIN_SEPARATOR_STR)
             && match_def_path(cx, receiver_def_id, &paths::PATH_MAIN_SEPARATOR)
             && let ty::Ref(_, ty, Mutability::Not) = cx.typeck_results().expr_ty_adjusted(expr).kind()
             && ty.is_str()
diff --git a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
index d2ac0ad8363..73a505fd73f 100644
--- a/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_non_exhaustive.rs
@@ -97,19 +97,15 @@ impl_lint_pass!(ManualNonExhaustiveEnum => [MANUAL_NON_EXHAUSTIVE]);
 
 impl EarlyLintPass for ManualNonExhaustiveStruct {
     fn check_item(&mut self, cx: &EarlyContext<'_>, item: &ast::Item) {
-        if !self.msrv.meets(msrvs::NON_EXHAUSTIVE) {
-            return;
-        }
-
-        if let ast::ItemKind::Struct(variant_data, _) = &item.kind {
-            let (fields, delimiter) = match variant_data {
+        if let ast::ItemKind::Struct(variant_data, _) = &item.kind
+            && let (fields, delimiter) = match variant_data {
                 ast::VariantData::Struct { fields, .. } => (&**fields, '{'),
                 ast::VariantData::Tuple(fields, _) => (&**fields, '('),
                 ast::VariantData::Unit(_) => return,
-            };
-            if fields.len() <= 1 {
-                return;
             }
+            && fields.len() > 1
+            && self.msrv.meets(msrvs::NON_EXHAUSTIVE)
+        {
             let mut iter = fields.iter().filter_map(|f| match f.vis.kind {
                 VisibilityKind::Public => None,
                 VisibilityKind::Inherited => Some(Ok(f)),
diff --git a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
index ec60de92c4b..07d4abbf5cd 100644
--- a/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_range_patterns.rs
@@ -76,14 +76,11 @@ impl Num {
 
 impl LateLintPass<'_> for ManualRangePatterns {
     fn check_pat(&mut self, cx: &LateContext<'_>, pat: &'_ rustc_hir::Pat<'_>) {
-        if in_external_macro(cx.sess(), pat.span) {
-            return;
-        }
-
         // a pattern like 1 | 2 seems fine, lint if there are at least 3 alternatives
         // or at least one range
         if let PatKind::Or(pats) = pat.kind
             && (pats.len() >= 3 || pats.iter().any(|p| matches!(p.kind, PatKind::Range(..))))
+            && !in_external_macro(cx.sess(), pat.span)
         {
             let mut min = Num::dummy(i128::MAX);
             let mut max = Num::dummy(i128::MIN);
diff --git a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
index ab9bca170cf..b518dc26937 100644
--- a/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_rem_euclid.rs
@@ -48,35 +48,30 @@ impl_lint_pass!(ManualRemEuclid => [MANUAL_REM_EUCLID]);
 
 impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !self.msrv.meets(msrvs::REM_EUCLID) {
-            return;
-        }
-
-        if in_constant(cx, expr.hir_id) && !self.msrv.meets(msrvs::REM_EUCLID_CONST) {
-            return;
-        }
-
-        if in_external_macro(cx.sess(), expr.span) {
-            return;
-        }
-
         // (x % c + c) % c
-        if let ExprKind::Binary(op1, expr1, right) = expr.kind
-            && op1.node == BinOpKind::Rem
+        if let ExprKind::Binary(rem_op, rem_lhs, rem_rhs) = expr.kind
+            && rem_op.node == BinOpKind::Rem
+            && let ExprKind::Binary(add_op, add_lhs, add_rhs) = rem_lhs.kind
+            && add_op.node == BinOpKind::Add
             && let ctxt = expr.span.ctxt()
-            && expr1.span.ctxt() == ctxt
-            && let Some(const1) = check_for_unsigned_int_constant(cx, right)
-            && let ExprKind::Binary(op2, left, right) = expr1.kind
-            && op2.node == BinOpKind::Add
-            && let Some((const2, expr2)) = check_for_either_unsigned_int_constant(cx, left, right)
-            && expr2.span.ctxt() == ctxt
-            && let ExprKind::Binary(op3, expr3, right) = expr2.kind
-            && op3.node == BinOpKind::Rem
-            && let Some(const3) = check_for_unsigned_int_constant(cx, right)
+            && rem_lhs.span.ctxt() == ctxt
+            && rem_rhs.span.ctxt() == ctxt
+            && add_lhs.span.ctxt() == ctxt
+            && add_rhs.span.ctxt() == ctxt
+            && !in_external_macro(cx.sess(), expr.span)
+            && self.msrv.meets(msrvs::REM_EUCLID)
+            && (self.msrv.meets(msrvs::REM_EUCLID_CONST) || !in_constant(cx, expr.hir_id))
+            && let Some(const1) = check_for_unsigned_int_constant(cx, rem_rhs)
+            && let Some((const2, add_other)) = check_for_either_unsigned_int_constant(cx, add_lhs, add_rhs)
+            && let ExprKind::Binary(rem2_op, rem2_lhs, rem2_rhs) = add_other.kind
+            && rem2_op.node == BinOpKind::Rem
+            && const1 == const2
+            && let Some(hir_id) = path_to_local(rem2_lhs)
+            && let Some(const3) = check_for_unsigned_int_constant(cx, rem2_rhs)
             // Also ensures the const is nonzero since zero can't be a divisor
-            && const1 == const2 && const2 == const3
-            && let Some(hir_id) = path_to_local(expr3)
-            && let Node::Pat(_) = cx.tcx.hir_node(hir_id)
+            && const2 == const3
+            && rem2_lhs.span.ctxt() == ctxt
+            && rem2_rhs.span.ctxt() == ctxt
         {
             // Apply only to params or locals with annotated types
             match cx.tcx.parent_hir_node(hir_id) {
@@ -91,7 +86,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualRemEuclid {
             };
 
             let mut app = Applicability::MachineApplicable;
-            let rem_of = snippet_with_context(cx, expr3.span, ctxt, "_", &mut app).0;
+            let rem_of = snippet_with_context(cx, rem2_lhs.span, ctxt, "_", &mut app).0;
             span_lint_and_sugg(
                 cx,
                 MANUAL_REM_EUCLID,
diff --git a/src/tools/clippy/clippy_lints/src/manual_retain.rs b/src/tools/clippy/clippy_lints/src/manual_retain.rs
index 3ddb06a1e08..8f7b8abd0c3 100644
--- a/src/tools/clippy/clippy_lints/src/manual_retain.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_retain.rs
@@ -70,9 +70,8 @@ impl_lint_pass!(ManualRetain => [MANUAL_RETAIN]);
 impl<'tcx> LateLintPass<'tcx> for ManualRetain {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) {
         if let Assign(left_expr, collect_expr, _) = &expr.kind
-            && let hir::ExprKind::MethodCall(seg, ..) = &collect_expr.kind
+            && let hir::ExprKind::MethodCall(seg, target_expr, [], _) = &collect_expr.kind
             && seg.args.is_none()
-            && let hir::ExprKind::MethodCall(_, target_expr, [], _) = &collect_expr.kind
             && let Some(collect_def_id) = cx.typeck_results().type_dependent_def_id(collect_expr.hir_id)
             && cx.tcx.is_diagnostic_item(sym::iterator_collect_fn, collect_def_id)
         {
diff --git a/src/tools/clippy/clippy_lints/src/manual_rotate.rs b/src/tools/clippy/clippy_lints/src/manual_rotate.rs
new file mode 100644
index 00000000000..a517a4d5075
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/manual_rotate.rs
@@ -0,0 +1,117 @@
+use std::fmt::Display;
+
+use clippy_utils::consts::{constant, Constant};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::sugg;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
+use rustc_session::declare_lint_pass;
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// It detects manual bit rotations that could be rewritten using standard
+    /// functions `rotate_left` or `rotate_right`.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// Calling the function better conveys the intent.
+    ///
+    /// ### Known issues
+    ///
+    /// Currently, the lint only catches shifts by constant amount.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// let x = 12345678_u32;
+    /// let _ = (x >> 8) | (x << 24);
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// let x = 12345678_u32;
+    /// let _ = x.rotate_right(8);
+    /// ```
+    #[clippy::version = "1.81.0"]
+    pub MANUAL_ROTATE,
+    style,
+    "using bit shifts to rotate integers"
+}
+
+declare_lint_pass!(ManualRotate => [MANUAL_ROTATE]);
+
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+enum ShiftDirection {
+    Left,
+    Right,
+}
+
+impl Display for ShiftDirection {
+    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
+        f.write_str(match self {
+            Self::Left => "rotate_left",
+            Self::Right => "rotate_right",
+        })
+    }
+}
+
+fn parse_shift<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+) -> Option<(ShiftDirection, u128, &'tcx Expr<'tcx>)> {
+    if let ExprKind::Binary(op, l, r) = expr.kind {
+        let dir = match op.node {
+            BinOpKind::Shl => ShiftDirection::Left,
+            BinOpKind::Shr => ShiftDirection::Right,
+            _ => return None,
+        };
+        let const_expr = constant(cx, cx.typeck_results(), r)?;
+        if let Constant::Int(shift) = const_expr {
+            return Some((dir, shift, l));
+        }
+    }
+    None
+}
+
+impl LateLintPass<'_> for ManualRotate {
+    fn check_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
+        if let ExprKind::Binary(op, l, r) = expr.kind
+            && let BinOpKind::Add | BinOpKind::BitOr = op.node
+            && let Some((l_shift_dir, l_amount, l_expr)) = parse_shift(cx, l)
+            && let Some((r_shift_dir, r_amount, r_expr)) = parse_shift(cx, r)
+        {
+            if l_shift_dir == r_shift_dir {
+                return;
+            }
+            if !clippy_utils::eq_expr_value(cx, l_expr, r_expr) {
+                return;
+            }
+            let Some(bit_width) = (match cx.typeck_results().expr_ty(expr).kind() {
+                ty::Int(itype) => itype.bit_width(),
+                ty::Uint(itype) => itype.bit_width(),
+                _ => return,
+            }) else {
+                return;
+            };
+            if l_amount + r_amount == u128::from(bit_width) {
+                let (shift_function, amount) = if l_amount < r_amount {
+                    (l_shift_dir, l_amount)
+                } else {
+                    (r_shift_dir, r_amount)
+                };
+                let mut applicability = Applicability::MachineApplicable;
+                let expr_sugg = sugg::Sugg::hir_with_applicability(cx, l_expr, "_", &mut applicability).maybe_par();
+                span_lint_and_sugg(
+                    cx,
+                    MANUAL_ROTATE,
+                    expr.span,
+                    "there is no need to manually implement bit rotation",
+                    "this expression can be rewritten as",
+                    format!("{expr_sugg}.{shift_function}({amount})"),
+                    Applicability::MachineApplicable,
+                );
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
index 1de686dbcb5..429ee2637c2 100644
--- a/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_slice_size_calculation.rs
@@ -40,11 +40,11 @@ declare_lint_pass!(ManualSliceSizeCalculation => [MANUAL_SLICE_SIZE_CALCULATION]
 
 impl<'tcx> LateLintPass<'tcx> for ManualSliceSizeCalculation {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        // Does not apply inside const because size_of_val is not cost in stable.
-        if !in_constant(cx, expr.hir_id)
-            && let ExprKind::Binary(ref op, left, right) = expr.kind
+        if let ExprKind::Binary(ref op, left, right) = expr.kind
             && BinOpKind::Mul == op.node
             && !expr.span.from_expansion()
+            // Does not apply inside const because size_of_val is not cost in stable.
+            && !in_constant(cx, expr.hir_id)
             && let Some(receiver) = simplify(cx, left, right)
         {
             let ctxt = expr.span.ctxt();
diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs
index 45af9f07718..6a523ad1564 100644
--- a/src/tools/clippy/clippy_lints/src/manual_strip.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs
@@ -66,14 +66,11 @@ enum StripKind {
 
 impl<'tcx> LateLintPass<'tcx> for ManualStrip {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if !self.msrv.meets(msrvs::STR_STRIP_PREFIX) {
-            return;
-        }
-
         if let Some(higher::If { cond, then, .. }) = higher::If::hir(expr)
             && let ExprKind::MethodCall(_, target_arg, [pattern], _) = cond.kind
-            && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id)
             && let ExprKind::Path(target_path) = &target_arg.kind
+            && self.msrv.meets(msrvs::STR_STRIP_PREFIX)
+            && let Some(method_def_id) = cx.typeck_results().type_dependent_def_id(cond.hir_id)
         {
             let strip_kind = if match_def_path(cx, method_def_id, &paths::STR_STARTS_WITH) {
                 StripKind::Prefix
diff --git a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs
index 58b2ebebbf0..f1acc4b2174 100644
--- a/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_unwrap_or_default.rs
@@ -172,11 +172,10 @@ fn handle<'tcx>(cx: &LateContext<'tcx>, if_let_or_match: IfLetOrMatch<'tcx>, exp
 
 impl<'tcx> LateLintPass<'tcx> for ManualUnwrapOrDefault {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
-        if expr.span.from_expansion() || in_constant(cx, expr.hir_id) {
-            return;
-        }
-        // Call handle only if the expression is `if let` or `match`
-        if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, expr) {
+        if let Some(if_let_or_match) = IfLetOrMatch::parse(cx, expr)
+            && !expr.span.from_expansion()
+            && !in_constant(cx, expr.hir_id)
+        {
             handle(cx, if_let_or_match, expr);
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
index 9db04b615be..2db71b1f7a3 100644
--- a/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/map_unit_fn.rs
@@ -253,14 +253,11 @@ fn lint_map_unit_fn(
 
 impl<'tcx> LateLintPass<'tcx> for MapUnit {
     fn check_stmt(&mut self, cx: &LateContext<'_>, stmt: &hir::Stmt<'_>) {
-        if stmt.span.from_expansion() {
-            return;
-        }
-
-        if let hir::StmtKind::Semi(expr) = stmt.kind {
-            if let Some(arglists) = method_chain_args(expr, &["map"]) {
-                lint_map_unit_fn(cx, stmt, expr, arglists[0]);
-            }
+        if let hir::StmtKind::Semi(expr) = stmt.kind
+            && !stmt.span.from_expansion()
+            && let Some(arglists) = method_chain_args(expr, &["map"])
+        {
+            lint_map_unit_fn(cx, stmt, expr, arglists[0]);
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/matches/mod.rs b/src/tools/clippy/clippy_lints/src/matches/mod.rs
index bf7156cc53e..22a299ae3d8 100644
--- a/src/tools/clippy/clippy_lints/src/matches/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/mod.rs
@@ -1019,6 +1019,7 @@ impl_lint_pass!(Matches => [
 ]);
 
 impl<'tcx> LateLintPass<'tcx> for Matches {
+    #[expect(clippy::too_many_lines)]
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if is_direct_expn_of(expr.span, "matches").is_none() && in_external_macro(cx.sess(), expr.span) {
             return;
@@ -1037,7 +1038,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
                 return;
             }
             if matches!(source, MatchSource::Normal | MatchSource::ForLoopDesugar) {
-                significant_drop_in_scrutinee::check(cx, expr, ex, arms, source);
+                significant_drop_in_scrutinee::check_match(cx, expr, ex, arms, source);
             }
 
             collapsible_match::check_match(cx, arms, &self.msrv);
@@ -1084,6 +1085,7 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
             }
         } else if let Some(if_let) = higher::IfLet::hir(cx, expr) {
             collapsible_match::check_if_let(cx, if_let.let_pat, if_let.if_then, if_let.if_else, &self.msrv);
+            significant_drop_in_scrutinee::check_if_let(cx, expr, if_let.let_expr, if_let.if_then, if_let.if_else);
             if !from_expansion {
                 if let Some(else_expr) = if_let.if_else {
                     if self.msrv.meets(msrvs::MATCHES_MACRO) {
@@ -1126,8 +1128,13 @@ impl<'tcx> LateLintPass<'tcx> for Matches {
                 );
                 needless_match::check_if_let(cx, expr, &if_let);
             }
-        } else if !from_expansion {
-            redundant_pattern_match::check(cx, expr);
+        } else {
+            if let Some(while_let) = higher::WhileLet::hir(expr) {
+                significant_drop_in_scrutinee::check_while_let(cx, expr, while_let.let_expr, while_let.if_then);
+            }
+            if !from_expansion {
+                redundant_pattern_match::check(cx, expr);
+            }
         }
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
index 2f72e59834f..9047c9627d9 100644
--- a/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/significant_drop_in_scrutinee.rs
@@ -16,7 +16,7 @@ use rustc_span::Span;
 
 use super::SIGNIFICANT_DROP_IN_SCRUTINEE;
 
-pub(super) fn check<'tcx>(
+pub(super) fn check_match<'tcx>(
     cx: &LateContext<'tcx>,
     expr: &'tcx Expr<'tcx>,
     scrutinee: &'tcx Expr<'_>,
@@ -27,10 +27,89 @@ pub(super) fn check<'tcx>(
         return;
     }
 
-    let (suggestions, message) = has_significant_drop_in_scrutinee(cx, scrutinee, source);
+    let scrutinee = match (source, &scrutinee.kind) {
+        (MatchSource::ForLoopDesugar, ExprKind::Call(_, [e])) => e,
+        _ => scrutinee,
+    };
+
+    let message = if source == MatchSource::Normal {
+        "temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression"
+    } else {
+        "temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression"
+    };
+
+    let arms = arms.iter().map(|arm| arm.body).collect::<Vec<_>>();
+
+    check(cx, expr, scrutinee, &arms, message, Suggestion::Emit);
+}
+
+pub(super) fn check_if_let<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    scrutinee: &'tcx Expr<'_>,
+    if_then: &'tcx Expr<'_>,
+    if_else: Option<&'tcx Expr<'_>>,
+) {
+    if is_lint_allowed(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, expr.hir_id) {
+        return;
+    }
+
+    let message =
+        "temporary with significant `Drop` in `if let` scrutinee will live until the end of the `if let` expression";
+
+    if let Some(if_else) = if_else {
+        check(cx, expr, scrutinee, &[if_then, if_else], message, Suggestion::Emit);
+    } else {
+        check(cx, expr, scrutinee, &[if_then], message, Suggestion::Emit);
+    }
+}
+
+pub(super) fn check_while_let<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    scrutinee: &'tcx Expr<'_>,
+    body: &'tcx Expr<'_>,
+) {
+    if is_lint_allowed(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, expr.hir_id) {
+        return;
+    }
+
+    check(
+        cx,
+        expr,
+        scrutinee,
+        &[body],
+        "temporary with significant `Drop` in `while let` scrutinee will live until the end of the `while let` expression",
+        // Don't emit wrong suggestions: We cannot fix the significant drop in the `while let` scrutinee by simply
+        // moving it out. We need to change the `while` to a `loop` instead.
+        Suggestion::DontEmit,
+    );
+}
+
+#[derive(Copy, Clone, Debug)]
+enum Suggestion {
+    Emit,
+    DontEmit,
+}
+
+fn check<'tcx>(
+    cx: &LateContext<'tcx>,
+    expr: &'tcx Expr<'tcx>,
+    scrutinee: &'tcx Expr<'_>,
+    arms: &[&'tcx Expr<'_>],
+    message: &'static str,
+    sugg: Suggestion,
+) {
+    let mut helper = SigDropHelper::new(cx);
+    let suggestions = helper.find_sig_drop(scrutinee);
+
     for found in suggestions {
         span_lint_and_then(cx, SIGNIFICANT_DROP_IN_SCRUTINEE, found.found_span, message, |diag| {
-            set_diagnostic(diag, cx, expr, found);
+            match sugg {
+                Suggestion::Emit => set_suggestion(diag, cx, expr, found),
+                Suggestion::DontEmit => (),
+            }
+
             let s = Span::new(expr.span.hi(), expr.span.hi(), expr.span.ctxt(), None);
             diag.span_label(s, "temporary lives until here");
             for span in has_significant_drop_in_arms(cx, arms) {
@@ -41,7 +120,7 @@ pub(super) fn check<'tcx>(
     }
 }
 
-fn set_diagnostic<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, found: FoundSigDrop) {
+fn set_suggestion<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, found: FoundSigDrop) {
     let original = snippet(cx, found.found_span, "..");
     let trailing_indent = " ".repeat(indent_of(cx, found.found_span).unwrap_or(0));
 
@@ -79,26 +158,6 @@ fn set_diagnostic<'tcx>(diag: &mut Diag<'_, ()>, cx: &LateContext<'tcx>, expr: &
     );
 }
 
-/// If the expression is an `ExprKind::Match`, check if the scrutinee has a significant drop that
-/// may have a surprising lifetime.
-fn has_significant_drop_in_scrutinee<'tcx>(
-    cx: &LateContext<'tcx>,
-    scrutinee: &'tcx Expr<'tcx>,
-    source: MatchSource,
-) -> (Vec<FoundSigDrop>, &'static str) {
-    let mut helper = SigDropHelper::new(cx);
-    let scrutinee = match (source, &scrutinee.kind) {
-        (MatchSource::ForLoopDesugar, ExprKind::Call(_, [e])) => e,
-        _ => scrutinee,
-    };
-    let message = if source == MatchSource::Normal {
-        "temporary with significant `Drop` in `match` scrutinee will live until the end of the `match` expression"
-    } else {
-        "temporary with significant `Drop` in `for` loop condition will live until the end of the `for` expression"
-    };
-    (helper.find_sig_drop(scrutinee), message)
-}
-
 struct SigDropChecker<'a, 'tcx> {
     seen_types: FxHashSet<Ty<'tcx>>,
     cx: &'a LateContext<'tcx>,
@@ -428,10 +487,10 @@ impl<'a, 'tcx> ArmSigDropHelper<'a, 'tcx> {
     }
 }
 
-fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'_>]) -> FxHashSet<Span> {
+fn has_significant_drop_in_arms<'tcx>(cx: &LateContext<'tcx>, arms: &[&'tcx Expr<'_>]) -> FxHashSet<Span> {
     let mut helper = ArmSigDropHelper::new(cx);
     for arm in arms {
-        helper.visit_expr(arm.body);
+        helper.visit_expr(arm);
     }
     helper.found_sig_drop_spans
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs
index e3ce64c246a..cac2e11f591 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs
@@ -167,14 +167,12 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
             } else {
                 edits.extend(addr_of_edits);
             }
-            edits.push((
-                name_span,
-                String::from(match name {
-                    "map" => "inspect",
-                    "map_err" => "inspect_err",
-                    _ => return,
-                }),
-            ));
+            let edit = match name {
+                "map" => "inspect",
+                "map_err" => "inspect_err",
+                _ => return,
+            };
+            edits.push((name_span, edit.to_string()));
             edits.push((
                 final_expr
                     .span
@@ -187,9 +185,15 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
             } else {
                 Applicability::MachineApplicable
             };
-            span_lint_and_then(cx, MANUAL_INSPECT, name_span, "", |diag| {
-                diag.multipart_suggestion("try", edits, app);
-            });
+            span_lint_and_then(
+                cx,
+                MANUAL_INSPECT,
+                name_span,
+                format!("using `{name}` over `{edit}`"),
+                |diag| {
+                    diag.multipart_suggestion("try", edits, app);
+                },
+            );
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 1408f454820..a846552cddf 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -628,12 +628,11 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))` or
-    /// `_.or_else(|x| Err(y))`.
+    /// Checks for usage of `_.and_then(|x| Some(y))`, `_.and_then(|x| Ok(y))`
+    /// or `_.or_else(|x| Err(y))`.
     ///
     /// ### Why is this bad?
-    /// Readability, this can be written more concisely as
-    /// `_.map(|x| y)` or `_.map_err(|x| y)`.
+    /// This can be written more concisely as `_.map(|x| y)` or `_.map_err(|x| y)`.
     ///
     /// ### Example
     /// ```no_run
@@ -4121,7 +4120,7 @@ declare_clippy_lint! {
     /// ```no_run
     /// let x = Some(0).inspect(|x| println!("{x}"));
     /// ```
-    #[clippy::version = "1.78.0"]
+    #[clippy::version = "1.81.0"]
     pub MANUAL_INSPECT,
     complexity,
     "use of `map` returning the original item"
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
index ae9aa83efd6..5d899415d77 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs
@@ -1,13 +1,15 @@
 use super::implicit_clone::is_clone_like;
 use super::unnecessary_iter_cloned::{self, is_into_iter};
 use clippy_config::msrvs::{self, Msrv};
-use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::source::snippet_opt;
+use clippy_utils::diagnostics::{span_lint_and_sugg, span_lint_and_then};
+use clippy_utils::source::{snippet, snippet_opt};
 use clippy_utils::ty::{
     get_iterator_item_ty, implements_trait, is_copy, is_type_diagnostic_item, is_type_lang_item, peel_mid_ty_refs,
 };
 use clippy_utils::visitors::find_all_ret_expressions;
-use clippy_utils::{fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, return_ty};
+use clippy_utils::{
+    fn_def_id, get_parent_expr, is_diag_item_method, is_diag_trait_item, match_def_path, paths, return_ty,
+};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::def_id::DefId;
@@ -52,6 +54,9 @@ pub fn check<'tcx>(
             if check_into_iter_call_arg(cx, expr, method_name, receiver, msrv) {
                 return;
             }
+            if check_string_from_utf8(cx, expr, receiver) {
+                return;
+            }
             check_other_call_arg(cx, expr, method_name, receiver);
         }
     } else {
@@ -240,6 +245,65 @@ fn check_into_iter_call_arg(
     false
 }
 
+/// Checks for `&String::from_utf8(bytes.{to_vec,to_owned,...}()).unwrap()` coercing to `&str`,
+/// which can be written as just `std::str::from_utf8(bytes).unwrap()`.
+fn check_string_from_utf8<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>, receiver: &'tcx Expr<'tcx>) -> bool {
+    if let Some((call, arg)) = skip_addr_of_ancestors(cx, expr)
+        && !arg.span.from_expansion()
+        && let ExprKind::Call(callee, _) = call.kind
+        && fn_def_id(cx, call).is_some_and(|did| match_def_path(cx, did, &paths::STRING_FROM_UTF8))
+        && let Some(unwrap_call) = get_parent_expr(cx, call)
+        && let ExprKind::MethodCall(unwrap_method_name, ..) = unwrap_call.kind
+        && matches!(unwrap_method_name.ident.name, sym::unwrap | sym::expect)
+        && let Some(ref_string) = get_parent_expr(cx, unwrap_call)
+        && let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, _) = ref_string.kind
+        && let adjusted_ty = cx.typeck_results().expr_ty_adjusted(ref_string)
+        // `&...` creates a `&String`, so only actually lint if this coerces to a `&str`
+        && matches!(adjusted_ty.kind(), ty::Ref(_, ty, _) if ty.is_str())
+    {
+        span_lint_and_then(
+            cx,
+            UNNECESSARY_TO_OWNED,
+            ref_string.span,
+            "allocating a new `String` only to create a temporary `&str` from it",
+            |diag| {
+                let arg_suggestion = format!(
+                    "{borrow}{recv_snippet}",
+                    recv_snippet = snippet(cx, receiver.span.source_callsite(), ".."),
+                    borrow = if cx.typeck_results().expr_ty(receiver).is_ref() {
+                        ""
+                    } else {
+                        // If not already a reference, prefix with a borrow so that it can coerce to one
+                        "&"
+                    }
+                );
+
+                diag.multipart_suggestion(
+                    "convert from `&[u8]` to `&str` directly",
+                    vec![
+                        // `&String::from_utf8(bytes.to_vec()).unwrap()`
+                        //   ^^^^^^^^^^^^^^^^^
+                        (callee.span, "core::str::from_utf8".into()),
+                        // `&String::from_utf8(bytes.to_vec()).unwrap()`
+                        //  ^
+                        (
+                            ref_string.span.shrink_to_lo().to(unwrap_call.span.shrink_to_lo()),
+                            String::new(),
+                        ),
+                        // `&String::from_utf8(bytes.to_vec()).unwrap()`
+                        //                     ^^^^^^^^^^^^^^
+                        (arg.span, arg_suggestion),
+                    ],
+                    Applicability::MachineApplicable,
+                );
+            },
+        );
+        true
+    } else {
+        false
+    }
+}
+
 /// Checks whether `expr` is an argument in an `into_iter` call and, if so, determines whether its
 /// call of a `to_owned`-like function is unnecessary.
 fn check_split_call_arg(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol, receiver: &Expr<'_>) -> bool {
diff --git a/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs b/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs
index 516b8984ad7..5b0bd0f716a 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unwrap_expect_used.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::ty::{is_never_like, is_type_diagnostic_item};
-use clippy_utils::{is_in_cfg_test, is_in_test_function, is_lint_allowed};
+use clippy_utils::{is_in_test, is_lint_allowed};
 use rustc_hir::Expr;
 use rustc_lint::{LateContext, Lint};
 use rustc_middle::ty;
@@ -61,7 +61,7 @@ pub(super) fn check(
 
     let method_suffix = if is_err { "_err" } else { "" };
 
-    if allow_unwrap_in_tests && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id)) {
+    if allow_unwrap_in_tests && is_in_test(cx.tcx, expr.hir_id) {
         return;
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/minmax.rs b/src/tools/clippy/clippy_lints/src/minmax.rs
index fca626fa5c3..c3fbca1d560 100644
--- a/src/tools/clippy/clippy_lints/src/minmax.rs
+++ b/src/tools/clippy/clippy_lints/src/minmax.rs
@@ -5,7 +5,7 @@ use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::sym;
-use std::cmp::Ordering;
+use std::cmp::Ordering::{Equal, Greater, Less};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -36,26 +36,21 @@ declare_lint_pass!(MinMaxPass => [MIN_MAX]);
 
 impl<'tcx> LateLintPass<'tcx> for MinMaxPass {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if let Some((outer_max, outer_c, oe)) = min_max(cx, expr) {
-            if let Some((inner_max, inner_c, ie)) = min_max(cx, oe) {
-                if outer_max == inner_max {
-                    return;
-                }
-                match (
-                    outer_max,
-                    Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(ie), &outer_c, &inner_c),
-                ) {
-                    (_, None) | (MinMax::Max, Some(Ordering::Less)) | (MinMax::Min, Some(Ordering::Greater)) => (),
-                    _ => {
-                        span_lint(
-                            cx,
-                            MIN_MAX,
-                            expr.span,
-                            "this `min`/`max` combination leads to constant result",
-                        );
-                    },
-                }
-            }
+        if let Some((outer_max, outer_c, oe)) = min_max(cx, expr)
+            && let Some((inner_max, inner_c, ie)) = min_max(cx, oe)
+            && outer_max != inner_max
+            && let Some(ord) = Constant::partial_cmp(cx.tcx, cx.typeck_results().expr_ty(ie), &outer_c, &inner_c)
+            && matches!(
+                (outer_max, ord),
+                (MinMax::Max, Equal | Greater) | (MinMax::Min, Equal | Less)
+            )
+        {
+            span_lint(
+                cx,
+                MIN_MAX,
+                expr.span,
+                "this `min`/`max` combination leads to constant result",
+            );
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/misc.rs b/src/tools/clippy/clippy_lints/src/misc.rs
index f3f9bf11a61..32c8731c537 100644
--- a/src/tools/clippy/clippy_lints/src/misc.rs
+++ b/src/tools/clippy/clippy_lints/src/misc.rs
@@ -2,8 +2,8 @@ use clippy_utils::diagnostics::{span_lint, span_lint_and_then, span_lint_hir_and
 use clippy_utils::source::{snippet, snippet_with_context};
 use clippy_utils::sugg::Sugg;
 use clippy_utils::{
-    any_parent_is_automatically_derived, fulfill_or_allowed, get_parent_expr, is_lint_allowed, iter_input_pats,
-    last_path_segment, SpanlessEq,
+    fulfill_or_allowed, get_parent_expr, in_automatically_derived, is_lint_allowed, iter_input_pats, last_path_segment,
+    SpanlessEq,
 };
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
@@ -206,7 +206,7 @@ impl<'tcx> LateLintPass<'tcx> for LintPass {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         if in_external_macro(cx.sess(), expr.span)
             || expr.span.desugaring_kind().is_some()
-            || any_parent_is_automatically_derived(cx.tcx, expr.hir_id)
+            || in_automatically_derived(cx.tcx, expr.hir_id)
         {
             return;
         }
diff --git a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
index fedcfd11fdc..4cba13a05c2 100644
--- a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
@@ -364,8 +364,8 @@ declare_lint_pass!(MiscEarlyLints => [
 ]);
 
 impl EarlyLintPass for MiscEarlyLints {
-    fn check_generics(&mut self, cx: &EarlyContext<'_>, gen: &Generics) {
-        for param in &gen.params {
+    fn check_generics(&mut self, cx: &EarlyContext<'_>, generics: &Generics) {
+        for param in &generics.params {
             builtin_type_shadow::check(cx, param);
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
index dd98352da86..935ed48dacc 100644
--- a/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_assert_message.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_and_help;
+use clippy_utils::is_in_test;
 use clippy_utils::macros::{find_assert_args, find_assert_eq_args, root_macro_call_first_node, PanicExpn};
-use clippy_utils::{is_in_cfg_test, is_in_test_function};
 use rustc_hir::Expr;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
@@ -62,7 +62,7 @@ impl<'tcx> LateLintPass<'tcx> for MissingAssertMessage {
         };
 
         // This lint would be very noisy in tests, so just ignore if we're in test context
-        if is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id) {
+        if is_in_test(cx.tcx, expr.hir_id) {
             return;
         }
 
diff --git a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
index bb0d714a31f..ed5f46ddc8d 100644
--- a/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_const_for_fn.rs
@@ -8,9 +8,11 @@ use rustc_hir::intravisit::FnKind;
 use rustc_hir::{self as hir, Body, Constness, FnDecl, GenericParamKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty;
 use rustc_session::impl_lint_pass;
 use rustc_span::def_id::LocalDefId;
 use rustc_span::Span;
+use rustc_target::spec::abi::Abi;
 
 declare_clippy_lint! {
     /// ### What it does
@@ -115,7 +117,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
                     .iter()
                     .any(|param| matches!(param.kind, GenericParamKind::Const { .. }));
 
-                if already_const(header) || has_const_generic_params {
+                if already_const(header)
+                    || has_const_generic_params
+                    || !could_be_const_with_abi(cx, &self.msrv, header.abi)
+                {
                     return;
                 }
             },
@@ -127,6 +132,10 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
             FnKind::Closure => return,
         }
 
+        if fn_inputs_has_impl_trait_ty(cx, def_id) {
+            return;
+        }
+
         let hir_id = cx.tcx.local_def_id_to_hir_id(def_id);
 
         // Const fns are not allowed as methods in a trait.
@@ -171,3 +180,25 @@ impl<'tcx> LateLintPass<'tcx> for MissingConstForFn {
 fn already_const(header: hir::FnHeader) -> bool {
     header.constness == Constness::Const
 }
+
+fn could_be_const_with_abi(cx: &LateContext<'_>, msrv: &Msrv, abi: Abi) -> bool {
+    match abi {
+        Abi::Rust => true,
+        // `const extern "C"` was stablized after 1.62.0
+        Abi::C { unwind: false } => msrv.meets(msrvs::CONST_EXTERN_FN),
+        // Rest ABIs are still unstable and need the `const_extern_fn` feature enabled.
+        _ => cx.tcx.features().const_extern_fn,
+    }
+}
+
+/// Return `true` when the given `def_id` is a function that has `impl Trait` ty as one of
+/// its parameter types.
+fn fn_inputs_has_impl_trait_ty(cx: &LateContext<'_>, def_id: LocalDefId) -> bool {
+    let inputs = cx.tcx.fn_sig(def_id).instantiate_identity().inputs().skip_binder();
+    inputs.iter().any(|input| {
+        matches!(
+            input.kind(),
+            ty::Alias(ty::AliasTyKind::Weak, alias_ty) if cx.tcx.type_of(alias_ty.def_id).skip_binder().is_impl_trait()
+        )
+    })
+}
diff --git a/src/tools/clippy/clippy_lints/src/thread_local_initializer_can_be_made_const.rs b/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs
index 4af3ee74d0e..ab1b4aa3dee 100644
--- a/src/tools/clippy/clippy_lints/src/thread_local_initializer_can_be_made_const.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_const_for_thread_local.rs
@@ -39,23 +39,23 @@ declare_clippy_lint! {
     /// }
     /// ```
     #[clippy::version = "1.77.0"]
-    pub THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST,
+    pub MISSING_CONST_FOR_THREAD_LOCAL,
     perf,
     "suggest using `const` in `thread_local!` macro"
 }
 
-pub struct ThreadLocalInitializerCanBeMadeConst {
+pub struct MissingConstForThreadLocal {
     msrv: Msrv,
 }
 
-impl ThreadLocalInitializerCanBeMadeConst {
+impl MissingConstForThreadLocal {
     #[must_use]
     pub fn new(msrv: Msrv) -> Self {
         Self { msrv }
     }
 }
 
-impl_lint_pass!(ThreadLocalInitializerCanBeMadeConst => [THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST]);
+impl_lint_pass!(MissingConstForThreadLocal => [MISSING_CONST_FOR_THREAD_LOCAL]);
 
 #[inline]
 fn is_thread_local_initializer(
@@ -102,7 +102,7 @@ fn initializer_can_be_made_const(cx: &LateContext<'_>, defid: rustc_span::def_id
     false
 }
 
-impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst {
+impl<'tcx> LateLintPass<'tcx> for MissingConstForThreadLocal {
     fn check_fn(
         &mut self,
         cx: &LateContext<'tcx>,
@@ -113,7 +113,7 @@ impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst {
         local_defid: rustc_span::def_id::LocalDefId,
     ) {
         let defid = local_defid.to_def_id();
-        if self.msrv.meets(msrvs::THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST)
+        if self.msrv.meets(msrvs::THREAD_LOCAL_CONST_INIT)
             && is_thread_local_initializer(cx, fn_kind, span).unwrap_or(false)
             // Some implementations of `thread_local!` include an initializer fn.
             // In the case of a const initializer, the init fn is also const,
@@ -139,7 +139,7 @@ impl<'tcx> LateLintPass<'tcx> for ThreadLocalInitializerCanBeMadeConst {
         {
             span_lint_and_sugg(
                 cx,
-                THREAD_LOCAL_INITIALIZER_CAN_BE_MADE_CONST,
+                MISSING_CONST_FOR_THREAD_LOCAL,
                 unpeeled.span,
                 "initializer for `thread_local` value can be made `const`",
                 "replace with",
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
index 57ba0da5331..5ffd41d78e0 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs
@@ -27,7 +27,7 @@ declare_clippy_lint! {
     /// Check if a `&mut` function argument is actually used mutably.
     ///
     /// Be careful if the function is publicly reexported as it would break compatibility with
-    /// users of this function.
+    /// users of this function, when the users pass this function as an argument.
     ///
     /// ### Why is this bad?
     /// Less `mut` means less fights with the borrow checker. It can also lead to more
@@ -138,6 +138,10 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
             return;
         }
 
+        if self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(fn_def_id) {
+            return;
+        }
+
         let hir_id = cx.tcx.local_def_id_to_hir_id(fn_def_id);
         let is_async = match kind {
             FnKind::ItemFn(.., header) => {
@@ -262,9 +266,6 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
             .iter()
             .filter(|(def_id, _)| !self.used_fn_def_ids.contains(def_id))
         {
-            let show_semver_warning =
-                self.avoid_breaking_exported_api && cx.effective_visibilities.is_exported(*fn_def_id);
-
             let mut is_cfged = None;
             for input in unused {
                 // If the argument is never used mutably, we emit the warning.
@@ -284,7 +285,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
                                 format!("&{}", snippet(cx, cx.tcx.hir().span(inner_ty.ty.hir_id), "_"),),
                                 Applicability::Unspecified,
                             );
-                            if show_semver_warning {
+                            if cx.effective_visibilities.is_exported(*fn_def_id) {
                                 diag.warn("changing this function will impact semver compatibility");
                             }
                             if *is_cfged {
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index 87f886b1128..0ecfa7baa72 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -1,7 +1,9 @@
 use clippy_utils::diagnostics::{span_lint_hir, span_lint_hir_and_then};
 use clippy_utils::source::snippet_opt;
 use clippy_utils::ty::has_drop;
-use clippy_utils::{any_parent_is_automatically_derived, is_lint_allowed, path_to_local, peel_blocks};
+use clippy_utils::{
+    in_automatically_derived, is_inside_always_const_context, is_lint_allowed, path_to_local, peel_blocks,
+};
 use rustc_errors::Applicability;
 use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{
@@ -185,7 +187,7 @@ impl NoEffect {
                 && has_no_effect(cx, init)
                 && let PatKind::Binding(_, hir_id, ident, _) = local.pat.kind
                 && ident.name.to_ident_string().starts_with('_')
-                && !any_parent_is_automatically_derived(cx.tcx, local.hir_id)
+                && !in_automatically_derived(cx.tcx, local.hir_id)
             {
                 if let Some(l) = self.local_bindings.last_mut() {
                     l.push(hir_id);
@@ -258,13 +260,16 @@ fn has_no_effect(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 
 fn check_unnecessary_operation(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
     if let StmtKind::Semi(expr) = stmt.kind
+        && !in_external_macro(cx.sess(), stmt.span)
         && let ctxt = stmt.span.ctxt()
         && expr.span.ctxt() == ctxt
         && let Some(reduced) = reduce_expression(cx, expr)
-        && !in_external_macro(cx.sess(), stmt.span)
         && reduced.iter().all(|e| e.span.ctxt() == ctxt)
     {
         if let ExprKind::Index(..) = &expr.kind {
+            if is_inside_always_const_context(cx.tcx, expr.hir_id) {
+                return;
+            }
             let snippet =
                 if let (Some(arr), Some(func)) = (snippet_opt(cx, reduced[0].span), snippet_opt(cx, reduced[1].span)) {
                     format!("assert!({}.len() > {});", &arr, &func)
diff --git a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs
index faab79de9d3..0e5b440c50f 100644
--- a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs
@@ -57,7 +57,6 @@ pub(crate) fn check<'tcx>(
                     Applicability::HasPlaceholders, // snippet
                 );
             }
-            diag.note("`f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`");
         });
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/operators/mod.rs b/src/tools/clippy/clippy_lints/src/operators/mod.rs
index 48a442705b2..59834781a58 100644
--- a/src/tools/clippy/clippy_lints/src/operators/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/mod.rs
@@ -242,6 +242,13 @@ declare_clippy_lint! {
     /// # let x = 1;
     /// if (x | 1 > 3) {  }
     /// ```
+    ///
+    /// Use instead:
+    ///
+    /// ```no_run
+    /// # let x = 1;
+    /// if (x >= 2) {  }
+    /// ```
     #[clippy::version = "pre 1.29.0"]
     pub INEFFECTIVE_BIT_MASK,
     correctness,
@@ -265,6 +272,13 @@ declare_clippy_lint! {
     /// # let x = 1;
     /// if x & 0b1111 == 0 { }
     /// ```
+    ///
+    /// Use instead:
+    ///
+    /// ```no_run
+    /// # let x: i32 = 1;
+    /// if x.trailing_zeros() > 4 { }
+    /// ```
     #[clippy::version = "pre 1.29.0"]
     pub VERBOSE_BIT_MASK,
     pedantic,
@@ -560,74 +574,128 @@ declare_clippy_lint! {
     /// implement equality for a type involving floats).
     ///
     /// ### Why is this bad?
-    /// Floating point calculations are usually imprecise, so
-    /// asking if two values are *exactly* equal is asking for trouble. For a good
-    /// guide on what to do, see [the floating point
-    /// guide](http://www.floating-point-gui.de/errors/comparison).
+    /// Floating point calculations are usually imprecise, so asking if two values are *exactly*
+    /// equal is asking for trouble because arriving at the same logical result via different
+    /// routes (e.g. calculation versus constant) may yield different values.
     ///
     /// ### Example
+    ///
     /// ```no_run
-    /// let x = 1.2331f64;
-    /// let y = 1.2332f64;
+    /// let a: f64 = 1000.1;
+    /// let b: f64 = 0.2;
+    /// let x = a + b;
+    /// let y = 1000.3; // Expected value.
     ///
-    /// if y == 1.23f64 { }
-    /// if y != x {} // where both are floats
+    /// // Actual value: 1000.3000000000001
+    /// println!("{x}");
+    ///
+    /// let are_equal = x == y;
+    /// println!("{are_equal}"); // false
     /// ```
     ///
-    /// Use instead:
+    /// The correct way to compare floating point numbers is to define an allowed error margin. This
+    /// may be challenging if there is no "natural" error margin to permit. Broadly speaking, there
+    /// are two cases:
+    ///
+    /// 1. If your values are in a known range and you can define a threshold for "close enough to
+    ///    be equal", it may be appropriate to define an absolute error margin. For example, if your
+    ///    data is "length of vehicle in centimeters", you may consider 0.1 cm to be "close enough".
+    /// 1. If your code is more general and you do not know the range of values, you should use a
+    ///    relative error margin, accepting e.g. 0.1% of error regardless of specific values.
+    ///
+    /// For the scenario where you can define a meaningful absolute error margin, consider using:
+    ///
     /// ```no_run
-    /// # let x = 1.2331f64;
-    /// # let y = 1.2332f64;
-    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
-    /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
-    /// // let error_margin = std::f64::EPSILON;
-    /// if (y - 1.23f64).abs() < error_margin { }
-    /// if (y - x).abs() > error_margin { }
+    /// let a: f64 = 1000.1;
+    /// let b: f64 = 0.2;
+    /// let x = a + b;
+    /// let y = 1000.3; // Expected value.
+    ///
+    /// const ALLOWED_ERROR_VEHICLE_LENGTH_CM: f64 = 0.1;
+    /// let within_tolerance = (x - y).abs() < ALLOWED_ERROR_VEHICLE_LENGTH_CM;
+    /// println!("{within_tolerance}"); // true
     /// ```
+    ///
+    /// NB! Do not use `f64::EPSILON` - while the error margin is often called "epsilon", this is
+    /// a different use of the term that is not suitable for floating point equality comparison.
+    /// Indeed, for the example above using `f64::EPSILON` as the allowed error would return `false`.
+    ///
+    /// For the scenario where no meaningful absolute error can be defined, refer to
+    /// [the floating point guide](https://www.floating-point-gui.de/errors/comparison)
+    /// for a reference implementation of relative error based comparison of floating point values.
+    /// `MIN_NORMAL` in the reference implementation is equivalent to `MIN_POSITIVE` in Rust.
     #[clippy::version = "pre 1.29.0"]
     pub FLOAT_CMP,
     pedantic,
-    "using `==` or `!=` on float values instead of comparing difference with an epsilon"
+    "using `==` or `!=` on float values instead of comparing difference with an allowed error"
 }
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for (in-)equality comparisons on floating-point
-    /// value and constant, except in functions called `*eq*` (which probably
+    /// Checks for (in-)equality comparisons on constant floating-point
+    /// values (apart from zero), except in functions called `*eq*` (which probably
     /// implement equality for a type involving floats).
     ///
     /// ### Why restrict this?
-    /// Floating point calculations are usually imprecise, so
-    /// asking if two values are *exactly* equal is asking for trouble. For a good
-    /// guide on what to do, see [the floating point
-    /// guide](http://www.floating-point-gui.de/errors/comparison).
+    /// Floating point calculations are usually imprecise, so asking if two values are *exactly*
+    /// equal is asking for trouble because arriving at the same logical result via different
+    /// routes (e.g. calculation versus constant) may yield different values.
     ///
     /// ### Example
+    ///
     /// ```no_run
-    /// let x: f64 = 1.0;
-    /// const ONE: f64 = 1.00;
+    /// let a: f64 = 1000.1;
+    /// let b: f64 = 0.2;
+    /// let x = a + b;
+    /// const Y: f64 = 1000.3; // Expected value.
+    ///
+    /// // Actual value: 1000.3000000000001
+    /// println!("{x}");
     ///
-    /// if x == ONE { } // where both are floats
+    /// let are_equal = x == Y;
+    /// println!("{are_equal}"); // false
     /// ```
     ///
-    /// Use instead:
+    /// The correct way to compare floating point numbers is to define an allowed error margin. This
+    /// may be challenging if there is no "natural" error margin to permit. Broadly speaking, there
+    /// are two cases:
+    ///
+    /// 1. If your values are in a known range and you can define a threshold for "close enough to
+    ///    be equal", it may be appropriate to define an absolute error margin. For example, if your
+    ///    data is "length of vehicle in centimeters", you may consider 0.1 cm to be "close enough".
+    /// 1. If your code is more general and you do not know the range of values, you should use a
+    ///    relative error margin, accepting e.g. 0.1% of error regardless of specific values.
+    ///
+    /// For the scenario where you can define a meaningful absolute error margin, consider using:
+    ///
     /// ```no_run
-    /// # let x: f64 = 1.0;
-    /// # const ONE: f64 = 1.00;
-    /// let error_margin = f64::EPSILON; // Use an epsilon for comparison
-    /// // Or, if Rust <= 1.42, use `std::f64::EPSILON` constant instead.
-    /// // let error_margin = std::f64::EPSILON;
-    /// if (x - ONE).abs() < error_margin { }
+    /// let a: f64 = 1000.1;
+    /// let b: f64 = 0.2;
+    /// let x = a + b;
+    /// const Y: f64 = 1000.3; // Expected value.
+    ///
+    /// const ALLOWED_ERROR_VEHICLE_LENGTH_CM: f64 = 0.1;
+    /// let within_tolerance = (x - Y).abs() < ALLOWED_ERROR_VEHICLE_LENGTH_CM;
+    /// println!("{within_tolerance}"); // true
     /// ```
+    ///
+    /// NB! Do not use `f64::EPSILON` - while the error margin is often called "epsilon", this is
+    /// a different use of the term that is not suitable for floating point equality comparison.
+    /// Indeed, for the example above using `f64::EPSILON` as the allowed error would return `false`.
+    ///
+    /// For the scenario where no meaningful absolute error can be defined, refer to
+    /// [the floating point guide](https://www.floating-point-gui.de/errors/comparison)
+    /// for a reference implementation of relative error based comparison of floating point values.
+    /// `MIN_NORMAL` in the reference implementation is equivalent to `MIN_POSITIVE` in Rust.
     #[clippy::version = "pre 1.29.0"]
     pub FLOAT_CMP_CONST,
     restriction,
-    "using `==` or `!=` on float constants instead of comparing difference with an epsilon"
+    "using `==` or `!=` on float constants instead of comparing difference with an allowed error"
 }
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks for getting the remainder of a division by one or minus
+    /// Checks for getting the remainder of integer division by one or minus
     /// one.
     ///
     /// ### Why is this bad?
@@ -646,7 +714,7 @@ declare_clippy_lint! {
     #[clippy::version = "pre 1.29.0"]
     pub MODULO_ONE,
     correctness,
-    "taking a number modulo +/-1, which can either panic/overflow or always returns 0"
+    "taking an integer modulo +/-1, which can either panic/overflow or always returns 0"
 }
 
 declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs b/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs
deleted file mode 100644
index de789879331..00000000000
--- a/src/tools/clippy/clippy_lints/src/overflow_check_conditional.rs
+++ /dev/null
@@ -1,70 +0,0 @@
-use clippy_utils::diagnostics::span_lint;
-use clippy_utils::SpanlessEq;
-use rustc_hir::{BinOpKind, Expr, ExprKind, QPath};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::declare_lint_pass;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Detects classic underflow/overflow checks.
-    ///
-    /// ### Why is this bad?
-    /// Most classic C underflow/overflow checks will fail in
-    /// Rust. Users can use functions like `overflowing_*` and `wrapping_*` instead.
-    ///
-    /// ### Example
-    /// ```no_run
-    /// # let a = 1;
-    /// # let b = 2;
-    /// a + b < a;
-    /// ```
-    #[clippy::version = "pre 1.29.0"]
-    pub OVERFLOW_CHECK_CONDITIONAL,
-    complexity,
-    "overflow checks inspired by C which are likely to panic"
-}
-
-declare_lint_pass!(OverflowCheckConditional => [OVERFLOW_CHECK_CONDITIONAL]);
-
-const OVERFLOW_MSG: &str = "you are trying to use classic C overflow conditions that will fail in Rust";
-const UNDERFLOW_MSG: &str = "you are trying to use classic C underflow conditions that will fail in Rust";
-
-impl<'tcx> LateLintPass<'tcx> for OverflowCheckConditional {
-    // a + b < a, a > a + b, a < a - b, a - b > a
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        let eq = |l, r| SpanlessEq::new(cx).eq_path_segment(l, r);
-        if let ExprKind::Binary(ref op, first, second) = expr.kind
-            && let ExprKind::Binary(ref op2, ident1, ident2) = first.kind
-            && let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind
-            && let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind
-            && let ExprKind::Path(QPath::Resolved(_, path3)) = second.kind
-            && (eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]))
-            && cx.typeck_results().expr_ty(ident1).is_integral()
-            && cx.typeck_results().expr_ty(ident2).is_integral()
-        {
-            if op.node == BinOpKind::Lt && op2.node == BinOpKind::Add {
-                span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, OVERFLOW_MSG);
-            }
-            if op.node == BinOpKind::Gt && op2.node == BinOpKind::Sub {
-                span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, UNDERFLOW_MSG);
-            }
-        }
-
-        if let ExprKind::Binary(ref op, first, second) = expr.kind
-            && let ExprKind::Binary(ref op2, ident1, ident2) = second.kind
-            && let ExprKind::Path(QPath::Resolved(_, path1)) = ident1.kind
-            && let ExprKind::Path(QPath::Resolved(_, path2)) = ident2.kind
-            && let ExprKind::Path(QPath::Resolved(_, path3)) = first.kind
-            && (eq(&path1.segments[0], &path3.segments[0]) || eq(&path2.segments[0], &path3.segments[0]))
-            && cx.typeck_results().expr_ty(ident1).is_integral()
-            && cx.typeck_results().expr_ty(ident2).is_integral()
-        {
-            if op.node == BinOpKind::Gt && op2.node == BinOpKind::Add {
-                span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, OVERFLOW_MSG);
-            }
-            if op.node == BinOpKind::Lt && op2.node == BinOpKind::Sub {
-                span_lint(cx, OVERFLOW_CHECK_CONDITIONAL, expr.span, UNDERFLOW_MSG);
-            }
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/panicking_overflow_checks.rs b/src/tools/clippy/clippy_lints/src/panicking_overflow_checks.rs
new file mode 100644
index 00000000000..7f100a746d5
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/panicking_overflow_checks.rs
@@ -0,0 +1,86 @@
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::eq_expr_value;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty;
+use rustc_session::declare_lint_pass;
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Detects C-style underflow/overflow checks.
+    ///
+    /// ### Why is this bad?
+    /// These checks will, by default, panic in debug builds rather than check
+    /// whether the operation caused an overflow.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// # let a = 1i32;
+    /// # let b = 2i32;
+    /// if a + b < a {
+    ///     // handle overflow
+    /// }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```no_run
+    /// # let a = 1i32;
+    /// # let b = 2i32;
+    /// if a.checked_add(b).is_none() {
+    ///     // handle overflow
+    /// }
+    /// ```
+    ///
+    /// Or:
+    /// ```no_run
+    /// # let a = 1i32;
+    /// # let b = 2i32;
+    /// if a.overflowing_add(b).1 {
+    ///     // handle overflow
+    /// }
+    /// ```
+    #[clippy::version = "pre 1.29.0"]
+    pub PANICKING_OVERFLOW_CHECKS,
+    correctness,
+    "overflow checks which will panic in debug mode"
+}
+
+declare_lint_pass!(PanickingOverflowChecks => [PANICKING_OVERFLOW_CHECKS]);
+
+impl<'tcx> LateLintPass<'tcx> for PanickingOverflowChecks {
+    // a + b < a, a > a + b, a < a - b, a - b > a
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if let ExprKind::Binary(op, lhs, rhs) = expr.kind
+            && let (lt, gt) = match op.node {
+                BinOpKind::Lt => (lhs, rhs),
+                BinOpKind::Gt => (rhs, lhs),
+                _ => return,
+            }
+            && let ctxt = expr.span.ctxt()
+            && let (op_lhs, op_rhs, other, commutative) = match (&lt.kind, &gt.kind) {
+                (&ExprKind::Binary(op, lhs, rhs), _) if op.node == BinOpKind::Add && ctxt == lt.span.ctxt() => {
+                    (lhs, rhs, gt, true)
+                },
+                (_, &ExprKind::Binary(op, lhs, rhs)) if op.node == BinOpKind::Sub && ctxt == gt.span.ctxt() => {
+                    (lhs, rhs, lt, false)
+                },
+                _ => return,
+            }
+            && let typeck = cx.typeck_results()
+            && let ty = typeck.expr_ty(op_lhs)
+            && matches!(ty.kind(), ty::Uint(_))
+            && ty == typeck.expr_ty(op_rhs)
+            && ty == typeck.expr_ty(other)
+            && !in_external_macro(cx.tcx.sess, expr.span)
+            && (eq_expr_value(cx, op_lhs, other) || (commutative && eq_expr_value(cx, op_rhs, other)))
+        {
+            span_lint(
+                cx,
+                PANICKING_OVERFLOW_CHECKS,
+                expr.span,
+                "you are trying to use classic C overflow conditions that will fail in Rust",
+            );
+        }
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/renamed_lints.rs b/src/tools/clippy/clippy_lints/src/renamed_lints.rs
index 85979903b58..8e999f3e89a 100644
--- a/src/tools/clippy/clippy_lints/src/renamed_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/renamed_lints.rs
@@ -26,12 +26,14 @@ pub static RENAMED_LINTS: &[(&str, &str)] = &[
     ("clippy::option_map_unwrap_or", "clippy::map_unwrap_or"),
     ("clippy::option_map_unwrap_or_else", "clippy::map_unwrap_or"),
     ("clippy::option_unwrap_used", "clippy::unwrap_used"),
+    ("clippy::overflow_check_conditional", "clippy::panicking_overflow_checks"),
     ("clippy::ref_in_deref", "clippy::needless_borrow"),
     ("clippy::result_expect_used", "clippy::expect_used"),
     ("clippy::result_map_unwrap_or_else", "clippy::map_unwrap_or"),
     ("clippy::result_unwrap_used", "clippy::unwrap_used"),
     ("clippy::single_char_push_str", "clippy::single_char_add_str"),
     ("clippy::stutter", "clippy::module_name_repetitions"),
+    ("clippy::thread_local_initializer_can_be_made_const", "clippy::missing_const_for_thread_local"),
     ("clippy::to_string_in_display", "clippy::recursive_format_impl"),
     ("clippy::unwrap_or_else_default", "clippy::unwrap_or_default"),
     ("clippy::zero_width_space", "clippy::invisible_characters"),
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index c11da3147ef..8ced47b48a4 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -7,6 +7,7 @@ use clippy_utils::{
     path_to_local_id, span_contains_cfg, span_find_starting_semi,
 };
 use core::ops::ControlFlow;
+use rustc_ast::NestedMetaItem;
 use rustc_errors::Applicability;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::LangItem::ResultErr;
@@ -14,13 +15,13 @@ use rustc_hir::{
     Block, Body, Expr, ExprKind, FnDecl, HirId, ItemKind, LangItem, MatchSource, Node, OwnerNode, PatKind, QPath, Stmt,
     StmtKind,
 };
-use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_lint::{LateContext, LateLintPass, Level, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_middle::ty::adjustment::Adjust;
 use rustc_middle::ty::{self, GenericArgKind, Ty};
 use rustc_session::declare_lint_pass;
 use rustc_span::def_id::LocalDefId;
-use rustc_span::{BytePos, Pos, Span};
+use rustc_span::{sym, BytePos, Pos, Span};
 use std::borrow::Cow;
 use std::fmt::Display;
 
@@ -80,6 +81,9 @@ declare_clippy_lint! {
     /// ```
     #[clippy::version = "pre 1.29.0"]
     pub NEEDLESS_RETURN,
+    // This lint requires some special handling in `check_final_expr` for `#[expect]`.
+    // This handling needs to be updated if the group gets changed. This should also
+    // be caught by tests.
     style,
     "using a return statement like `return expr;` where an expression would suffice"
 }
@@ -91,6 +95,9 @@ declare_clippy_lint! {
     /// ### Why is this bad?
     /// The `return` is unnecessary.
     ///
+    /// Returns may be used to add attributes to the return expression. Return
+    /// statements with attributes are therefore be accepted by this lint.
+    ///
     /// ### Example
     /// ```rust,ignore
     /// fn foo(x: usize) -> Result<(), Box<dyn Error>> {
@@ -377,13 +384,39 @@ fn check_final_expr<'tcx>(
                 }
             };
 
-            if !cx.tcx.hir().attrs(expr.hir_id).is_empty() {
-                return;
-            }
             let borrows = inner.map_or(false, |inner| last_statement_borrows(cx, inner));
             if borrows {
                 return;
             }
+            if ret_span.from_expansion() {
+                return;
+            }
+
+            // Returns may be used to turn an expression into a statement in rustc's AST.
+            // This allows the addition of attributes, like `#[allow]` (See: clippy#9361)
+            // `#[expect(clippy::needless_return)]` needs to be handled separatly to
+            // actually fullfil the expectation (clippy::#12998)
+            match cx.tcx.hir().attrs(expr.hir_id) {
+                [] => {},
+                [attr] => {
+                    if matches!(Level::from_attr(attr), Some(Level::Expect(_)))
+                        && let metas = attr.meta_item_list()
+                        && let Some(lst) = metas
+                        && let [NestedMetaItem::MetaItem(meta_item)] = lst.as_slice()
+                        && let [tool, lint_name] = meta_item.path.segments.as_slice()
+                        && tool.ident.name == sym::clippy
+                        && matches!(
+                            lint_name.ident.name.as_str(),
+                            "needless_return" | "style" | "all" | "warnings"
+                        )
+                    {
+                        // This is an expectation of the `needless_return` lint
+                    } else {
+                        return;
+                    }
+                },
+                _ => return,
+            }
 
             emit_return_lint(cx, ret_span, semi_spans, &replacement, expr.hir_id);
         },
@@ -415,10 +448,6 @@ fn emit_return_lint(
     replacement: &RetReplacement<'_>,
     at: HirId,
 ) {
-    if ret_span.from_expansion() {
-        return;
-    }
-
     span_lint_hir_and_then(
         cx,
         NEEDLESS_RETURN,
diff --git a/src/tools/clippy/clippy_lints/src/same_name_method.rs b/src/tools/clippy/clippy_lints/src/same_name_method.rs
index 8fdd19c549f..508f3ae6def 100644
--- a/src/tools/clippy/clippy_lints/src/same_name_method.rs
+++ b/src/tools/clippy/clippy_lints/src/same_name_method.rs
@@ -12,7 +12,7 @@ use std::collections::{BTreeMap, BTreeSet};
 declare_clippy_lint! {
     /// ### What it does
     /// It lints if a struct has two methods with the same name:
-    /// one from a trait, another not from trait.
+    /// one from a trait, another not from a trait.
     ///
     /// ### Why restrict this?
     /// Confusing.
diff --git a/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs
new file mode 100644
index 00000000000..5e65b9fa517
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/set_contains_or_insert.rs
@@ -0,0 +1,125 @@
+use std::ops::ControlFlow;
+
+use clippy_utils::diagnostics::span_lint;
+use clippy_utils::ty::is_type_diagnostic_item;
+use clippy_utils::visitors::for_each_expr;
+use clippy_utils::{higher, peel_hir_expr_while, SpanlessEq};
+use rustc_hir::{Expr, ExprKind, UnOp};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::declare_lint_pass;
+use rustc_span::symbol::Symbol;
+use rustc_span::{sym, Span};
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for usage of `contains` to see if a value is not
+    /// present on `HashSet` followed by a `insert`.
+    ///
+    /// ### Why is this bad?
+    /// Using just `insert` and checking the returned `bool` is more efficient.
+    ///
+    /// ### Known problems
+    /// In case the value that wants to be inserted is borrowed and also expensive or impossible
+    /// to clone. In such a scenario, the developer might want to check with `contains` before inserting,
+    /// to avoid the clone. In this case, it will report a false positive.
+    ///
+    /// ### Example
+    /// ```rust
+    /// use std::collections::HashSet;
+    /// let mut set = HashSet::new();
+    /// let value = 5;
+    /// if !set.contains(&value) {
+    ///     set.insert(value);
+    ///     println!("inserted {value:?}");
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```rust
+    /// use std::collections::HashSet;
+    /// let mut set = HashSet::new();
+    /// let value = 5;
+    /// if set.insert(&value) {
+    ///     println!("inserted {value:?}");
+    /// }
+    /// ```
+    #[clippy::version = "1.80.0"]
+    pub SET_CONTAINS_OR_INSERT,
+    nursery,
+    "call to `HashSet::contains` followed by `HashSet::insert`"
+}
+
+declare_lint_pass!(HashsetInsertAfterContains => [SET_CONTAINS_OR_INSERT]);
+
+impl<'tcx> LateLintPass<'tcx> for HashsetInsertAfterContains {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if !expr.span.from_expansion()
+            && let Some(higher::If {
+                cond: cond_expr,
+                then: then_expr,
+                ..
+            }) = higher::If::hir(expr)
+            && let Some(contains_expr) = try_parse_op_call(cx, cond_expr, sym!(contains))//try_parse_contains(cx, cond_expr)
+            && let Some(insert_expr) = find_insert_calls(cx, &contains_expr, then_expr)
+        {
+            span_lint(
+                cx,
+                SET_CONTAINS_OR_INSERT,
+                vec![contains_expr.span, insert_expr.span],
+                "usage of `HashSet::insert` after `HashSet::contains`",
+            );
+        }
+    }
+}
+
+struct OpExpr<'tcx> {
+    receiver: &'tcx Expr<'tcx>,
+    value: &'tcx Expr<'tcx>,
+    span: Span,
+}
+
+fn try_parse_op_call<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, symbol: Symbol) -> Option<OpExpr<'tcx>> {
+    let expr = peel_hir_expr_while(expr, |e| {
+        if let ExprKind::Unary(UnOp::Not, e) = e.kind {
+            Some(e)
+        } else {
+            None
+        }
+    });
+
+    if let ExprKind::MethodCall(path, receiver, [value], span) = expr.kind {
+        let value = value.peel_borrows();
+        let value = peel_hir_expr_while(value, |e| {
+            if let ExprKind::Unary(UnOp::Deref, e) = e.kind {
+                Some(e)
+            } else {
+                None
+            }
+        });
+        let receiver = receiver.peel_borrows();
+        let receiver_ty = cx.typeck_results().expr_ty(receiver).peel_refs();
+        if value.span.eq_ctxt(expr.span)
+            && is_type_diagnostic_item(cx, receiver_ty, sym::HashSet)
+            && path.ident.name == symbol
+        {
+            return Some(OpExpr { receiver, value, span });
+        }
+    }
+    None
+}
+
+fn find_insert_calls<'tcx>(
+    cx: &LateContext<'tcx>,
+    contains_expr: &OpExpr<'tcx>,
+    expr: &'tcx Expr<'_>,
+) -> Option<OpExpr<'tcx>> {
+    for_each_expr(cx, expr, |e| {
+        if let Some(insert_expr) = try_parse_op_call(cx, e, sym!(insert))
+            && SpanlessEq::new(cx).eq_expr(contains_expr.receiver, insert_expr.receiver)
+            && SpanlessEq::new(cx).eq_expr(contains_expr.value, insert_expr.value)
+        {
+            ControlFlow::Break(insert_expr)
+        } else {
+            ControlFlow::Continue(())
+        }
+    })
+}
diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
index c05cd9ed593..07390d6f430 100644
--- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
@@ -102,9 +102,9 @@ impl TraitBounds {
 impl_lint_pass!(TraitBounds => [TYPE_REPETITION_IN_BOUNDS, TRAIT_DUPLICATION_IN_BOUNDS]);
 
 impl<'tcx> LateLintPass<'tcx> for TraitBounds {
-    fn check_generics(&mut self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
-        self.check_type_repetition(cx, gen);
-        check_trait_bound_duplication(cx, gen);
+    fn check_generics(&mut self, cx: &LateContext<'tcx>, generics: &'tcx Generics<'_>) {
+        self.check_type_repetition(cx, generics);
+        check_trait_bound_duplication(cx, generics);
     }
 
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
@@ -238,7 +238,7 @@ impl TraitBounds {
     }
 
     #[allow(clippy::mutable_key_type)]
-    fn check_type_repetition<'tcx>(&self, cx: &LateContext<'tcx>, gen: &'tcx Generics<'_>) {
+    fn check_type_repetition<'tcx>(&self, cx: &LateContext<'tcx>, generics: &'tcx Generics<'_>) {
         struct SpanlessTy<'cx, 'tcx> {
             ty: &'tcx Ty<'tcx>,
             cx: &'cx LateContext<'tcx>,
@@ -258,12 +258,12 @@ impl TraitBounds {
         }
         impl Eq for SpanlessTy<'_, '_> {}
 
-        if gen.span.from_expansion() {
+        if generics.span.from_expansion() {
             return;
         }
         let mut map: UnhashMap<SpanlessTy<'_, '_>, Vec<&GenericBound<'_>>> = UnhashMap::default();
         let mut applicability = Applicability::MaybeIncorrect;
-        for bound in gen.predicates {
+        for bound in generics.predicates {
             if let WherePredicate::BoundPredicate(ref p) = bound
                 && p.origin != PredicateOrigin::ImplTrait
                 && p.bounds.len() as u64 <= self.max_trait_bounds
@@ -301,8 +301,8 @@ impl TraitBounds {
     }
 }
 
-fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
-    if gen.span.from_expansion() {
+fn check_trait_bound_duplication(cx: &LateContext<'_>, generics: &'_ Generics<'_>) {
+    if generics.span.from_expansion() {
         return;
     }
 
@@ -313,7 +313,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
     //       |
     // collects each of these where clauses into a set keyed by generic name and comparable trait
     // eg. (T, Clone)
-    let where_predicates = gen
+    let where_predicates = generics
         .predicates
         .iter()
         .filter_map(|pred| {
@@ -342,7 +342,7 @@ fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
     //            |
     // compare trait bounds keyed by generic name and comparable trait to collected where
     // predicates eg. (T, Clone)
-    for predicate in gen.predicates.iter().filter(|pred| !pred.in_where_clause()) {
+    for predicate in generics.predicates.iter().filter(|pred| !pred.in_where_clause()) {
         if let WherePredicate::BoundPredicate(bound_predicate) = predicate
             && bound_predicate.origin != PredicateOrigin::ImplTrait
             && !bound_predicate.span.from_expansion()
diff --git a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs
index 0aa50c99c16..b6e765d7c39 100644
--- a/src/tools/clippy/clippy_lints/src/types/type_complexity.rs
+++ b/src/tools/clippy/clippy_lints/src/types/type_complexity.rs
@@ -57,7 +57,7 @@ impl<'tcx> Visitor<'tcx> for TypeComplexityVisitor {
                     bound
                         .bound_generic_params
                         .iter()
-                        .any(|gen| matches!(gen.kind, GenericParamKind::Lifetime { .. }))
+                        .any(|param| matches!(param.kind, GenericParamKind::Lifetime { .. }))
                 });
                 if has_lifetime_parameters {
                     // complex trait bounds like A<'a, 'b>
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
index 877a77fd6d2..1d294c2944b 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
@@ -1,6 +1,5 @@
 pub mod almost_standard_lint_formulation;
 pub mod collapsible_calls;
-pub mod compiler_lint_functions;
 pub mod interning_defined_symbol;
 pub mod invalid_paths;
 pub mod lint_without_lint_pass;
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs
deleted file mode 100644
index 9b6b6871818..00000000000
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/compiler_lint_functions.rs
+++ /dev/null
@@ -1,73 +0,0 @@
-use clippy_utils::diagnostics::span_lint_and_help;
-use clippy_utils::ty::match_type;
-use clippy_utils::{is_lint_allowed, paths};
-use rustc_data_structures::fx::FxHashMap;
-use rustc_hir::{Expr, ExprKind};
-use rustc_lint::{LateContext, LateLintPass};
-use rustc_session::impl_lint_pass;
-
-declare_clippy_lint! {
-    /// ### What it does
-    /// Checks for calls to `cx.span_lint*` and suggests to use the `utils::*`
-    /// variant of the function.
-    ///
-    /// ### Why is this bad?
-    /// The `utils::*` variants also add a link to the Clippy documentation to the
-    /// warning/error messages.
-    ///
-    /// ### Example
-    /// ```rust,ignore
-    /// cx.span_lint(LINT_NAME, "message");
-    /// ```
-    ///
-    /// Use instead:
-    /// ```rust,ignore
-    /// utils::span_lint(cx, LINT_NAME, "message");
-    /// ```
-    pub COMPILER_LINT_FUNCTIONS,
-    internal,
-    "usage of the lint functions of the compiler instead of the utils::* variant"
-}
-
-impl_lint_pass!(CompilerLintFunctions => [COMPILER_LINT_FUNCTIONS]);
-
-#[derive(Clone, Default)]
-pub struct CompilerLintFunctions {
-    map: FxHashMap<&'static str, &'static str>,
-}
-
-impl CompilerLintFunctions {
-    #[must_use]
-    pub fn new() -> Self {
-        let mut map = FxHashMap::default();
-        map.insert("span_lint", "utils::span_lint");
-        map.insert("lint", "utils::span_lint");
-        map.insert("span_lint_note", "utils::span_lint_and_note");
-        map.insert("span_lint_help", "utils::span_lint_and_help");
-        Self { map }
-    }
-}
-
-impl<'tcx> LateLintPass<'tcx> for CompilerLintFunctions {
-    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
-        if is_lint_allowed(cx, COMPILER_LINT_FUNCTIONS, expr.hir_id) {
-            return;
-        }
-
-        if let ExprKind::MethodCall(path, self_arg, _, _) = &expr.kind
-            && let fn_name = path.ident
-            && let Some(sugg) = self.map.get(fn_name.as_str())
-            && let ty = cx.typeck_results().expr_ty(self_arg).peel_refs()
-            && (match_type(cx, ty, &paths::EARLY_CONTEXT) || match_type(cx, ty, &paths::LATE_CONTEXT))
-        {
-            span_lint_and_help(
-                cx,
-                COMPILER_LINT_FUNCTIONS,
-                path.ident.span,
-                "usage of a compiler lint function",
-                None,
-                format!("please use the Clippy variant of this function: `{sugg}`"),
-            );
-        }
-    }
-}
diff --git a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
index 436f0cb79fb..a0a60ca8875 100644
--- a/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/wildcard_imports.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_sugg;
-use clippy_utils::is_test_module_or_function;
+use clippy_utils::is_in_test;
 use clippy_utils::source::{snippet, snippet_with_applicability};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Applicability;
@@ -100,7 +100,6 @@ declare_clippy_lint! {
 #[derive(Default)]
 pub struct WildcardImports {
     warn_on_all: bool,
-    test_modules_deep: u32,
     allowed_segments: FxHashSet<String>,
 }
 
@@ -108,7 +107,6 @@ impl WildcardImports {
     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,
         }
     }
@@ -122,15 +120,12 @@ impl LateLintPass<'_> for WildcardImports {
             return;
         }
 
-        if is_test_module_or_function(cx.tcx, item) {
-            self.test_modules_deep = self.test_modules_deep.saturating_add(1);
-        }
         let module = cx.tcx.parent_module_from_def_id(item.owner_id.def_id);
         if cx.tcx.visibility(item.owner_id.def_id) != ty::Visibility::Restricted(module.to_def_id()) {
             return;
         }
         if let ItemKind::Use(use_path, UseKind::Glob) = &item.kind
-            && (self.warn_on_all || !self.check_exceptions(item, use_path.segments))
+            && (self.warn_on_all || !self.check_exceptions(cx, item, use_path.segments))
             && let used_imports = cx.tcx.names_imported_by_glob_use(item.owner_id.def_id)
             && !used_imports.is_empty() // Already handled by `unused_imports`
             && !used_imports.contains(&kw::Underscore)
@@ -180,20 +175,14 @@ impl LateLintPass<'_> for WildcardImports {
             span_lint_and_sugg(cx, lint, span, message, "try", sugg, applicability);
         }
     }
-
-    fn check_item_post(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
-        if is_test_module_or_function(cx.tcx, item) {
-            self.test_modules_deep = self.test_modules_deep.saturating_sub(1);
-        }
-    }
 }
 
 impl WildcardImports {
-    fn check_exceptions(&self, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool {
+    fn check_exceptions(&self, cx: &LateContext<'_>, item: &Item<'_>, segments: &[PathSegment<'_>]) -> bool {
         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)
+            || (is_super_only_import(segments) && is_in_test(cx.tcx, item.hir_id()))
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index 652ce88bd95..96e53b7ef0b 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::{span_lint, span_lint_and_then};
+use clippy_utils::is_in_test;
 use clippy_utils::macros::{format_arg_removal_span, root_macro_call_first_node, FormatArgsStorage, MacroCall};
 use clippy_utils::source::{expand_past_previous_comma, snippet_opt};
-use clippy_utils::{is_in_cfg_test, is_in_test_function};
 use rustc_ast::token::LitKind;
 use rustc_ast::{
     FormatArgPosition, FormatArgPositionKind, FormatArgs, FormatArgsPiece, FormatOptions, FormatPlaceholder,
@@ -297,8 +297,7 @@ impl<'tcx> LateLintPass<'tcx> for Write {
             .as_ref()
             .map_or(false, |crate_name| crate_name == "build_script_build");
 
-        let allowed_in_tests = self.allow_print_in_tests
-            && (is_in_test_function(cx.tcx, expr.hir_id) || is_in_cfg_test(cx.tcx, expr.hir_id));
+        let allowed_in_tests = self.allow_print_in_tests && is_in_test(cx.tcx, expr.hir_id);
         match diag_name {
             sym::print_macro | sym::println_macro if !allowed_in_tests => {
                 if !is_build_script {
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index 935a25d7931..bdb3b5e45c4 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -102,10 +102,11 @@ use rustc_hir::hir_id::{HirIdMap, HirIdSet};
 use rustc_hir::intravisit::{walk_expr, FnKind, Visitor};
 use rustc_hir::LangItem::{OptionNone, OptionSome, ResultErr, ResultOk};
 use rustc_hir::{
-    self as hir, def, Arm, ArrayLen, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, Destination, Expr,
-    ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind, ImplItemRef, Item,
-    ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, Param, Pat, PatKind, Path, PathSegment,
-    PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef, TyKind, UnOp,
+    self as hir, def, Arm, ArrayLen, BindingMode, Block, BlockCheckMode, Body, ByRef, Closure, ConstContext,
+    Destination, Expr, ExprField, ExprKind, FnDecl, FnRetTy, GenericArgs, HirId, Impl, ImplItem, ImplItemKind,
+    ImplItemRef, Item, ItemKind, LangItem, LetStmt, MatchSource, Mutability, Node, OwnerId, OwnerNode, Param, Pat,
+    PatKind, Path, PathSegment, PrimTy, QPath, Stmt, StmtKind, TraitItem, TraitItemKind, TraitItemRef, TraitRef,
+    TyKind, UnOp,
 };
 use rustc_lexer::{tokenize, TokenKind};
 use rustc_lint::{LateContext, Level, Lint, LintContext};
@@ -210,7 +211,10 @@ pub fn local_is_initialized(cx: &LateContext<'_>, local: HirId) -> bool {
     false
 }
 
-/// Returns `true` if the given `NodeId` is inside a constant context
+/// Returns `true` if the given `HirId` is inside a constant context.
+///
+/// This is the same as `is_inside_always_const_context`, but also includes
+/// `const fn`.
 ///
 /// # Example
 ///
@@ -223,6 +227,24 @@ pub fn in_constant(cx: &LateContext<'_>, id: HirId) -> bool {
     cx.tcx.hir().is_inside_const_context(id)
 }
 
+/// Returns `true` if the given `HirId` is inside an always constant context.
+///
+/// This context includes:
+///  * const/static items
+///  * const blocks (or inline consts)
+///  * associated constants
+pub fn is_inside_always_const_context(tcx: TyCtxt<'_>, hir_id: HirId) -> bool {
+    use ConstContext::{Const, ConstFn, Static};
+    let hir = tcx.hir();
+    let Some(ctx) = hir.body_const_context(hir.enclosing_body_owner(hir_id)) else {
+        return false;
+    };
+    match ctx {
+        ConstFn => false,
+        Static(_) | Const { inline: _ } => true,
+    }
+}
+
 /// Checks if a `Res` refers to a constructor of a `LangItem`
 /// For example, use this to check whether a function call or a pattern is `Some(..)`.
 pub fn is_res_lang_ctor(cx: &LateContext<'_>, res: Res, lang_item: LangItem) -> bool {
@@ -1904,8 +1926,18 @@ pub fn any_parent_has_attr(tcx: TyCtxt<'_>, node: HirId, symbol: Symbol) -> bool
     false
 }
 
-pub fn any_parent_is_automatically_derived(tcx: TyCtxt<'_>, node: HirId) -> bool {
-    any_parent_has_attr(tcx, node, sym::automatically_derived)
+/// Checks if the given HIR node is inside an `impl` block with the `automatically_derived`
+/// attribute.
+pub fn in_automatically_derived(tcx: TyCtxt<'_>, id: HirId) -> bool {
+    tcx.hir()
+        .parent_owner_iter(id)
+        .filter(|(_, node)| matches!(node, OwnerNode::Item(item) if matches!(item.kind, ItemKind::Impl(_))))
+        .any(|(id, _)| {
+            has_attr(
+                tcx.hir().attrs(tcx.local_def_id_to_hir_id(id.def_id)),
+                sym::automatically_derived,
+            )
+        })
 }
 
 /// Matches a function call with the given path and returns the arguments.
@@ -2472,6 +2504,17 @@ pub fn peel_hir_ty_refs<'a>(mut ty: &'a hir::Ty<'a>) -> (&'a hir::Ty<'a>, usize)
     }
 }
 
+/// Peels off all references on the type. Returns the underlying type and the number of references
+/// removed.
+pub fn peel_middle_ty_refs(mut ty: Ty<'_>) -> (Ty<'_>, usize) {
+    let mut count = 0;
+    while let rustc_ty::Ref(_, dest_ty, _) = ty.kind() {
+        ty = *dest_ty;
+        count += 1;
+    }
+    (ty, count)
+}
+
 /// Removes `AddrOf` operators (`&`) or deref operators (`*`), but only if a reference type is
 /// dereferenced. An overloaded deref such as `Vec` to slice would not be removed.
 pub fn peel_ref_operators<'hir>(cx: &LateContext<'_>, mut expr: &'hir Expr<'hir>) -> &'hir Expr<'hir> {
@@ -2594,16 +2637,6 @@ pub fn inherits_cfg(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
             .any(|attr| attr.has_name(sym::cfg))
 }
 
-/// Checks whether item either has `test` attribute applied, or
-/// is a module with `test` in its name.
-///
-/// Note: Add `//@compile-flags: --test` to UI tests with a `#[test]` function
-pub fn is_test_module_or_function(tcx: TyCtxt<'_>, item: &Item<'_>) -> bool {
-    is_in_test_function(tcx, item.hir_id())
-        || matches!(item.kind, ItemKind::Mod(..))
-            && item.ident.name.as_str().split('_').any(|a| a == "test" || a == "tests")
-}
-
 /// Walks up the HIR tree from the given expression in an attempt to find where the value is
 /// consumed.
 ///
diff --git a/src/tools/clippy/clippy_utils/src/paths.rs b/src/tools/clippy/clippy_utils/src/paths.rs
index 3f66813801d..d5a3d8b9e5a 100644
--- a/src/tools/clippy/clippy_utils/src/paths.rs
+++ b/src/tools/clippy/clippy_utils/src/paths.rs
@@ -88,6 +88,7 @@ pub const SYMBOL_INTERN: [&str; 4] = ["rustc_span", "symbol", "Symbol", "intern"
 pub const SYMBOL_TO_IDENT_STRING: [&str; 4] = ["rustc_span", "symbol", "Symbol", "to_ident_string"];
 pub const SYM_MODULE: [&str; 3] = ["rustc_span", "symbol", "sym"];
 pub const SYNTAX_CONTEXT: [&str; 3] = ["rustc_span", "hygiene", "SyntaxContext"];
+pub const STRING_FROM_UTF8: [&str; 4] = ["alloc", "string", "String", "from_utf8"];
 #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
 pub const TOKIO_FILE_OPTIONS: [&str; 5] = ["tokio", "fs", "file", "File", "options"];
 #[expect(clippy::invalid_paths)] // internal lints do not know about all external crates
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index acaeb93f44a..fc02b974ee1 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -96,11 +96,7 @@ pub fn contains_ty_adt_constructor_opaque<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'
                         return false;
                     }
 
-                    for (predicate, _span) in cx
-                        .tcx
-                        .explicit_item_super_predicates(def_id)
-                        .iter_identity_copied()
-                    {
+                    for (predicate, _span) in cx.tcx.explicit_item_super_predicates(def_id).iter_identity_copied() {
                         match predicate.kind().skip_binder() {
                             // For `impl Trait<U>`, it will register a predicate of `T: Trait<U>`, so we go through
                             // and check substitutions to find `U`.
@@ -1332,19 +1328,13 @@ pub fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl
 /// If you need this, you should wrap this call in `clippy_utils::ty::deref_chain().any(...)`.
 pub fn get_adt_inherent_method<'a>(cx: &'a LateContext<'_>, ty: Ty<'_>, method_name: Symbol) -> Option<&'a AssocItem> {
     if let Some(ty_did) = ty.ty_adt_def().map(AdtDef::did) {
-        cx.tcx
-            .inherent_impls(ty_did)
-            .into_iter()
-            .flatten()
-            .map(|&did| {
-                cx.tcx
-                    .associated_items(did)
-                    .filter_by_name_unhygienic(method_name)
-                    .next()
-                    .filter(|item| item.kind == AssocKind::Fn)
-            })
-            .next()
-            .flatten()
+        cx.tcx.inherent_impls(ty_did).into_iter().flatten().find_map(|&did| {
+            cx.tcx
+                .associated_items(did)
+                .filter_by_name_unhygienic(method_name)
+                .next()
+                .filter(|item| item.kind == AssocKind::Fn)
+        })
     } else {
         None
     }
diff --git a/src/tools/clippy/lintcheck/Cargo.toml b/src/tools/clippy/lintcheck/Cargo.toml
index ae9e77b8eed..3c86dfe324f 100644
--- a/src/tools/clippy/lintcheck/Cargo.toml
+++ b/src/tools/clippy/lintcheck/Cargo.toml
@@ -16,6 +16,7 @@ clap = { version = "4.4", features = ["derive", "env"] }
 crossbeam-channel = "0.5.6"
 diff = "0.1.13"
 flate2 = "1.0"
+itertools = "0.12"
 rayon = "1.5.1"
 serde = { version = "1.0", features = ["derive"] }
 serde_json = "1.0.85"
diff --git a/src/tools/clippy/lintcheck/README.md b/src/tools/clippy/lintcheck/README.md
index 2d6039caeef..47a96e0a03c 100644
--- a/src/tools/clippy/lintcheck/README.md
+++ b/src/tools/clippy/lintcheck/README.md
@@ -7,13 +7,13 @@ repo.  We can then check the diff and spot new or disappearing warnings.
 From the repo root, run:
 
 ```
-cargo run --target-dir lintcheck/target --manifest-path lintcheck/Cargo.toml
+cargo lintcheck
 ```
 
 or
 
 ```
-cargo lintcheck
+cargo run --target-dir lintcheck/target --manifest-path lintcheck/Cargo.toml
 ```
 
 By default, the logs will be saved into
@@ -33,6 +33,8 @@ the 200 recently most downloaded crates:
 cargo lintcheck popular -n 200 custom.toml
 ```
 
+> Note: Lintcheck isn't sandboxed. Only use it to check crates that you trust or
+> sandbox it manually.
 
 ### Configuring the Crate Sources
 
@@ -65,17 +67,11 @@ sources.
 #### Command Line Options (optional)
 
 ```toml
-bitflags = {name = "bitflags", versions = ['1.2.1'], options = ['-Wclippy::pedantic', '-Wclippy::cargo']}
+clap = {name = "clap", versions = ['4.5.8'], options = ['-Fderive']}
 ```
 
 It is possible to specify command line options for each crate. This makes it
-possible to only check a crate for certain lint groups. If no options are
-specified, the lint groups `clippy::all`, `clippy::pedantic`, and
-`clippy::cargo` are checked. If an empty array is specified only `clippy::all`
-is checked.
-
-**Note:** `-Wclippy::all` is always enabled by default, unless `-Aclippy::all`
-is explicitly specified in the options.
+possible to enable or disable features.
 
 ### Fix mode
 You can run `cargo lintcheck --fix` which will run Clippy with `--fix` and
diff --git a/src/tools/clippy/lintcheck/lintcheck_crates.toml b/src/tools/clippy/lintcheck/lintcheck_crates.toml
index 52f7fee47b6..ff608e6f935 100644
--- a/src/tools/clippy/lintcheck/lintcheck_crates.toml
+++ b/src/tools/clippy/lintcheck/lintcheck_crates.toml
@@ -1,38 +1,38 @@
 [crates]
 # some of these are from cargotest
-cargo = {name = "cargo", versions = ['0.64.0']}
-iron = {name = "iron", versions = ['0.6.1']}
-ripgrep = {name = "ripgrep", versions = ['12.1.1']}
-xsv = {name = "xsv", versions = ['0.13.0']}
+cargo = {name = "cargo", version = '0.64.0'}
+iron = {name = "iron", version = '0.6.1'}
+ripgrep = {name = "ripgrep", version = '12.1.1'}
+xsv = {name = "xsv", version = '0.13.0'}
 # commented out because of 173K clippy::match_same_arms msgs in language_type.rs
-#tokei = { name = "tokei", versions = ['12.0.4']}
-rayon = {name = "rayon", versions = ['1.5.0']}
-serde = {name = "serde", versions = ['1.0.118']}
+#tokei = { name = "tokei", version = '12.0.4'}
+rayon = {name = "rayon", version = '1.5.0'}
+serde = {name = "serde", version = '1.0.118'}
 # top 10 crates.io dls
-bitflags = {name = "bitflags", versions = ['1.2.1']}
+bitflags = {name = "bitflags", version = '1.2.1'}
 # crash = {name = "clippy_crash", path = "/tmp/clippy_crash"}
-libc = {name = "libc", versions = ['0.2.81']}
-log = {name = "log", versions = ['0.4.11']}
-proc-macro2 = {name = "proc-macro2", versions = ['1.0.24']}
-quote = {name = "quote", versions = ['1.0.7']}
-rand = {name = "rand", versions = ['0.7.3']}
-rand_core = {name = "rand_core", versions = ['0.6.0']}
-regex = {name = "regex", versions = ['1.3.2']}
-syn = {name = "syn", versions = ['1.0.54']}
-unicode-xid = {name = "unicode-xid", versions = ['0.2.1']}
+libc = {name = "libc", version = '0.2.81'}
+log = {name = "log", version = '0.4.11'}
+proc-macro2 = {name = "proc-macro2", version = '1.0.24'}
+quote = {name = "quote", version = '1.0.7'}
+rand = {name = "rand", version = '0.7.3'}
+rand_core = {name = "rand_core", version = '0.6.0'}
+regex = {name = "regex", version = '1.3.2'}
+syn = {name = "syn", version = '1.0.54'}
+unicode-xid = {name = "unicode-xid", version = '0.2.1'}
 # some more of dtolnays crates
-anyhow = {name = "anyhow", versions = ['1.0.38']}
-async-trait = {name = "async-trait", versions = ['0.1.42']}
-cxx = {name = "cxx", versions = ['1.0.32']}
-ryu = {name = "ryu", versions = ['1.0.5']}
-serde_yaml = {name = "serde_yaml", versions = ['0.8.17']}
-thiserror = {name = "thiserror", versions = ['1.0.24']}
+anyhow = {name = "anyhow", version = '1.0.38'}
+async-trait = {name = "async-trait", version = '0.1.42'}
+cxx = {name = "cxx", version = '1.0.32'}
+ryu = {name = "ryu", version = '1.0.5'}
+serde_yaml = {name = "serde_yaml", version = '0.8.17'}
+thiserror = {name = "thiserror", version = '1.0.24'}
 # some embark crates, there are other interesting crates but
 # unfortunately adding them increases lintcheck runtime drastically
-cfg-expr = {name = "cfg-expr", versions = ['0.7.1']}
+cfg-expr = {name = "cfg-expr", version = '0.7.1'}
 puffin = {name = "puffin", git_url = "https://github.com/EmbarkStudios/puffin", git_hash = "02dd4a3"}
-rpmalloc = {name = "rpmalloc", versions = ['0.2.0']}
-tame-oidc = {name = "tame-oidc", versions = ['0.1.0']}
+rpmalloc = {name = "rpmalloc", version = '0.2.0'}
+tame-oidc = {name = "tame-oidc", version = '0.1.0'}
 
 [recursive]
 ignore = [
diff --git a/src/tools/clippy/lintcheck/src/config.rs b/src/tools/clippy/lintcheck/src/config.rs
index e6cd7c9fdc2..b35a62eed44 100644
--- a/src/tools/clippy/lintcheck/src/config.rs
+++ b/src/tools/clippy/lintcheck/src/config.rs
@@ -36,6 +36,10 @@ pub(crate) struct LintcheckConfig {
     /// Apply a filter to only collect specified lints, this also overrides `allow` attributes
     #[clap(long = "filter", value_name = "clippy_lint_name", use_value_delimiter = true)]
     pub lint_filter: Vec<String>,
+    /// Set all lints to the "warn" lint level, even resitriction ones. Usually,
+    /// it's better to use `--filter` instead
+    #[clap(long, conflicts_with("lint_filter"))]
+    pub warn_all: bool,
     /// Set the output format of the log file
     #[clap(long, short, default_value = "text")]
     pub format: OutputFormat,
diff --git a/src/tools/clippy/lintcheck/src/driver.rs b/src/tools/clippy/lintcheck/src/driver.rs
index 47724a2fedb..041be5081f2 100644
--- a/src/tools/clippy/lintcheck/src/driver.rs
+++ b/src/tools/clippy/lintcheck/src/driver.rs
@@ -11,8 +11,6 @@ use std::{env, mem};
 fn run_clippy(addr: &str) -> Option<i32> {
     let driver_info = DriverInfo {
         package_name: env::var("CARGO_PKG_NAME").ok()?,
-        crate_name: env::var("CARGO_CRATE_NAME").ok()?,
-        version: env::var("CARGO_PKG_VERSION").ok()?,
     };
 
     let mut stream = BufReader::new(TcpStream::connect(addr).unwrap());
diff --git a/src/tools/clippy/lintcheck/src/input.rs b/src/tools/clippy/lintcheck/src/input.rs
new file mode 100644
index 00000000000..3d034391c28
--- /dev/null
+++ b/src/tools/clippy/lintcheck/src/input.rs
@@ -0,0 +1,288 @@
+use std::collections::{HashMap, HashSet};
+use std::fs::{self};
+use std::io::{self, ErrorKind};
+use std::path::{Path, PathBuf};
+use std::process::Command;
+use std::time::Duration;
+
+use serde::Deserialize;
+use walkdir::{DirEntry, WalkDir};
+
+use crate::{Crate, LINTCHECK_DOWNLOADS, LINTCHECK_SOURCES};
+
+/// List of sources to check, loaded from a .toml file
+#[derive(Debug, Deserialize)]
+pub struct SourceList {
+    crates: HashMap<String, TomlCrate>,
+    #[serde(default)]
+    recursive: RecursiveOptions,
+}
+
+#[derive(Debug, Deserialize, Default)]
+pub struct RecursiveOptions {
+    pub ignore: HashSet<String>,
+}
+
+/// A crate source stored inside the .toml
+/// will be translated into on one of the `CrateSource` variants
+#[derive(Debug, Deserialize)]
+struct TomlCrate {
+    name: String,
+    version: Option<String>,
+    git_url: Option<String>,
+    git_hash: Option<String>,
+    path: Option<String>,
+    options: Option<Vec<String>>,
+}
+
+/// Represents an archive we download from crates.io, or a git repo, or a local repo/folder
+/// Once processed (downloaded/extracted/cloned/copied...), this will be translated into a `Crate`
+#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Ord, PartialOrd)]
+pub enum CrateSource {
+    CratesIo {
+        name: String,
+        version: String,
+        options: Option<Vec<String>>,
+    },
+    Git {
+        name: String,
+        url: String,
+        commit: String,
+        options: Option<Vec<String>>,
+    },
+    Path {
+        name: String,
+        path: PathBuf,
+        options: Option<Vec<String>>,
+    },
+}
+
+/// Read a `lintcheck_crates.toml` file
+pub fn read_crates(toml_path: &Path) -> (Vec<CrateSource>, RecursiveOptions) {
+    let toml_content: String =
+        fs::read_to_string(toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display()));
+    let crate_list: SourceList =
+        toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{e}", toml_path.display()));
+    // parse the hashmap of the toml file into a list of crates
+    let tomlcrates: Vec<TomlCrate> = crate_list.crates.into_values().collect();
+
+    // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate =>
+    // multiple Cratesources)
+    let mut crate_sources = Vec::new();
+    for tk in tomlcrates {
+        if let Some(ref path) = tk.path {
+            crate_sources.push(CrateSource::Path {
+                name: tk.name.clone(),
+                path: PathBuf::from(path),
+                options: tk.options.clone(),
+            });
+        } else if let Some(ref version) = tk.version {
+            crate_sources.push(CrateSource::CratesIo {
+                name: tk.name.clone(),
+                version: version.to_string(),
+                options: tk.options.clone(),
+            });
+        } else if tk.git_url.is_some() && tk.git_hash.is_some() {
+            // otherwise, we should have a git source
+            crate_sources.push(CrateSource::Git {
+                name: tk.name.clone(),
+                url: tk.git_url.clone().unwrap(),
+                commit: tk.git_hash.clone().unwrap(),
+                options: tk.options.clone(),
+            });
+        } else {
+            panic!("Invalid crate source: {tk:?}");
+        }
+
+        // if we have a version as well as a git data OR only one git data, something is funky
+        if tk.version.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some())
+            || tk.git_hash.is_some() != tk.git_url.is_some()
+        {
+            eprintln!("tomlkrate: {tk:?}");
+            assert_eq!(
+                tk.git_hash.is_some(),
+                tk.git_url.is_some(),
+                "Error: Encountered TomlCrate with only one of git_hash and git_url!"
+            );
+            assert!(
+                tk.path.is_none() || (tk.git_hash.is_none() && tk.version.is_none()),
+                "Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields"
+            );
+            unreachable!("Failed to translate TomlCrate into CrateSource!");
+        }
+    }
+    // sort the crates
+    crate_sources.sort();
+
+    (crate_sources, crate_list.recursive)
+}
+
+impl CrateSource {
+    /// Makes the sources available on the disk for clippy to check.
+    /// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or
+    /// copies a local folder
+    #[expect(clippy::too_many_lines)]
+    pub fn download_and_extract(&self) -> Crate {
+        #[allow(clippy::result_large_err)]
+        fn get(path: &str) -> Result<ureq::Response, ureq::Error> {
+            const MAX_RETRIES: u8 = 4;
+            let mut retries = 0;
+            loop {
+                match ureq::get(path).call() {
+                    Ok(res) => return Ok(res),
+                    Err(e) if retries >= MAX_RETRIES => return Err(e),
+                    Err(ureq::Error::Transport(e)) => eprintln!("Error: {e}"),
+                    Err(e) => return Err(e),
+                }
+                eprintln!("retrying in {retries} seconds...");
+                std::thread::sleep(Duration::from_secs(u64::from(retries)));
+                retries += 1;
+            }
+        }
+        match self {
+            CrateSource::CratesIo { name, version, options } => {
+                let extract_dir = PathBuf::from(LINTCHECK_SOURCES);
+                let krate_download_dir = PathBuf::from(LINTCHECK_DOWNLOADS);
+
+                // url to download the crate from crates.io
+                let url = format!("https://crates.io/api/v1/crates/{name}/{version}/download");
+                println!("Downloading and extracting {name} {version} from {url}");
+                create_dirs(&krate_download_dir, &extract_dir);
+
+                let krate_file_path = krate_download_dir.join(format!("{name}-{version}.crate.tar.gz"));
+                // don't download/extract if we already have done so
+                if !krate_file_path.is_file() {
+                    // create a file path to download and write the crate data into
+                    let mut krate_dest = fs::File::create(&krate_file_path).unwrap();
+                    let mut krate_req = get(&url).unwrap().into_reader();
+                    // copy the crate into the file
+                    io::copy(&mut krate_req, &mut krate_dest).unwrap();
+
+                    // unzip the tarball
+                    let ungz_tar = flate2::read::GzDecoder::new(fs::File::open(&krate_file_path).unwrap());
+                    // extract the tar archive
+                    let mut archive = tar::Archive::new(ungz_tar);
+                    archive.unpack(&extract_dir).expect("Failed to extract!");
+                }
+                // crate is extracted, return a new Krate object which contains the path to the extracted
+                // sources that clippy can check
+                Crate {
+                    version: version.clone(),
+                    name: name.clone(),
+                    path: extract_dir.join(format!("{name}-{version}/")),
+                    options: options.clone(),
+                }
+            },
+            CrateSource::Git {
+                name,
+                url,
+                commit,
+                options,
+            } => {
+                let repo_path = {
+                    let mut repo_path = PathBuf::from(LINTCHECK_SOURCES);
+                    // add a -git suffix in case we have the same crate from crates.io and a git repo
+                    repo_path.push(format!("{name}-git"));
+                    repo_path
+                };
+                // clone the repo if we have not done so
+                if !repo_path.is_dir() {
+                    println!("Cloning {url} and checking out {commit}");
+                    if !Command::new("git")
+                        .arg("clone")
+                        .arg(url)
+                        .arg(&repo_path)
+                        .status()
+                        .expect("Failed to clone git repo!")
+                        .success()
+                    {
+                        eprintln!("Failed to clone {url} into {}", repo_path.display());
+                    }
+                }
+                // check out the commit/branch/whatever
+                if !Command::new("git")
+                    .args(["-c", "advice.detachedHead=false"])
+                    .arg("checkout")
+                    .arg(commit)
+                    .current_dir(&repo_path)
+                    .status()
+                    .expect("Failed to check out commit")
+                    .success()
+                {
+                    eprintln!("Failed to checkout {commit} of repo at {}", repo_path.display());
+                }
+
+                Crate {
+                    version: commit.clone(),
+                    name: name.clone(),
+                    path: repo_path,
+                    options: options.clone(),
+                }
+            },
+            CrateSource::Path { name, path, options } => {
+                fn is_cache_dir(entry: &DirEntry) -> bool {
+                    fs::read(entry.path().join("CACHEDIR.TAG"))
+                        .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55"))
+                        .unwrap_or(false)
+                }
+
+                // copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file.
+                // The target/ directory contains a CACHEDIR.TAG file so it is the most commonly skipped directory
+                // as a result of this filter.
+                let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name);
+                if dest_crate_root.exists() {
+                    println!("Deleting existing directory at {dest_crate_root:?}");
+                    fs::remove_dir_all(&dest_crate_root).unwrap();
+                }
+
+                println!("Copying {path:?} to {dest_crate_root:?}");
+
+                for entry in WalkDir::new(path).into_iter().filter_entry(|e| !is_cache_dir(e)) {
+                    let entry = entry.unwrap();
+                    let entry_path = entry.path();
+                    let relative_entry_path = entry_path.strip_prefix(path).unwrap();
+                    let dest_path = dest_crate_root.join(relative_entry_path);
+                    let metadata = entry_path.symlink_metadata().unwrap();
+
+                    if metadata.is_dir() {
+                        fs::create_dir(dest_path).unwrap();
+                    } else if metadata.is_file() {
+                        fs::copy(entry_path, dest_path).unwrap();
+                    }
+                }
+
+                Crate {
+                    version: String::from("local"),
+                    name: name.clone(),
+                    path: dest_crate_root,
+                    options: options.clone(),
+                }
+            },
+        }
+    }
+}
+
+/// Create necessary directories to run the lintcheck tool.
+///
+/// # Panics
+///
+/// This function panics if creating one of the dirs fails.
+fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) {
+    fs::create_dir("target/lintcheck/").unwrap_or_else(|err| {
+        assert_eq!(
+            err.kind(),
+            ErrorKind::AlreadyExists,
+            "cannot create lintcheck target dir"
+        );
+    });
+    fs::create_dir(krate_download_dir).unwrap_or_else(|err| {
+        assert_eq!(err.kind(), ErrorKind::AlreadyExists, "cannot create crate download dir");
+    });
+    fs::create_dir(extract_dir).unwrap_or_else(|err| {
+        assert_eq!(
+            err.kind(),
+            ErrorKind::AlreadyExists,
+            "cannot create crate extraction dir"
+        );
+    });
+}
diff --git a/src/tools/clippy/lintcheck/src/json.rs b/src/tools/clippy/lintcheck/src/json.rs
index 43d0413c7ce..1a652927988 100644
--- a/src/tools/clippy/lintcheck/src/json.rs
+++ b/src/tools/clippy/lintcheck/src/json.rs
@@ -1,37 +1,50 @@
-use std::collections::HashMap;
-use std::fmt::Write;
 use std::fs;
-use std::hash::Hash;
 use std::path::Path;
 
+use itertools::EitherOrBoth;
+use serde::{Deserialize, Serialize};
+
 use crate::ClippyWarning;
 
-/// Creates the log file output for [`crate::config::OutputFormat::Json`]
-pub(crate) fn output(clippy_warnings: &[ClippyWarning]) -> String {
-    serde_json::to_string(&clippy_warnings).unwrap()
+#[derive(Deserialize, Serialize)]
+struct LintJson {
+    lint: String,
+    file_name: String,
+    byte_pos: (u32, u32),
+    rendered: String,
 }
 
-fn load_warnings(path: &Path) -> Vec<ClippyWarning> {
-    let file = fs::read(path).unwrap_or_else(|e| panic!("failed to read {}: {e}", path.display()));
-
-    serde_json::from_slice(&file).unwrap_or_else(|e| panic!("failed to deserialize {}: {e}", path.display()))
+impl LintJson {
+    fn key(&self) -> impl Ord + '_ {
+        (self.file_name.as_str(), self.byte_pos, self.lint.as_str())
+    }
 }
 
-/// Group warnings by their primary span location + lint name
-fn create_map(warnings: &[ClippyWarning]) -> HashMap<impl Eq + Hash + '_, Vec<&ClippyWarning>> {
-    let mut map = HashMap::<_, Vec<_>>::with_capacity(warnings.len());
-
-    for warning in warnings {
-        let span = warning.span();
-        let key = (&warning.lint_type, &span.file_name, span.byte_start, span.byte_end);
+/// Creates the log file output for [`crate::config::OutputFormat::Json`]
+pub(crate) fn output(clippy_warnings: Vec<ClippyWarning>) -> String {
+    let mut lints: Vec<LintJson> = clippy_warnings
+        .into_iter()
+        .map(|warning| {
+            let span = warning.span();
+            LintJson {
+                file_name: span.file_name.clone(),
+                byte_pos: (span.byte_start, span.byte_end),
+                lint: warning.lint,
+                rendered: warning.diag.rendered.unwrap(),
+            }
+        })
+        .collect();
+    lints.sort_by(|a, b| a.key().cmp(&b.key()));
+    serde_json::to_string(&lints).unwrap()
+}
 
-        map.entry(key).or_default().push(warning);
-    }
+fn load_warnings(path: &Path) -> Vec<LintJson> {
+    let file = fs::read(path).unwrap_or_else(|e| panic!("failed to read {}: {e}", path.display()));
 
-    map
+    serde_json::from_slice(&file).unwrap_or_else(|e| panic!("failed to deserialize {}: {e}", path.display()))
 }
 
-fn print_warnings(title: &str, warnings: &[&ClippyWarning]) {
+fn print_warnings(title: &str, warnings: &[LintJson]) {
     if warnings.is_empty() {
         return;
     }
@@ -39,31 +52,20 @@ fn print_warnings(title: &str, warnings: &[&ClippyWarning]) {
     println!("### {title}");
     println!("```");
     for warning in warnings {
-        print!("{}", warning.diag);
+        print!("{}", warning.rendered);
     }
     println!("```");
 }
 
-fn print_changed_diff(changed: &[(&[&ClippyWarning], &[&ClippyWarning])]) {
-    fn render(warnings: &[&ClippyWarning]) -> String {
-        let mut rendered = String::new();
-        for warning in warnings {
-            write!(&mut rendered, "{}", warning.diag).unwrap();
-        }
-        rendered
-    }
-
+fn print_changed_diff(changed: &[(LintJson, LintJson)]) {
     if changed.is_empty() {
         return;
     }
 
     println!("### Changed");
     println!("```diff");
-    for &(old, new) in changed {
-        let old_rendered = render(old);
-        let new_rendered = render(new);
-
-        for change in diff::lines(&old_rendered, &new_rendered) {
+    for (old, new) in changed {
+        for change in diff::lines(&old.rendered, &new.rendered) {
             use diff::Result::{Both, Left, Right};
 
             match change {
@@ -86,26 +88,19 @@ pub(crate) fn diff(old_path: &Path, new_path: &Path) {
     let old_warnings = load_warnings(old_path);
     let new_warnings = load_warnings(new_path);
 
-    let old_map = create_map(&old_warnings);
-    let new_map = create_map(&new_warnings);
-
     let mut added = Vec::new();
     let mut removed = Vec::new();
     let mut changed = Vec::new();
 
-    for (key, new) in &new_map {
-        if let Some(old) = old_map.get(key) {
-            if old != new {
-                changed.push((old.as_slice(), new.as_slice()));
-            }
-        } else {
-            added.extend(new);
-        }
-    }
-
-    for (key, old) in &old_map {
-        if !new_map.contains_key(key) {
-            removed.extend(old);
+    for change in itertools::merge_join_by(old_warnings, new_warnings, |old, new| old.key().cmp(&new.key())) {
+        match change {
+            EitherOrBoth::Both(old, new) => {
+                if old.rendered != new.rendered {
+                    changed.push((old, new));
+                }
+            },
+            EitherOrBoth::Left(old) => removed.push(old),
+            EitherOrBoth::Right(new) => added.push(new),
         }
     }
 
diff --git a/src/tools/clippy/lintcheck/src/main.rs b/src/tools/clippy/lintcheck/src/main.rs
index ec72e0eb5dc..e37ffab13ac 100644
--- a/src/tools/clippy/lintcheck/src/main.rs
+++ b/src/tools/clippy/lintcheck/src/main.rs
@@ -5,6 +5,7 @@
 // When a new lint is introduced, we can search the results for new warnings and check for false
 // positives.
 
+#![feature(iter_collect_into)]
 #![warn(
     trivial_casts,
     trivial_numeric_casts,
@@ -12,84 +13,38 @@
     unused_lifetimes,
     unused_qualifications
 )]
-#![allow(clippy::collapsible_else_if, clippy::needless_borrows_for_generic_args)]
+#![allow(
+    clippy::collapsible_else_if,
+    clippy::needless_borrows_for_generic_args,
+    clippy::module_name_repetitions
+)]
 
 mod config;
 mod driver;
+mod input;
 mod json;
+mod output;
 mod popular_crates;
 mod recursive;
 
 use crate::config::{Commands, LintcheckConfig, OutputFormat};
 use crate::recursive::LintcheckServer;
 
-use std::collections::{HashMap, HashSet};
 use std::env::consts::EXE_SUFFIX;
-use std::fmt::{self, Display, Write as _};
-use std::hash::Hash;
-use std::io::{self, ErrorKind};
+use std::io::{self};
 use std::path::{Path, PathBuf};
-use std::process::{Command, ExitStatus, Stdio};
+use std::process::{Command, Stdio};
 use std::sync::atomic::{AtomicUsize, Ordering};
-use std::time::Duration;
-use std::{env, fs, thread};
+use std::{env, fs};
 
-use cargo_metadata::diagnostic::{Diagnostic, DiagnosticSpan};
 use cargo_metadata::Message;
+use input::{read_crates, CrateSource};
+use output::{ClippyCheckOutput, ClippyWarning, RustcIce};
 use rayon::prelude::*;
-use serde::{Deserialize, Serialize};
-use walkdir::{DirEntry, WalkDir};
 
 const LINTCHECK_DOWNLOADS: &str = "target/lintcheck/downloads";
 const LINTCHECK_SOURCES: &str = "target/lintcheck/sources";
 
-/// List of sources to check, loaded from a .toml file
-#[derive(Debug, Deserialize)]
-struct SourceList {
-    crates: HashMap<String, TomlCrate>,
-    #[serde(default)]
-    recursive: RecursiveOptions,
-}
-
-#[derive(Debug, Deserialize, Default)]
-struct RecursiveOptions {
-    ignore: HashSet<String>,
-}
-
-/// A crate source stored inside the .toml
-/// will be translated into on one of the `CrateSource` variants
-#[derive(Debug, Deserialize)]
-struct TomlCrate {
-    name: String,
-    versions: Option<Vec<String>>,
-    git_url: Option<String>,
-    git_hash: Option<String>,
-    path: Option<String>,
-    options: Option<Vec<String>>,
-}
-
-/// Represents an archive we download from crates.io, or a git repo, or a local repo/folder
-/// Once processed (downloaded/extracted/cloned/copied...), this will be translated into a `Crate`
-#[derive(Debug, Deserialize, Eq, Hash, PartialEq, Ord, PartialOrd)]
-enum CrateSource {
-    CratesIo {
-        name: String,
-        version: String,
-        options: Option<Vec<String>>,
-    },
-    Git {
-        name: String,
-        url: String,
-        commit: String,
-        options: Option<Vec<String>>,
-    },
-    Path {
-        name: String,
-        path: PathBuf,
-        options: Option<Vec<String>>,
-    },
-}
-
 /// Represents the actual source code of a crate that we ran "cargo clippy" on
 #[derive(Debug)]
 struct Crate {
@@ -100,248 +55,6 @@ struct Crate {
     options: Option<Vec<String>>,
 }
 
-/// A single emitted output from clippy being executed on a crate. It may either be a
-/// `ClippyWarning`, or a `RustcIce` caused by a panic within clippy. A crate may have many
-/// `ClippyWarning`s but a maximum of one `RustcIce` (at which point clippy halts execution).
-#[derive(Debug)]
-enum ClippyCheckOutput {
-    ClippyWarning(ClippyWarning),
-    RustcIce(RustcIce),
-}
-
-#[derive(Debug)]
-struct RustcIce {
-    pub crate_name: String,
-    pub ice_content: String,
-}
-
-impl Display for RustcIce {
-    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
-        write!(
-            f,
-            "{}:\n{}\n========================================\n",
-            self.crate_name, self.ice_content
-        )
-    }
-}
-
-impl RustcIce {
-    pub fn from_stderr_and_status(crate_name: &str, status: ExitStatus, stderr: &str) -> Option<Self> {
-        if status.code().unwrap_or(0) == 101
-        /* ice exit status */
-        {
-            Some(Self {
-                crate_name: crate_name.to_owned(),
-                ice_content: stderr.to_owned(),
-            })
-        } else {
-            None
-        }
-    }
-}
-
-/// A single warning that clippy issued while checking a `Crate`
-#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
-struct ClippyWarning {
-    crate_name: String,
-    crate_version: String,
-    lint_type: String,
-    diag: Diagnostic,
-}
-
-#[allow(unused)]
-impl ClippyWarning {
-    fn new(mut diag: Diagnostic, crate_name: &str, crate_version: &str) -> Option<Self> {
-        let lint_type = diag.code.clone()?.code;
-        if !(lint_type.contains("clippy") || diag.message.contains("clippy"))
-            || diag.message.contains("could not read cargo metadata")
-        {
-            return None;
-        }
-
-        // --recursive bypasses cargo so we have to strip the rendered output ourselves
-        let rendered = diag.rendered.as_mut().unwrap();
-        *rendered = strip_ansi_escapes::strip_str(&rendered);
-
-        Some(Self {
-            crate_name: crate_name.to_owned(),
-            crate_version: crate_version.to_owned(),
-            lint_type,
-            diag,
-        })
-    }
-
-    fn span(&self) -> &DiagnosticSpan {
-        self.diag.spans.iter().find(|span| span.is_primary).unwrap()
-    }
-
-    fn to_output(&self, format: OutputFormat) -> String {
-        let span = self.span();
-        let mut file = span.file_name.clone();
-        let file_with_pos = format!("{file}:{}:{}", span.line_start, span.line_end);
-        match format {
-            OutputFormat::Text => format!("{file_with_pos} {} \"{}\"\n", self.lint_type, self.diag.message),
-            OutputFormat::Markdown => {
-                if file.starts_with("target") {
-                    file.insert_str(0, "../");
-                }
-
-                let mut output = String::from("| ");
-                write!(output, "[`{file_with_pos}`]({file}#L{})", span.line_start).unwrap();
-                write!(output, r#" | `{:<50}` | "{}" |"#, self.lint_type, self.diag.message).unwrap();
-                output.push('\n');
-                output
-            },
-            OutputFormat::Json => unreachable!("JSON output is handled via serde"),
-        }
-    }
-}
-
-#[allow(clippy::result_large_err)]
-fn get(path: &str) -> Result<ureq::Response, ureq::Error> {
-    const MAX_RETRIES: u8 = 4;
-    let mut retries = 0;
-    loop {
-        match ureq::get(path).call() {
-            Ok(res) => return Ok(res),
-            Err(e) if retries >= MAX_RETRIES => return Err(e),
-            Err(ureq::Error::Transport(e)) => eprintln!("Error: {e}"),
-            Err(e) => return Err(e),
-        }
-        eprintln!("retrying in {retries} seconds...");
-        thread::sleep(Duration::from_secs(u64::from(retries)));
-        retries += 1;
-    }
-}
-
-impl CrateSource {
-    /// Makes the sources available on the disk for clippy to check.
-    /// Clones a git repo and checks out the specified commit or downloads a crate from crates.io or
-    /// copies a local folder
-    fn download_and_extract(&self) -> Crate {
-        match self {
-            CrateSource::CratesIo { name, version, options } => {
-                let extract_dir = PathBuf::from(LINTCHECK_SOURCES);
-                let krate_download_dir = PathBuf::from(LINTCHECK_DOWNLOADS);
-
-                // url to download the crate from crates.io
-                let url = format!("https://crates.io/api/v1/crates/{name}/{version}/download");
-                println!("Downloading and extracting {name} {version} from {url}");
-                create_dirs(&krate_download_dir, &extract_dir);
-
-                let krate_file_path = krate_download_dir.join(format!("{name}-{version}.crate.tar.gz"));
-                // don't download/extract if we already have done so
-                if !krate_file_path.is_file() {
-                    // create a file path to download and write the crate data into
-                    let mut krate_dest = fs::File::create(&krate_file_path).unwrap();
-                    let mut krate_req = get(&url).unwrap().into_reader();
-                    // copy the crate into the file
-                    io::copy(&mut krate_req, &mut krate_dest).unwrap();
-
-                    // unzip the tarball
-                    let ungz_tar = flate2::read::GzDecoder::new(fs::File::open(&krate_file_path).unwrap());
-                    // extract the tar archive
-                    let mut archive = tar::Archive::new(ungz_tar);
-                    archive.unpack(&extract_dir).expect("Failed to extract!");
-                }
-                // crate is extracted, return a new Krate object which contains the path to the extracted
-                // sources that clippy can check
-                Crate {
-                    version: version.clone(),
-                    name: name.clone(),
-                    path: extract_dir.join(format!("{name}-{version}/")),
-                    options: options.clone(),
-                }
-            },
-            CrateSource::Git {
-                name,
-                url,
-                commit,
-                options,
-            } => {
-                let repo_path = {
-                    let mut repo_path = PathBuf::from(LINTCHECK_SOURCES);
-                    // add a -git suffix in case we have the same crate from crates.io and a git repo
-                    repo_path.push(format!("{name}-git"));
-                    repo_path
-                };
-                // clone the repo if we have not done so
-                if !repo_path.is_dir() {
-                    println!("Cloning {url} and checking out {commit}");
-                    if !Command::new("git")
-                        .arg("clone")
-                        .arg(url)
-                        .arg(&repo_path)
-                        .status()
-                        .expect("Failed to clone git repo!")
-                        .success()
-                    {
-                        eprintln!("Failed to clone {url} into {}", repo_path.display());
-                    }
-                }
-                // check out the commit/branch/whatever
-                if !Command::new("git")
-                    .args(["-c", "advice.detachedHead=false"])
-                    .arg("checkout")
-                    .arg(commit)
-                    .current_dir(&repo_path)
-                    .status()
-                    .expect("Failed to check out commit")
-                    .success()
-                {
-                    eprintln!("Failed to checkout {commit} of repo at {}", repo_path.display());
-                }
-
-                Crate {
-                    version: commit.clone(),
-                    name: name.clone(),
-                    path: repo_path,
-                    options: options.clone(),
-                }
-            },
-            CrateSource::Path { name, path, options } => {
-                fn is_cache_dir(entry: &DirEntry) -> bool {
-                    fs::read(entry.path().join("CACHEDIR.TAG"))
-                        .map(|x| x.starts_with(b"Signature: 8a477f597d28d172789f06886806bc55"))
-                        .unwrap_or(false)
-                }
-
-                // copy path into the dest_crate_root but skip directories that contain a CACHEDIR.TAG file.
-                // The target/ directory contains a CACHEDIR.TAG file so it is the most commonly skipped directory
-                // as a result of this filter.
-                let dest_crate_root = PathBuf::from(LINTCHECK_SOURCES).join(name);
-                if dest_crate_root.exists() {
-                    println!("Deleting existing directory at {dest_crate_root:?}");
-                    fs::remove_dir_all(&dest_crate_root).unwrap();
-                }
-
-                println!("Copying {path:?} to {dest_crate_root:?}");
-
-                for entry in WalkDir::new(path).into_iter().filter_entry(|e| !is_cache_dir(e)) {
-                    let entry = entry.unwrap();
-                    let entry_path = entry.path();
-                    let relative_entry_path = entry_path.strip_prefix(path).unwrap();
-                    let dest_path = dest_crate_root.join(relative_entry_path);
-                    let metadata = entry_path.symlink_metadata().unwrap();
-
-                    if metadata.is_dir() {
-                        fs::create_dir(dest_path).unwrap();
-                    } else if metadata.is_file() {
-                        fs::copy(entry_path, dest_path).unwrap();
-                    }
-                }
-
-                Crate {
-                    version: String::from("local"),
-                    name: name.clone(),
-                    path: dest_crate_root,
-                    options: options.clone(),
-                }
-            },
-        }
-    }
-}
-
 impl Crate {
     /// Run `cargo clippy` on the `Crate` and collect and return all the lint warnings that clippy
     /// issued
@@ -352,7 +65,7 @@ impl Crate {
         target_dir_index: &AtomicUsize,
         total_crates_to_lint: usize,
         config: &LintcheckConfig,
-        lint_filter: &[String],
+        lint_levels_args: &[String],
         server: &Option<LintcheckServer>,
     ) -> Vec<ClippyCheckOutput> {
         // advance the atomic index by one
@@ -398,16 +111,9 @@ impl Crate {
             for opt in options {
                 clippy_args.push(opt);
             }
-        } else {
-            clippy_args.extend(["-Wclippy::pedantic", "-Wclippy::cargo"]);
         }
 
-        if lint_filter.is_empty() {
-            clippy_args.push("--cap-lints=warn");
-        } else {
-            clippy_args.push("--cap-lints=allow");
-            clippy_args.extend(lint_filter.iter().map(String::as_str));
-        }
+        clippy_args.extend(lint_levels_args.iter().map(String::as_str));
 
         let mut cmd = Command::new("cargo");
         cmd.arg(if config.fix { "fix" } else { "check" })
@@ -479,7 +185,7 @@ impl Crate {
         // get all clippy warnings and ICEs
         let mut entries: Vec<ClippyCheckOutput> = Message::parse_stream(stdout.as_bytes())
             .filter_map(|msg| match msg {
-                Ok(Message::CompilerMessage(message)) => ClippyWarning::new(message.message, &self.name, &self.version),
+                Ok(Message::CompilerMessage(message)) => ClippyWarning::new(message.message),
                 _ => None,
             })
             .map(ClippyCheckOutput::ClippyWarning)
@@ -509,96 +215,6 @@ fn build_clippy() -> String {
     String::from_utf8_lossy(&output.stdout).into_owned()
 }
 
-/// Read a `lintcheck_crates.toml` file
-fn read_crates(toml_path: &Path) -> (Vec<CrateSource>, RecursiveOptions) {
-    let toml_content: String =
-        fs::read_to_string(toml_path).unwrap_or_else(|_| panic!("Failed to read {}", toml_path.display()));
-    let crate_list: SourceList =
-        toml::from_str(&toml_content).unwrap_or_else(|e| panic!("Failed to parse {}: \n{e}", toml_path.display()));
-    // parse the hashmap of the toml file into a list of crates
-    let tomlcrates: Vec<TomlCrate> = crate_list.crates.into_values().collect();
-
-    // flatten TomlCrates into CrateSources (one TomlCrates may represent several versions of a crate =>
-    // multiple Cratesources)
-    let mut crate_sources = Vec::new();
-    for tk in tomlcrates {
-        if let Some(ref path) = tk.path {
-            crate_sources.push(CrateSource::Path {
-                name: tk.name.clone(),
-                path: PathBuf::from(path),
-                options: tk.options.clone(),
-            });
-        } else if let Some(ref versions) = tk.versions {
-            // if we have multiple versions, save each one
-            for ver in versions {
-                crate_sources.push(CrateSource::CratesIo {
-                    name: tk.name.clone(),
-                    version: ver.to_string(),
-                    options: tk.options.clone(),
-                });
-            }
-        } else if tk.git_url.is_some() && tk.git_hash.is_some() {
-            // otherwise, we should have a git source
-            crate_sources.push(CrateSource::Git {
-                name: tk.name.clone(),
-                url: tk.git_url.clone().unwrap(),
-                commit: tk.git_hash.clone().unwrap(),
-                options: tk.options.clone(),
-            });
-        } else {
-            panic!("Invalid crate source: {tk:?}");
-        }
-
-        // if we have a version as well as a git data OR only one git data, something is funky
-        if tk.versions.is_some() && (tk.git_url.is_some() || tk.git_hash.is_some())
-            || tk.git_hash.is_some() != tk.git_url.is_some()
-        {
-            eprintln!("tomlkrate: {tk:?}");
-            assert_eq!(
-                tk.git_hash.is_some(),
-                tk.git_url.is_some(),
-                "Error: Encountered TomlCrate with only one of git_hash and git_url!"
-            );
-            assert!(
-                tk.path.is_none() || (tk.git_hash.is_none() && tk.versions.is_none()),
-                "Error: TomlCrate can only have one of 'git_.*', 'version' or 'path' fields"
-            );
-            unreachable!("Failed to translate TomlCrate into CrateSource!");
-        }
-    }
-    // sort the crates
-    crate_sources.sort();
-
-    (crate_sources, crate_list.recursive)
-}
-
-/// Generate a short list of occurring lints-types and their count
-fn gather_stats(warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) {
-    // count lint type occurrences
-    let mut counter: HashMap<&String, usize> = HashMap::new();
-    warnings
-        .iter()
-        .for_each(|wrn| *counter.entry(&wrn.lint_type).or_insert(0) += 1);
-
-    // collect into a tupled list for sorting
-    let mut stats: Vec<(&&String, &usize)> = counter.iter().collect();
-    // sort by "000{count} {clippy::lintname}"
-    // to not have a lint with 200 and 2 warnings take the same spot
-    stats.sort_by_key(|(lint, count)| format!("{count:0>4}, {lint}"));
-
-    let mut header = String::from("| lint                                               | count |\n");
-    header.push_str("| -------------------------------------------------- | ----- |\n");
-    let stats_string = stats
-        .iter()
-        .map(|(lint, count)| format!("| {lint:<50} |  {count:>4} |\n"))
-        .fold(header, |mut table, line| {
-            table.push_str(&line);
-            table
-        });
-
-    (stats_string, counter)
-}
-
 fn main() {
     // We're being executed as a `RUSTC_WRAPPER` as part of `--recursive`
     if let Ok(addr) = env::var("LINTCHECK_SERVER") {
@@ -638,15 +254,39 @@ fn lintcheck(config: LintcheckConfig) {
     let (crates, recursive_options) = read_crates(&config.sources_toml_path);
 
     let counter = AtomicUsize::new(1);
-    let lint_filter: Vec<String> = config
-        .lint_filter
-        .iter()
-        .map(|filter| {
-            let mut filter = filter.clone();
-            filter.insert_str(0, "--force-warn=");
-            filter
-        })
-        .collect();
+    let mut lint_level_args: Vec<String> = vec![];
+    if config.lint_filter.is_empty() {
+        lint_level_args.push("--cap-lints=warn".to_string());
+
+        // Set allow-by-default to warn
+        if config.warn_all {
+            [
+                "clippy::cargo",
+                "clippy::nursery",
+                "clippy::pedantic",
+                "clippy::restriction",
+            ]
+            .iter()
+            .map(|group| format!("--warn={group}"))
+            .collect_into(&mut lint_level_args);
+        } else {
+            ["clippy::cargo", "clippy::pedantic"]
+                .iter()
+                .map(|group| format!("--warn={group}"))
+                .collect_into(&mut lint_level_args);
+        }
+    } else {
+        lint_level_args.push("--cap-lints=allow".to_string());
+        config
+            .lint_filter
+            .iter()
+            .map(|filter| {
+                let mut filter = filter.clone();
+                filter.insert_str(0, "--force-warn=");
+                filter
+            })
+            .collect_into(&mut lint_level_args);
+    };
 
     let crates: Vec<Crate> = crates
         .into_iter()
@@ -698,7 +338,7 @@ fn lintcheck(config: LintcheckConfig) {
                 &counter,
                 crates.len(),
                 &config,
-                &lint_filter,
+                &lint_level_args,
                 &server,
             )
         })
@@ -727,7 +367,9 @@ fn lintcheck(config: LintcheckConfig) {
     }
 
     let text = match config.format {
-        OutputFormat::Text | OutputFormat::Markdown => output(&warnings, &raw_ices, clippy_ver, &config),
+        OutputFormat::Text | OutputFormat::Markdown => {
+            output::summarize_and_print_changes(&warnings, &raw_ices, clippy_ver, &config)
+        },
         OutputFormat::Json => {
             if !raw_ices.is_empty() {
                 for ice in raw_ices {
@@ -736,7 +378,7 @@ fn lintcheck(config: LintcheckConfig) {
                 panic!("Some crates ICEd");
             }
 
-            json::output(&warnings)
+            json::output(warnings)
         },
     };
 
@@ -745,135 +387,6 @@ fn lintcheck(config: LintcheckConfig) {
     fs::write(&config.lintcheck_results_path, text).unwrap();
 }
 
-/// Creates the log file output for [`OutputFormat::Text`] and [`OutputFormat::Markdown`]
-fn output(warnings: &[ClippyWarning], ices: &[RustcIce], clippy_ver: String, config: &LintcheckConfig) -> String {
-    // generate some stats
-    let (stats_formatted, new_stats) = gather_stats(warnings);
-    let old_stats = read_stats_from_file(&config.lintcheck_results_path);
-
-    let mut all_msgs: Vec<String> = warnings.iter().map(|warn| warn.to_output(config.format)).collect();
-    all_msgs.sort();
-    all_msgs.push("\n\n### Stats:\n\n".into());
-    all_msgs.push(stats_formatted);
-
-    let mut text = clippy_ver; // clippy version number on top
-    text.push_str("\n### Reports\n\n");
-    if config.format == OutputFormat::Markdown {
-        text.push_str("| file | lint | message |\n");
-        text.push_str("| --- | --- | --- |\n");
-    }
-    write!(text, "{}", all_msgs.join("")).unwrap();
-    text.push_str("\n\n### ICEs:\n");
-    for ice in ices {
-        writeln!(text, "{ice}").unwrap();
-    }
-
-    print_stats(old_stats, new_stats, &config.lint_filter);
-
-    text
-}
-
-/// read the previous stats from the lintcheck-log file
-fn read_stats_from_file(file_path: &Path) -> HashMap<String, usize> {
-    let file_content: String = match fs::read_to_string(file_path).ok() {
-        Some(content) => content,
-        None => {
-            return HashMap::new();
-        },
-    };
-
-    let lines: Vec<String> = file_content.lines().map(ToString::to_string).collect();
-
-    lines
-        .iter()
-        .skip_while(|line| line.as_str() != "### Stats:")
-        // Skipping the table header and the `Stats:` label
-        .skip(4)
-        .take_while(|line| line.starts_with("| "))
-        .filter_map(|line| {
-            let mut spl = line.split('|');
-            // Skip the first `|` symbol
-            spl.next();
-            if let (Some(lint), Some(count)) = (spl.next(), spl.next()) {
-                Some((lint.trim().to_string(), count.trim().parse::<usize>().unwrap()))
-            } else {
-                None
-            }
-        })
-        .collect::<HashMap<String, usize>>()
-}
-
-/// print how lint counts changed between runs
-fn print_stats(old_stats: HashMap<String, usize>, new_stats: HashMap<&String, usize>, lint_filter: &[String]) {
-    let same_in_both_hashmaps = old_stats
-        .iter()
-        .filter(|(old_key, old_val)| new_stats.get::<&String>(old_key) == Some(old_val))
-        .map(|(k, v)| (k.to_string(), *v))
-        .collect::<Vec<(String, usize)>>();
-
-    let mut old_stats_deduped = old_stats;
-    let mut new_stats_deduped = new_stats;
-
-    // remove duplicates from both hashmaps
-    for (k, v) in &same_in_both_hashmaps {
-        assert!(old_stats_deduped.remove(k) == Some(*v));
-        assert!(new_stats_deduped.remove(k) == Some(*v));
-    }
-
-    println!("\nStats:");
-
-    // list all new counts  (key is in new stats but not in old stats)
-    new_stats_deduped
-        .iter()
-        .filter(|(new_key, _)| !old_stats_deduped.contains_key::<str>(new_key))
-        .for_each(|(new_key, new_value)| {
-            println!("{new_key} 0 => {new_value}");
-        });
-
-    // list all changed counts (key is in both maps but value differs)
-    new_stats_deduped
-        .iter()
-        .filter(|(new_key, _new_val)| old_stats_deduped.contains_key::<str>(new_key))
-        .for_each(|(new_key, new_val)| {
-            let old_val = old_stats_deduped.get::<str>(new_key).unwrap();
-            println!("{new_key} {old_val} => {new_val}");
-        });
-
-    // list all gone counts (key is in old status but not in new stats)
-    old_stats_deduped
-        .iter()
-        .filter(|(old_key, _)| !new_stats_deduped.contains_key::<&String>(old_key))
-        .filter(|(old_key, _)| lint_filter.is_empty() || lint_filter.contains(old_key))
-        .for_each(|(old_key, old_value)| {
-            println!("{old_key} {old_value} => 0");
-        });
-}
-
-/// Create necessary directories to run the lintcheck tool.
-///
-/// # Panics
-///
-/// This function panics if creating one of the dirs fails.
-fn create_dirs(krate_download_dir: &Path, extract_dir: &Path) {
-    fs::create_dir("target/lintcheck/").unwrap_or_else(|err| {
-        assert_eq!(
-            err.kind(),
-            ErrorKind::AlreadyExists,
-            "cannot create lintcheck target dir"
-        );
-    });
-    fs::create_dir(krate_download_dir).unwrap_or_else(|err| {
-        assert_eq!(err.kind(), ErrorKind::AlreadyExists, "cannot create crate download dir");
-    });
-    fs::create_dir(extract_dir).unwrap_or_else(|err| {
-        assert_eq!(
-            err.kind(),
-            ErrorKind::AlreadyExists,
-            "cannot create crate extraction dir"
-        );
-    });
-}
-
 /// Returns the path to the Clippy project directory
 #[must_use]
 fn clippy_project_root() -> &'static Path {
diff --git a/src/tools/clippy/lintcheck/src/output.rs b/src/tools/clippy/lintcheck/src/output.rs
new file mode 100644
index 00000000000..4bfc554ef9e
--- /dev/null
+++ b/src/tools/clippy/lintcheck/src/output.rs
@@ -0,0 +1,235 @@
+use cargo_metadata::diagnostic::{Diagnostic, DiagnosticSpan};
+use serde::{Deserialize, Serialize};
+use std::collections::HashMap;
+use std::fmt::{self, Write as _};
+use std::fs;
+use std::path::Path;
+use std::process::ExitStatus;
+
+use crate::config::{LintcheckConfig, OutputFormat};
+
+/// A single emitted output from clippy being executed on a crate. It may either be a
+/// `ClippyWarning`, or a `RustcIce` caused by a panic within clippy. A crate may have many
+/// `ClippyWarning`s but a maximum of one `RustcIce` (at which point clippy halts execution).
+#[derive(Debug)]
+pub enum ClippyCheckOutput {
+    ClippyWarning(ClippyWarning),
+    RustcIce(RustcIce),
+}
+
+#[derive(Debug)]
+pub struct RustcIce {
+    pub crate_name: String,
+    pub ice_content: String,
+}
+
+impl fmt::Display for RustcIce {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(
+            f,
+            "{}:\n{}\n========================================\n",
+            self.crate_name, self.ice_content
+        )
+    }
+}
+
+impl RustcIce {
+    pub fn from_stderr_and_status(crate_name: &str, status: ExitStatus, stderr: &str) -> Option<Self> {
+        if status.code().unwrap_or(0) == 101
+        /* ice exit status */
+        {
+            Some(Self {
+                crate_name: crate_name.to_owned(),
+                ice_content: stderr.to_owned(),
+            })
+        } else {
+            None
+        }
+    }
+}
+
+/// A single warning that clippy issued while checking a `Crate`
+#[derive(Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
+pub struct ClippyWarning {
+    pub lint: String,
+    pub diag: Diagnostic,
+}
+
+#[allow(unused)]
+impl ClippyWarning {
+    pub fn new(mut diag: Diagnostic) -> Option<Self> {
+        let lint = diag.code.clone()?.code;
+        if !(lint.contains("clippy") || diag.message.contains("clippy"))
+            || diag.message.contains("could not read cargo metadata")
+        {
+            return None;
+        }
+
+        // --recursive bypasses cargo so we have to strip the rendered output ourselves
+        let rendered = diag.rendered.as_mut().unwrap();
+        *rendered = strip_ansi_escapes::strip_str(&rendered);
+
+        Some(Self { lint, diag })
+    }
+
+    pub fn span(&self) -> &DiagnosticSpan {
+        self.diag.spans.iter().find(|span| span.is_primary).unwrap()
+    }
+
+    pub fn to_output(&self, format: OutputFormat) -> String {
+        let span = self.span();
+        let mut file = span.file_name.clone();
+        let file_with_pos = format!("{file}:{}:{}", span.line_start, span.line_end);
+        match format {
+            OutputFormat::Text => format!("{file_with_pos} {} \"{}\"\n", self.lint, self.diag.message),
+            OutputFormat::Markdown => {
+                if file.starts_with("target") {
+                    file.insert_str(0, "../");
+                }
+
+                let mut output = String::from("| ");
+                write!(output, "[`{file_with_pos}`]({file}#L{})", span.line_start).unwrap();
+                write!(output, r#" | `{:<50}` | "{}" |"#, self.lint, self.diag.message).unwrap();
+                output.push('\n');
+                output
+            },
+            OutputFormat::Json => unreachable!("JSON output is handled via serde"),
+        }
+    }
+}
+
+/// Creates the log file output for [`OutputFormat::Text`] and [`OutputFormat::Markdown`]
+pub fn summarize_and_print_changes(
+    warnings: &[ClippyWarning],
+    ices: &[RustcIce],
+    clippy_ver: String,
+    config: &LintcheckConfig,
+) -> String {
+    // generate some stats
+    let (stats_formatted, new_stats) = gather_stats(warnings);
+    let old_stats = read_stats_from_file(&config.lintcheck_results_path);
+
+    let mut all_msgs: Vec<String> = warnings.iter().map(|warn| warn.to_output(config.format)).collect();
+    all_msgs.sort();
+    all_msgs.push("\n\n### Stats:\n\n".into());
+    all_msgs.push(stats_formatted);
+
+    let mut text = clippy_ver; // clippy version number on top
+    text.push_str("\n### Reports\n\n");
+    if config.format == OutputFormat::Markdown {
+        text.push_str("| file | lint | message |\n");
+        text.push_str("| --- | --- | --- |\n");
+    }
+    write!(text, "{}", all_msgs.join("")).unwrap();
+    text.push_str("\n\n### ICEs:\n");
+    for ice in ices {
+        writeln!(text, "{ice}").unwrap();
+    }
+
+    print_stats(old_stats, new_stats, &config.lint_filter);
+
+    text
+}
+
+/// Generate a short list of occurring lints-types and their count
+fn gather_stats(warnings: &[ClippyWarning]) -> (String, HashMap<&String, usize>) {
+    // count lint type occurrences
+    let mut counter: HashMap<&String, usize> = HashMap::new();
+    warnings
+        .iter()
+        .for_each(|wrn| *counter.entry(&wrn.lint).or_insert(0) += 1);
+
+    // collect into a tupled list for sorting
+    let mut stats: Vec<(&&String, &usize)> = counter.iter().collect();
+    // sort by "000{count} {clippy::lintname}"
+    // to not have a lint with 200 and 2 warnings take the same spot
+    stats.sort_by_key(|(lint, count)| format!("{count:0>4}, {lint}"));
+
+    let mut header = String::from("| lint                                               | count |\n");
+    header.push_str("| -------------------------------------------------- | ----- |\n");
+    let stats_string = stats
+        .iter()
+        .map(|(lint, count)| format!("| {lint:<50} |  {count:>4} |\n"))
+        .fold(header, |mut table, line| {
+            table.push_str(&line);
+            table
+        });
+
+    (stats_string, counter)
+}
+
+/// read the previous stats from the lintcheck-log file
+fn read_stats_from_file(file_path: &Path) -> HashMap<String, usize> {
+    let file_content: String = match fs::read_to_string(file_path).ok() {
+        Some(content) => content,
+        None => {
+            return HashMap::new();
+        },
+    };
+
+    let lines: Vec<String> = file_content.lines().map(ToString::to_string).collect();
+
+    lines
+        .iter()
+        .skip_while(|line| line.as_str() != "### Stats:")
+        // Skipping the table header and the `Stats:` label
+        .skip(4)
+        .take_while(|line| line.starts_with("| "))
+        .filter_map(|line| {
+            let mut spl = line.split('|');
+            // Skip the first `|` symbol
+            spl.next();
+            if let (Some(lint), Some(count)) = (spl.next(), spl.next()) {
+                Some((lint.trim().to_string(), count.trim().parse::<usize>().unwrap()))
+            } else {
+                None
+            }
+        })
+        .collect::<HashMap<String, usize>>()
+}
+
+/// print how lint counts changed between runs
+fn print_stats(old_stats: HashMap<String, usize>, new_stats: HashMap<&String, usize>, lint_filter: &[String]) {
+    let same_in_both_hashmaps = old_stats
+        .iter()
+        .filter(|(old_key, old_val)| new_stats.get::<&String>(old_key) == Some(old_val))
+        .map(|(k, v)| (k.to_string(), *v))
+        .collect::<Vec<(String, usize)>>();
+
+    let mut old_stats_deduped = old_stats;
+    let mut new_stats_deduped = new_stats;
+
+    // remove duplicates from both hashmaps
+    for (k, v) in &same_in_both_hashmaps {
+        assert!(old_stats_deduped.remove(k) == Some(*v));
+        assert!(new_stats_deduped.remove(k) == Some(*v));
+    }
+
+    println!("\nStats:");
+
+    // list all new counts  (key is in new stats but not in old stats)
+    new_stats_deduped
+        .iter()
+        .filter(|(new_key, _)| !old_stats_deduped.contains_key::<str>(new_key))
+        .for_each(|(new_key, new_value)| {
+            println!("{new_key} 0 => {new_value}");
+        });
+
+    // list all changed counts (key is in both maps but value differs)
+    new_stats_deduped
+        .iter()
+        .filter(|(new_key, _new_val)| old_stats_deduped.contains_key::<str>(new_key))
+        .for_each(|(new_key, new_val)| {
+            let old_val = old_stats_deduped.get::<str>(new_key).unwrap();
+            println!("{new_key} {old_val} => {new_val}");
+        });
+
+    // list all gone counts (key is in old status but not in new stats)
+    old_stats_deduped
+        .iter()
+        .filter(|(old_key, _)| !new_stats_deduped.contains_key::<&String>(old_key))
+        .filter(|(old_key, _)| lint_filter.is_empty() || lint_filter.contains(old_key))
+        .for_each(|(old_key, old_value)| {
+            println!("{old_key} {old_value} => 0");
+        });
+}
diff --git a/src/tools/clippy/lintcheck/src/popular_crates.rs b/src/tools/clippy/lintcheck/src/popular_crates.rs
index 880a8bd81f0..ad8fc440c42 100644
--- a/src/tools/clippy/lintcheck/src/popular_crates.rs
+++ b/src/tools/clippy/lintcheck/src/popular_crates.rs
@@ -44,7 +44,7 @@ pub(crate) fn fetch(output: PathBuf, number: usize) -> Result<(), Box<dyn Error>
 
     let mut out = "[crates]\n".to_string();
     for Crate { name, max_version } in crates {
-        writeln!(out, "{name} = {{ name = '{name}', versions = ['{max_version}'] }}").unwrap();
+        writeln!(out, "{name} = {{ name = '{name}', version = '{max_version}' }}").unwrap();
     }
     fs::write(output, out)?;
 
diff --git a/src/tools/clippy/lintcheck/src/recursive.rs b/src/tools/clippy/lintcheck/src/recursive.rs
index 994fa3c3b23..373ca6f9918 100644
--- a/src/tools/clippy/lintcheck/src/recursive.rs
+++ b/src/tools/clippy/lintcheck/src/recursive.rs
@@ -3,7 +3,8 @@
 //! [`LintcheckServer`] to ask if it should be skipped, and if not sends the stderr of running
 //! clippy on the crate to the server
 
-use crate::{ClippyWarning, RecursiveOptions};
+use crate::input::RecursiveOptions;
+use crate::ClippyWarning;
 
 use std::collections::HashSet;
 use std::io::{BufRead, BufReader, Read, Write};
@@ -19,8 +20,6 @@ use serde::{Deserialize, Serialize};
 #[derive(Debug, Eq, Hash, PartialEq, Clone, Serialize, Deserialize)]
 pub(crate) struct DriverInfo {
     pub package_name: String,
-    pub crate_name: String,
-    pub version: String,
 }
 
 pub(crate) fn serialize_line<T, W>(value: &T, writer: &mut W)
@@ -65,7 +64,7 @@ fn process_stream(
     let messages = stderr
         .lines()
         .filter_map(|json_msg| serde_json::from_str::<Diagnostic>(json_msg).ok())
-        .filter_map(|diag| ClippyWarning::new(diag, &driver_info.package_name, &driver_info.version));
+        .filter_map(ClippyWarning::new);
 
     for message in messages {
         sender.send(message).unwrap();
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index 72b50d59f7e..a61c22c59f9 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,3 +1,4 @@
 [toolchain]
-channel = "nightly-2024-06-27"
+channel = "nightly-2024-07-11"
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
+profile = "minimal"
diff --git a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr
index 1be4b665bcb..66eda44f745 100644
--- a/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr
+++ b/src/tools/clippy/tests/ui-internal/disallow_span_lint.stderr
@@ -1,22 +1,18 @@
 error: use of a disallowed method `rustc_lint::context::LintContext::span_lint`
-  --> tests/ui-internal/disallow_span_lint.rs:14:5
+  --> tests/ui-internal/disallow_span_lint.rs:14:8
    |
-LL | /     cx.span_lint(lint, span, |lint| {
-LL | |         lint.primary_message(msg);
-LL | |     });
-   | |______^
+LL |     cx.span_lint(lint, span, |lint| {
+   |        ^^^^^^^^^
    |
    = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint*` functions instead (from clippy.toml)
    = note: `-D clippy::disallowed-methods` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]`
 
 error: use of a disallowed method `rustc_middle::ty::context::TyCtxt::node_span_lint`
-  --> tests/ui-internal/disallow_span_lint.rs:20:5
+  --> tests/ui-internal/disallow_span_lint.rs:20:9
    |
-LL | /     tcx.node_span_lint(lint, hir_id, span, |lint| {
-LL | |         lint.primary_message(msg);
-LL | |     });
-   | |______^
+LL |     tcx.node_span_lint(lint, hir_id, span, |lint| {
+   |         ^^^^^^^^^^^^^^
    |
    = note: this function does not add a link to our documentation, please use the `clippy_utils::diagnostics::span_lint_hir*` functions instead (from clippy.toml)
 
diff --git a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr
index a74d8757e4a..016ee502c24 100644
--- a/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr
+++ b/src/tools/clippy/tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.stderr
@@ -1,4 +1,4 @@
-error: `std::string::String` may not be held across an `await` point per `clippy.toml`
+error: `std::string::String` may not be held across an await point per `clippy.toml`
   --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:5:9
    |
 LL |     let _x = String::from("hello");
@@ -8,13 +8,13 @@ LL |     let _x = String::from("hello");
    = note: `-D clippy::await-holding-invalid-type` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::await_holding_invalid_type)]`
 
-error: `std::net::Ipv4Addr` may not be held across an `await` point per `clippy.toml`
+error: `std::net::Ipv4Addr` may not be held across an await point per `clippy.toml`
   --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:10:9
    |
 LL |     let x = Ipv4Addr::new(127, 0, 0, 1);
    |         ^
 
-error: `std::string::String` may not be held across an `await` point per `clippy.toml`
+error: `std::string::String` may not be held across an await point per `clippy.toml`
   --> tests/ui-toml/await_holding_invalid_type/await_holding_invalid_type.rs:33:13
    |
 LL |         let _x = String::from("hi!");
diff --git a/src/tools/clippy/tests/ui-toml/needless_pass_by_ref_mut/clippy.toml b/src/tools/clippy/tests/ui-toml/needless_pass_by_ref_mut/clippy.toml
new file mode 100644
index 00000000000..cda8d17eed4
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/needless_pass_by_ref_mut/clippy.toml
@@ -0,0 +1 @@
+avoid-breaking-exported-api = false
diff --git a/src/tools/clippy/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.fixed b/src/tools/clippy/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.fixed
new file mode 100644
index 00000000000..40556ca5410
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.fixed
@@ -0,0 +1,10 @@
+#![warn(clippy::needless_pass_by_ref_mut)]
+#![allow(clippy::ptr_arg)]
+
+// Should warn
+pub fn pub_foo(s: &Vec<u32>, b: &u32, x: &mut u32) {
+    //~^ ERROR: this argument is a mutable reference, but not used mutably
+    *x += *b + s.len() as u32;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.rs b/src/tools/clippy/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.rs
new file mode 100644
index 00000000000..bbc63ceb15a
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.rs
@@ -0,0 +1,10 @@
+#![warn(clippy::needless_pass_by_ref_mut)]
+#![allow(clippy::ptr_arg)]
+
+// Should warn
+pub fn pub_foo(s: &mut Vec<u32>, b: &u32, x: &mut u32) {
+    //~^ ERROR: this argument is a mutable reference, but not used mutably
+    *x += *b + s.len() as u32;
+}
+
+fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.stderr b/src/tools/clippy/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.stderr
new file mode 100644
index 00000000000..c10607bf4ba
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.stderr
@@ -0,0 +1,12 @@
+error: this argument is a mutable reference, but not used mutably
+  --> tests/ui-toml/needless_pass_by_ref_mut/needless_pass_by_ref_mut.rs:5:19
+   |
+LL | pub fn pub_foo(s: &mut Vec<u32>, b: &u32, x: &mut u32) {
+   |                   ^^^^^^^^^^^^^ help: consider changing to: `&Vec<u32>`
+   |
+   = warning: changing this function will impact semver compatibility
+   = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]`
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr
index 4afbbf5f807..f661e76cc74 100644
--- a/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr
+++ b/src/tools/clippy/tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.stderr
@@ -2,36 +2,36 @@ error: use of a disallowed method `regex::Regex::new`
   --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:35:14
    |
 LL |     let re = Regex::new(r"ab.*c").unwrap();
-   |              ^^^^^^^^^^^^^^^^^^^^
+   |              ^^^^^^^^^^
    |
    = note: `-D clippy::disallowed-methods` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::disallowed_methods)]`
 
 error: use of a disallowed method `regex::Regex::is_match`
-  --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:36:5
+  --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:36:8
    |
 LL |     re.is_match("abc");
-   |     ^^^^^^^^^^^^^^^^^^
+   |        ^^^^^^^^
    |
    = note: no matching allowed (from clippy.toml)
 
 error: use of a disallowed method `std::iter::Iterator::sum`
-  --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:39:5
+  --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:39:14
    |
 LL |     a.iter().sum::<i32>();
-   |     ^^^^^^^^^^^^^^^^^^^^^
+   |              ^^^
 
 error: use of a disallowed method `slice::sort_unstable`
-  --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:41:5
+  --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:41:7
    |
 LL |     a.sort_unstable();
-   |     ^^^^^^^^^^^^^^^^^
+   |       ^^^^^^^^^^^^^
 
 error: use of a disallowed method `f32::clamp`
-  --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:44:13
+  --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:44:20
    |
 LL |     let _ = 2.0f32.clamp(3.0f32, 4.0f32);
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                    ^^^^^
 
 error: use of a disallowed method `regex::Regex::new`
   --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:47:61
@@ -55,37 +55,37 @@ error: use of a disallowed method `futures::stream::select_all`
   --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:54:31
    |
 LL |     let same_name_as_module = select_all(vec![empty::<()>()]);
-   |                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |                               ^^^^^^^^^^
 
 error: use of a disallowed method `conf_disallowed_methods::local_fn`
   --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:56:5
    |
 LL |     local_fn();
-   |     ^^^^^^^^^^
+   |     ^^^^^^^^
 
 error: use of a disallowed method `conf_disallowed_methods::local_mod::f`
   --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:57:5
    |
 LL |     local_mod::f();
-   |     ^^^^^^^^^^^^^^
+   |     ^^^^^^^^^^^^
 
 error: use of a disallowed method `conf_disallowed_methods::Struct::method`
-  --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:59:5
+  --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:59:7
    |
 LL |     s.method();
-   |     ^^^^^^^^^^
+   |       ^^^^^^
 
 error: use of a disallowed method `conf_disallowed_methods::Trait::provided_method`
-  --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:60:5
+  --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:60:7
    |
 LL |     s.provided_method();
-   |     ^^^^^^^^^^^^^^^^^^^
+   |       ^^^^^^^^^^^^^^^
 
 error: use of a disallowed method `conf_disallowed_methods::Trait::implemented_method`
-  --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:61:5
+  --> tests/ui-toml/toml_disallowed_methods/conf_disallowed_methods.rs:61:7
    |
 LL |     s.implemented_method();
-   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |       ^^^^^^^^^^^^^^^^^^
 
 error: aborting due to 14 previous errors
 
diff --git a/src/tools/clippy/tests/ui/assertions_on_constants.rs b/src/tools/clippy/tests/ui/assertions_on_constants.rs
index 1309ae45d0a..957154e60de 100644
--- a/src/tools/clippy/tests/ui/assertions_on_constants.rs
+++ b/src/tools/clippy/tests/ui/assertions_on_constants.rs
@@ -1,4 +1,4 @@
-#![allow(non_fmt_panics, clippy::needless_bool)]
+#![allow(non_fmt_panics, clippy::needless_bool, clippy::eq_op)]
 
 macro_rules! assert_const {
     ($len:expr) => {
@@ -49,7 +49,16 @@ fn main() {
     const _: () = assert!(true);
     //~^ ERROR: `assert!(true)` will be optimized out by the compiler
 
+    assert!(8 == (7 + 1));
+    //~^ ERROR: `assert!(true)` will be optimized out by the compiler
+
     // Don't lint if the value is dependent on a defined constant:
     const N: usize = 1024;
     const _: () = assert!(N.is_power_of_two());
 }
+
+const _: () = {
+    assert!(true);
+    //~^ ERROR: `assert!(true)` will be optimized out by the compiler
+    assert!(8 == (7 + 1));
+};
diff --git a/src/tools/clippy/tests/ui/assertions_on_constants.stderr b/src/tools/clippy/tests/ui/assertions_on_constants.stderr
index 00f117c9492..e164a999c43 100644
--- a/src/tools/clippy/tests/ui/assertions_on_constants.stderr
+++ b/src/tools/clippy/tests/ui/assertions_on_constants.stderr
@@ -80,5 +80,21 @@ LL |     const _: () = assert!(true);
    |
    = help: remove it
 
-error: aborting due to 10 previous errors
+error: `assert!(true)` will be optimized out by the compiler
+  --> tests/ui/assertions_on_constants.rs:52:5
+   |
+LL |     assert!(8 == (7 + 1));
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: remove it
+
+error: `assert!(true)` will be optimized out by the compiler
+  --> tests/ui/assertions_on_constants.rs:61:5
+   |
+LL |     assert!(true);
+   |     ^^^^^^^^^^^^^
+   |
+   = help: remove it
+
+error: aborting due to 12 previous errors
 
diff --git a/src/tools/clippy/tests/ui/await_holding_lock.rs b/src/tools/clippy/tests/ui/await_holding_lock.rs
index 8e5510e6cd0..cecf00c934f 100644
--- a/src/tools/clippy/tests/ui/await_holding_lock.rs
+++ b/src/tools/clippy/tests/ui/await_holding_lock.rs
@@ -8,7 +8,7 @@ mod std_mutex {
 
     pub async fn bad(x: &Mutex<u32>) -> u32 {
         let guard = x.lock().unwrap();
-        //~^ ERROR: this `MutexGuard` is held across an `await` point
+        //~^ ERROR: this `MutexGuard` is held across an await point
         baz().await
     }
 
@@ -24,13 +24,13 @@ mod std_mutex {
 
     pub async fn bad_rw(x: &RwLock<u32>) -> u32 {
         let guard = x.read().unwrap();
-        //~^ ERROR: this `MutexGuard` is held across an `await` point
+        //~^ ERROR: this `MutexGuard` is held across an await point
         baz().await
     }
 
     pub async fn bad_rw_write(x: &RwLock<u32>) -> u32 {
         let mut guard = x.write().unwrap();
-        //~^ ERROR: this `MutexGuard` is held across an `await` point
+        //~^ ERROR: this `MutexGuard` is held across an await point
         baz().await
     }
 
@@ -52,7 +52,7 @@ mod std_mutex {
         let first = baz().await;
 
         let guard = x.lock().unwrap();
-        //~^ ERROR: this `MutexGuard` is held across an `await` point
+        //~^ ERROR: this `MutexGuard` is held across an await point
 
         let second = baz().await;
 
@@ -66,7 +66,7 @@ mod std_mutex {
 
         let second = {
             let guard = x.lock().unwrap();
-            //~^ ERROR: this `MutexGuard` is held across an `await` point
+            //~^ ERROR: this `MutexGuard` is held across an await point
             baz().await
         };
 
@@ -79,7 +79,7 @@ mod std_mutex {
     pub fn block_bad(x: &Mutex<u32>) -> impl std::future::Future<Output = u32> + '_ {
         async move {
             let guard = x.lock().unwrap();
-            //~^ ERROR: this `MutexGuard` is held across an `await` point
+            //~^ ERROR: this `MutexGuard` is held across an await point
             baz().await
         }
     }
@@ -92,7 +92,7 @@ mod parking_lot_mutex {
 
     pub async fn bad(x: &Mutex<u32>) -> u32 {
         let guard = x.lock();
-        //~^ ERROR: this `MutexGuard` is held across an `await` point
+        //~^ ERROR: this `MutexGuard` is held across an await point
         baz().await
     }
 
@@ -108,13 +108,13 @@ mod parking_lot_mutex {
 
     pub async fn bad_rw(x: &RwLock<u32>) -> u32 {
         let guard = x.read();
-        //~^ ERROR: this `MutexGuard` is held across an `await` point
+        //~^ ERROR: this `MutexGuard` is held across an await point
         baz().await
     }
 
     pub async fn bad_rw_write(x: &RwLock<u32>) -> u32 {
         let mut guard = x.write();
-        //~^ ERROR: this `MutexGuard` is held across an `await` point
+        //~^ ERROR: this `MutexGuard` is held across an await point
         baz().await
     }
 
@@ -136,7 +136,7 @@ mod parking_lot_mutex {
         let first = baz().await;
 
         let guard = x.lock();
-        //~^ ERROR: this `MutexGuard` is held across an `await` point
+        //~^ ERROR: this `MutexGuard` is held across an await point
 
         let second = baz().await;
 
@@ -150,7 +150,7 @@ mod parking_lot_mutex {
 
         let second = {
             let guard = x.lock();
-            //~^ ERROR: this `MutexGuard` is held across an `await` point
+            //~^ ERROR: this `MutexGuard` is held across an await point
             baz().await
         };
 
@@ -163,7 +163,7 @@ mod parking_lot_mutex {
     pub fn block_bad(x: &Mutex<u32>) -> impl std::future::Future<Output = u32> + '_ {
         async move {
             let guard = x.lock();
-            //~^ ERROR: this `MutexGuard` is held across an `await` point
+            //~^ ERROR: this `MutexGuard` is held across an await point
             baz().await
         }
     }
@@ -184,7 +184,7 @@ async fn no_await(x: std::sync::Mutex<u32>) {
 // `*guard += 1` is removed it is picked up.
 async fn dropped_before_await(x: std::sync::Mutex<u32>) {
     let mut guard = x.lock().unwrap();
-    //~^ ERROR: this `MutexGuard` is held across an `await` point
+    //~^ ERROR: this `MutexGuard` is held across an await point
     *guard += 1;
     drop(guard);
     baz().await;
diff --git a/src/tools/clippy/tests/ui/await_holding_lock.stderr b/src/tools/clippy/tests/ui/await_holding_lock.stderr
index 0af48a36acc..af61d893948 100644
--- a/src/tools/clippy/tests/ui/await_holding_lock.stderr
+++ b/src/tools/clippy/tests/ui/await_holding_lock.stderr
@@ -1,11 +1,11 @@
-error: this `MutexGuard` is held across an `await` point
+error: this `MutexGuard` is held across an await point
   --> tests/ui/await_holding_lock.rs:10:13
    |
 LL |         let guard = x.lock().unwrap();
    |             ^^^^^
    |
-   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
-note: these are all the `await` points this lock is held through
+   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await`
+note: these are all the await points this lock is held through
   --> tests/ui/await_holding_lock.rs:12:15
    |
 LL |         baz().await
@@ -13,40 +13,40 @@ LL |         baz().await
    = note: `-D clippy::await-holding-lock` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::await_holding_lock)]`
 
-error: this `MutexGuard` is held across an `await` point
+error: this `MutexGuard` is held across an await point
   --> tests/ui/await_holding_lock.rs:26:13
    |
 LL |         let guard = x.read().unwrap();
    |             ^^^^^
    |
-   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
-note: these are all the `await` points this lock is held through
+   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await`
+note: these are all the await points this lock is held through
   --> tests/ui/await_holding_lock.rs:28:15
    |
 LL |         baz().await
    |               ^^^^^
 
-error: this `MutexGuard` is held across an `await` point
+error: this `MutexGuard` is held across an await point
   --> tests/ui/await_holding_lock.rs:32:13
    |
 LL |         let mut guard = x.write().unwrap();
    |             ^^^^^^^^^
    |
-   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
-note: these are all the `await` points this lock is held through
+   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await`
+note: these are all the await points this lock is held through
   --> tests/ui/await_holding_lock.rs:34:15
    |
 LL |         baz().await
    |               ^^^^^
 
-error: this `MutexGuard` is held across an `await` point
+error: this `MutexGuard` is held across an await point
   --> tests/ui/await_holding_lock.rs:54:13
    |
 LL |         let guard = x.lock().unwrap();
    |             ^^^^^
    |
-   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
-note: these are all the `await` points this lock is held through
+   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await`
+note: these are all the await points this lock is held through
   --> tests/ui/await_holding_lock.rs:57:28
    |
 LL |         let second = baz().await;
@@ -55,79 +55,79 @@ LL |
 LL |         let third = baz().await;
    |                           ^^^^^
 
-error: this `MutexGuard` is held across an `await` point
+error: this `MutexGuard` is held across an await point
   --> tests/ui/await_holding_lock.rs:68:17
    |
 LL |             let guard = x.lock().unwrap();
    |                 ^^^^^
    |
-   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
-note: these are all the `await` points this lock is held through
+   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await`
+note: these are all the await points this lock is held through
   --> tests/ui/await_holding_lock.rs:70:19
    |
 LL |             baz().await
    |                   ^^^^^
 
-error: this `MutexGuard` is held across an `await` point
+error: this `MutexGuard` is held across an await point
   --> tests/ui/await_holding_lock.rs:81:17
    |
 LL |             let guard = x.lock().unwrap();
    |                 ^^^^^
    |
-   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
-note: these are all the `await` points this lock is held through
+   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await`
+note: these are all the await points this lock is held through
   --> tests/ui/await_holding_lock.rs:83:19
    |
 LL |             baz().await
    |                   ^^^^^
 
-error: this `MutexGuard` is held across an `await` point
+error: this `MutexGuard` is held across an await point
   --> tests/ui/await_holding_lock.rs:94:13
    |
 LL |         let guard = x.lock();
    |             ^^^^^
    |
-   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
-note: these are all the `await` points this lock is held through
+   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await`
+note: these are all the await points this lock is held through
   --> tests/ui/await_holding_lock.rs:96:15
    |
 LL |         baz().await
    |               ^^^^^
 
-error: this `MutexGuard` is held across an `await` point
+error: this `MutexGuard` is held across an await point
   --> tests/ui/await_holding_lock.rs:110:13
    |
 LL |         let guard = x.read();
    |             ^^^^^
    |
-   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
-note: these are all the `await` points this lock is held through
+   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await`
+note: these are all the await points this lock is held through
   --> tests/ui/await_holding_lock.rs:112:15
    |
 LL |         baz().await
    |               ^^^^^
 
-error: this `MutexGuard` is held across an `await` point
+error: this `MutexGuard` is held across an await point
   --> tests/ui/await_holding_lock.rs:116:13
    |
 LL |         let mut guard = x.write();
    |             ^^^^^^^^^
    |
-   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
-note: these are all the `await` points this lock is held through
+   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await`
+note: these are all the await points this lock is held through
   --> tests/ui/await_holding_lock.rs:118:15
    |
 LL |         baz().await
    |               ^^^^^
 
-error: this `MutexGuard` is held across an `await` point
+error: this `MutexGuard` is held across an await point
   --> tests/ui/await_holding_lock.rs:138:13
    |
 LL |         let guard = x.lock();
    |             ^^^^^
    |
-   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
-note: these are all the `await` points this lock is held through
+   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await`
+note: these are all the await points this lock is held through
   --> tests/ui/await_holding_lock.rs:141:28
    |
 LL |         let second = baz().await;
@@ -136,40 +136,40 @@ LL |
 LL |         let third = baz().await;
    |                           ^^^^^
 
-error: this `MutexGuard` is held across an `await` point
+error: this `MutexGuard` is held across an await point
   --> tests/ui/await_holding_lock.rs:152:17
    |
 LL |             let guard = x.lock();
    |                 ^^^^^
    |
-   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
-note: these are all the `await` points this lock is held through
+   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await`
+note: these are all the await points this lock is held through
   --> tests/ui/await_holding_lock.rs:154:19
    |
 LL |             baz().await
    |                   ^^^^^
 
-error: this `MutexGuard` is held across an `await` point
+error: this `MutexGuard` is held across an await point
   --> tests/ui/await_holding_lock.rs:165:17
    |
 LL |             let guard = x.lock();
    |                 ^^^^^
    |
-   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
-note: these are all the `await` points this lock is held through
+   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await`
+note: these are all the await points this lock is held through
   --> tests/ui/await_holding_lock.rs:167:19
    |
 LL |             baz().await
    |                   ^^^^^
 
-error: this `MutexGuard` is held across an `await` point
+error: this `MutexGuard` is held across an await point
   --> tests/ui/await_holding_lock.rs:186:9
    |
 LL |     let mut guard = x.lock().unwrap();
    |         ^^^^^^^^^
    |
-   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling await
-note: these are all the `await` points this lock is held through
+   = help: consider using an async-aware `Mutex` type or ensuring the `MutexGuard` is dropped before calling `await`
+note: these are all the await points this lock is held through
   --> tests/ui/await_holding_lock.rs:190:11
    |
 LL |     baz().await;
diff --git a/src/tools/clippy/tests/ui/await_holding_refcell_ref.rs b/src/tools/clippy/tests/ui/await_holding_refcell_ref.rs
index 5bd26c62836..b0c92d8c1f6 100644
--- a/src/tools/clippy/tests/ui/await_holding_refcell_ref.rs
+++ b/src/tools/clippy/tests/ui/await_holding_refcell_ref.rs
@@ -4,13 +4,13 @@ use std::cell::RefCell;
 
 async fn bad(x: &RefCell<u32>) -> u32 {
     let b = x.borrow();
-    //~^ ERROR: this `RefCell` reference is held across an `await` point
+    //~^ ERROR: this `RefCell` reference is held across an await point
     baz().await
 }
 
 async fn bad_mut(x: &RefCell<u32>) -> u32 {
     let b = x.borrow_mut();
-    //~^ ERROR: this `RefCell` reference is held across an `await` point
+    //~^ ERROR: this `RefCell` reference is held across an await point
     baz().await
 }
 
@@ -32,7 +32,7 @@ async fn also_bad(x: &RefCell<u32>) -> u32 {
     let first = baz().await;
 
     let b = x.borrow_mut();
-    //~^ ERROR: this `RefCell` reference is held across an `await` point
+    //~^ ERROR: this `RefCell` reference is held across an await point
 
     let second = baz().await;
 
@@ -45,7 +45,7 @@ async fn less_bad(x: &RefCell<u32>) -> u32 {
     let first = baz().await;
 
     let b = x.borrow_mut();
-    //~^ ERROR: this `RefCell` reference is held across an `await` point
+    //~^ ERROR: this `RefCell` reference is held across an await point
 
     let second = baz().await;
 
@@ -61,7 +61,7 @@ async fn not_good(x: &RefCell<u32>) -> u32 {
 
     let second = {
         let b = x.borrow_mut();
-        //~^ ERROR: this `RefCell` reference is held across an `await` point
+        //~^ ERROR: this `RefCell` reference is held across an await point
         baz().await
     };
 
@@ -74,7 +74,7 @@ async fn not_good(x: &RefCell<u32>) -> u32 {
 fn block_bad(x: &RefCell<u32>) -> impl std::future::Future<Output = u32> + '_ {
     async move {
         let b = x.borrow_mut();
-        //~^ ERROR: this `RefCell` reference is held across an `await` point
+        //~^ ERROR: this `RefCell` reference is held across an await point
         baz().await
     }
 }
diff --git a/src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr b/src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr
index 6b474c27ddc..6c7209c9ff9 100644
--- a/src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr
+++ b/src/tools/clippy/tests/ui/await_holding_refcell_ref.stderr
@@ -1,11 +1,11 @@
-error: this `RefCell` reference is held across an `await` point
+error: this `RefCell` reference is held across an await point
   --> tests/ui/await_holding_refcell_ref.rs:6:9
    |
 LL |     let b = x.borrow();
    |         ^
    |
    = help: ensure the reference is dropped before calling `await`
-note: these are all the `await` points this reference is held through
+note: these are all the await points this reference is held through
   --> tests/ui/await_holding_refcell_ref.rs:8:11
    |
 LL |     baz().await
@@ -13,27 +13,27 @@ LL |     baz().await
    = note: `-D clippy::await-holding-refcell-ref` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::await_holding_refcell_ref)]`
 
-error: this `RefCell` reference is held across an `await` point
+error: this `RefCell` reference is held across an await point
   --> tests/ui/await_holding_refcell_ref.rs:12:9
    |
 LL |     let b = x.borrow_mut();
    |         ^
    |
    = help: ensure the reference is dropped before calling `await`
-note: these are all the `await` points this reference is held through
+note: these are all the await points this reference is held through
   --> tests/ui/await_holding_refcell_ref.rs:14:11
    |
 LL |     baz().await
    |           ^^^^^
 
-error: this `RefCell` reference is held across an `await` point
+error: this `RefCell` reference is held across an await point
   --> tests/ui/await_holding_refcell_ref.rs:34:9
    |
 LL |     let b = x.borrow_mut();
    |         ^
    |
    = help: ensure the reference is dropped before calling `await`
-note: these are all the `await` points this reference is held through
+note: these are all the await points this reference is held through
   --> tests/ui/await_holding_refcell_ref.rs:37:24
    |
 LL |     let second = baz().await;
@@ -42,40 +42,40 @@ LL |
 LL |     let third = baz().await;
    |                       ^^^^^
 
-error: this `RefCell` reference is held across an `await` point
+error: this `RefCell` reference is held across an await point
   --> tests/ui/await_holding_refcell_ref.rs:47:9
    |
 LL |     let b = x.borrow_mut();
    |         ^
    |
    = help: ensure the reference is dropped before calling `await`
-note: these are all the `await` points this reference is held through
+note: these are all the await points this reference is held through
   --> tests/ui/await_holding_refcell_ref.rs:50:24
    |
 LL |     let second = baz().await;
    |                        ^^^^^
 
-error: this `RefCell` reference is held across an `await` point
+error: this `RefCell` reference is held across an await point
   --> tests/ui/await_holding_refcell_ref.rs:63:13
    |
 LL |         let b = x.borrow_mut();
    |             ^
    |
    = help: ensure the reference is dropped before calling `await`
-note: these are all the `await` points this reference is held through
+note: these are all the await points this reference is held through
   --> tests/ui/await_holding_refcell_ref.rs:65:15
    |
 LL |         baz().await
    |               ^^^^^
 
-error: this `RefCell` reference is held across an `await` point
+error: this `RefCell` reference is held across an await point
   --> tests/ui/await_holding_refcell_ref.rs:76:13
    |
 LL |         let b = x.borrow_mut();
    |             ^
    |
    = help: ensure the reference is dropped before calling `await`
-note: these are all the `await` points this reference is held through
+note: these are all the await points this reference is held through
   --> tests/ui/await_holding_refcell_ref.rs:78:15
    |
 LL |         baz().await
diff --git a/src/tools/clippy/tests/ui/byte_char_slices.fixed b/src/tools/clippy/tests/ui/byte_char_slices.fixed
new file mode 100644
index 00000000000..d1db58f9363
--- /dev/null
+++ b/src/tools/clippy/tests/ui/byte_char_slices.fixed
@@ -0,0 +1,13 @@
+#![allow(unused)]
+#![warn(clippy::byte_char_slices)]
+
+fn main() {
+    let bad = b"abc";
+    let quotes = b"\"Hi";
+    let quotes = b"'Sup";
+    let escapes = b"\x42Esc";
+
+    let good = &[b'a', 0x42];
+    let good = [b'a', b'a'];
+    let good: u8 = [b'a', b'c'].into_iter().sum();
+}
diff --git a/src/tools/clippy/tests/ui/byte_char_slices.rs b/src/tools/clippy/tests/ui/byte_char_slices.rs
new file mode 100644
index 00000000000..18648fffceb
--- /dev/null
+++ b/src/tools/clippy/tests/ui/byte_char_slices.rs
@@ -0,0 +1,13 @@
+#![allow(unused)]
+#![warn(clippy::byte_char_slices)]
+
+fn main() {
+    let bad = &[b'a', b'b', b'c'];
+    let quotes = &[b'"', b'H', b'i'];
+    let quotes = &[b'\'', b'S', b'u', b'p'];
+    let escapes = &[b'\x42', b'E', b's', b'c'];
+
+    let good = &[b'a', 0x42];
+    let good = vec![b'a', b'a'];
+    let good: u8 = [b'a', b'c'].into_iter().sum();
+}
diff --git a/src/tools/clippy/tests/ui/byte_char_slices.stderr b/src/tools/clippy/tests/ui/byte_char_slices.stderr
new file mode 100644
index 00000000000..4e2b5d8a732
--- /dev/null
+++ b/src/tools/clippy/tests/ui/byte_char_slices.stderr
@@ -0,0 +1,38 @@
+error: can be more succinctly written as a byte str
+  --> tests/ui/byte_char_slices.rs:5:15
+   |
+LL |     let bad = &[b'a', b'b', b'c'];
+   |               ^^^^^^^^^^^^^^^^^^^ help: try: `b"abc"`
+   |
+   = note: `-D clippy::byte-char-slices` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::byte_char_slices)]`
+
+error: can be more succinctly written as a byte str
+  --> tests/ui/byte_char_slices.rs:6:18
+   |
+LL |     let quotes = &[b'"', b'H', b'i'];
+   |                  ^^^^^^^^^^^^^^^^^^^ help: try: `b"\"Hi"`
+
+error: can be more succinctly written as a byte str
+  --> tests/ui/byte_char_slices.rs:7:18
+   |
+LL |     let quotes = &[b'\'', b'S', b'u', b'p'];
+   |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b"'Sup"`
+
+error: can be more succinctly written as a byte str
+  --> tests/ui/byte_char_slices.rs:8:19
+   |
+LL |     let escapes = &[b'\x42', b'E', b's', b'c'];
+   |                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `b"\x42Esc"`
+
+error: useless use of `vec!`
+  --> tests/ui/byte_char_slices.rs:11:16
+   |
+LL |     let good = vec![b'a', b'a'];
+   |                ^^^^^^^^^^^^^^^^ help: you can use an array directly: `[b'a', b'a']`
+   |
+   = note: `-D clippy::useless-vec` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::useless_vec)]`
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/cfg_not_test.rs b/src/tools/clippy/tests/ui/cfg_not_test.rs
new file mode 100644
index 00000000000..da3e29d2896
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cfg_not_test.rs
@@ -0,0 +1,32 @@
+#![allow(unused)]
+#![warn(clippy::cfg_not_test)]
+
+fn important_check() {}
+
+fn main() {
+    // Statement
+    #[cfg(not(test))]
+    let answer = 42;
+
+    // Expression
+    #[cfg(not(test))]
+    important_check();
+
+    // Make sure only not(test) are checked, not other attributes
+    #[cfg(not(foo))]
+    important_check();
+}
+
+#[cfg(not(not(test)))]
+struct CfgNotTest;
+
+// Deeply nested `not(test)`
+#[cfg(not(test))]
+fn foo() {}
+#[cfg(all(debug_assertions, not(test)))]
+fn bar() {}
+#[cfg(not(any(not(debug_assertions), test)))]
+fn baz() {}
+
+#[cfg(test)]
+mod tests {}
diff --git a/src/tools/clippy/tests/ui/cfg_not_test.stderr b/src/tools/clippy/tests/ui/cfg_not_test.stderr
new file mode 100644
index 00000000000..c1bf626887a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/cfg_not_test.stderr
@@ -0,0 +1,45 @@
+error: code is excluded from test builds
+  --> tests/ui/cfg_not_test.rs:8:5
+   |
+LL |     #[cfg(not(test))]
+   |     ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider not excluding any code from test builds
+   = note: this could increase code coverage despite not actually being tested
+   = note: `-D clippy::cfg-not-test` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::cfg_not_test)]`
+
+error: code is excluded from test builds
+  --> tests/ui/cfg_not_test.rs:12:5
+   |
+LL |     #[cfg(not(test))]
+   |     ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider not excluding any code from test builds
+
+error: code is excluded from test builds
+  --> tests/ui/cfg_not_test.rs:24:1
+   |
+LL | #[cfg(not(test))]
+   | ^^^^^^^^^^^^^^^^^
+   |
+   = help: consider not excluding any code from test builds
+
+error: code is excluded from test builds
+  --> tests/ui/cfg_not_test.rs:26:1
+   |
+LL | #[cfg(all(debug_assertions, not(test)))]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider not excluding any code from test builds
+
+error: code is excluded from test builds
+  --> tests/ui/cfg_not_test.rs:28:1
+   |
+LL | #[cfg(not(any(not(debug_assertions), test)))]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: consider not excluding any code from test builds
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/crashes/ice-12616.stderr b/src/tools/clippy/tests/ui/crashes/ice-12616.stderr
index c7cf5cf5483..a84a945a429 100644
--- a/src/tools/clippy/tests/ui/crashes/ice-12616.stderr
+++ b/src/tools/clippy/tests/ui/crashes/ice-12616.stderr
@@ -1,4 +1,4 @@
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/crashes/ice-12616.rs:6:5
    |
 LL |     s() as *const ();
diff --git a/src/tools/clippy/tests/ui/disallowed_names.rs b/src/tools/clippy/tests/ui/disallowed_names.rs
index 13c883409bf..96531bf8d88 100644
--- a/src/tools/clippy/tests/ui/disallowed_names.rs
+++ b/src/tools/clippy/tests/ui/disallowed_names.rs
@@ -60,6 +60,7 @@ fn issue_1647_ref_mut() {
     //~^ ERROR: use of a disallowed/placeholder name `quux`
 }
 
+#[cfg(test)]
 mod tests {
     fn issue_7305() {
         // `disallowed_names` lint should not be triggered inside of the test code.
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.fixed b/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.fixed
new file mode 100644
index 00000000000..1aaa26afe7f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.fixed
@@ -0,0 +1,47 @@
+// https://github.com/rust-lang/rust-clippy/issues/12917
+#![warn(clippy::doc_lazy_continuation)]
+
+/// This is a constant.
+///
+/// The meaning of which should not be explained.
+pub const A: i32 = 42;
+
+/// This is another constant, no longer used.
+///
+/// This block of documentation has a long
+/// explanation and derivation to explain
+/// why it is what it is, and how it's used.
+///
+/// It is left here for historical reasons, and
+/// for reference.
+///
+/// Reasons it's great:
+///  - First reason
+///  - Second reason
+///
+//pub const B: i32 = 1337;
+
+/// This is yet another constant.
+///
+/// This has a similar fate as `B`.
+///
+/// Reasons it's useful:
+///  1. First reason
+///  2. Second reason
+///
+//pub const C: i32 = 8008;
+
+/// This is still in use.
+pub const D: i32 = 20;
+
+/// > blockquote code path
+///
+
+/// bottom text
+pub const E: i32 = 20;
+
+/// > blockquote code path
+///
+#[repr(C)]
+/// bottom text
+pub struct Foo(i32);
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.rs b/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.rs
new file mode 100644
index 00000000000..e1ab8fc8389
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.rs
@@ -0,0 +1,43 @@
+// https://github.com/rust-lang/rust-clippy/issues/12917
+#![warn(clippy::doc_lazy_continuation)]
+
+/// This is a constant.
+///
+/// The meaning of which should not be explained.
+pub const A: i32 = 42;
+
+/// This is another constant, no longer used.
+///
+/// This block of documentation has a long
+/// explanation and derivation to explain
+/// why it is what it is, and how it's used.
+///
+/// It is left here for historical reasons, and
+/// for reference.
+///
+/// Reasons it's great:
+///  - First reason
+///  - Second reason
+//pub const B: i32 = 1337;
+
+/// This is yet another constant.
+///
+/// This has a similar fate as `B`.
+///
+/// Reasons it's useful:
+///  1. First reason
+///  2. Second reason
+//pub const C: i32 = 8008;
+
+/// This is still in use.
+pub const D: i32 = 20;
+
+/// > blockquote code path
+
+/// bottom text
+pub const E: i32 = 20;
+
+/// > blockquote code path
+#[repr(C)]
+/// bottom text
+pub struct Foo(i32);
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.stderr b/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.stderr
new file mode 100644
index 00000000000..854906a7474
--- /dev/null
+++ b/src/tools/clippy/tests/ui/doc/doc_lazy_blank_line.stderr
@@ -0,0 +1,56 @@
+error: doc list item without indentation
+  --> tests/ui/doc/doc_lazy_blank_line.rs:23:5
+   |
+LL | /// This is yet another constant.
+   |     ^
+   |
+   = help: if this is intended to be part of the list, indent 3 spaces
+   = note: `-D clippy::doc-lazy-continuation` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::doc_lazy_continuation)]`
+help: if this should be its own paragraph, add a blank doc comment line
+   |
+LL ~ ///  - Second reason
+LL + ///
+   |
+
+error: doc list item without indentation
+  --> tests/ui/doc/doc_lazy_blank_line.rs:32:5
+   |
+LL | /// This is still in use.
+   |     ^
+   |
+   = help: if this is intended to be part of the list, indent 4 spaces
+help: if this should be its own paragraph, add a blank doc comment line
+   |
+LL ~ ///  2. Second reason
+LL + ///
+   |
+
+error: doc quote line without `>` marker
+  --> tests/ui/doc/doc_lazy_blank_line.rs:37:5
+   |
+LL | /// bottom text
+   |     ^
+   |
+   = help: if this not intended to be a quote at all, escape it with `\>`
+help: if this should be its own paragraph, add a blank doc comment line
+   |
+LL ~ /// > blockquote code path
+LL + ///
+   |
+
+error: doc quote line without `>` marker
+  --> tests/ui/doc/doc_lazy_blank_line.rs:42:5
+   |
+LL | /// bottom text
+   |     ^
+   |
+   = help: if this not intended to be a quote at all, escape it with `\>`
+help: if this should be its own paragraph, add a blank doc comment line
+   |
+LL ~ /// > blockquote code path
+LL + ///
+   |
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.fixed b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.fixed
index 9877991f183..9d6e8637608 100644
--- a/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.fixed
+++ b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.fixed
@@ -2,7 +2,7 @@
 
 /// > blockquote with
 /// > lazy continuation
-//~^ ERROR: doc quote missing `>` marker
+//~^ ERROR: doc quote line without `>` marker
 fn first() {}
 
 /// > blockquote with no
@@ -18,24 +18,24 @@ fn two_nowarn() {}
 /// >
 /// > > nest here
 /// > > lazy continuation
-//~^ ERROR: doc quote missing `>` marker
+//~^ ERROR: doc quote line without `>` marker
 fn two() {}
 
 /// > nest here
 /// >
 /// > > nest here
 /// > > lazy continuation
-//~^ ERROR: doc quote missing `>` marker
+//~^ ERROR: doc quote line without `>` marker
 fn three() {}
 
 /// >   * > nest here
 /// >     > lazy continuation
-//~^ ERROR: doc quote missing `>` marker
+//~^ ERROR: doc quote line without `>` marker
 fn four() {}
 
 /// > * > nest here
 /// >   > lazy continuation
-//~^ ERROR: doc quote missing `>` marker
+//~^ ERROR: doc quote line without `>` marker
 fn four_point_1() {}
 
 /// * > nest here lazy continuation
@@ -43,5 +43,5 @@ fn five() {}
 
 /// 1. > nest here
 ///    > lazy continuation (this results in strange indentation, but still works)
-//~^ ERROR: doc quote missing `>` marker
+//~^ ERROR: doc quote line without `>` marker
 fn six() {}
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.rs b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.rs
index 587b2fdd533..0323a1b44e7 100644
--- a/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.rs
+++ b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.rs
@@ -2,7 +2,7 @@
 
 /// > blockquote with
 /// lazy continuation
-//~^ ERROR: doc quote missing `>` marker
+//~^ ERROR: doc quote line without `>` marker
 fn first() {}
 
 /// > blockquote with no
@@ -18,24 +18,24 @@ fn two_nowarn() {}
 /// >
 /// > > nest here
 /// > lazy continuation
-//~^ ERROR: doc quote missing `>` marker
+//~^ ERROR: doc quote line without `>` marker
 fn two() {}
 
 /// > nest here
 /// >
 /// > > nest here
 /// lazy continuation
-//~^ ERROR: doc quote missing `>` marker
+//~^ ERROR: doc quote line without `>` marker
 fn three() {}
 
 /// >   * > nest here
 /// lazy continuation
-//~^ ERROR: doc quote missing `>` marker
+//~^ ERROR: doc quote line without `>` marker
 fn four() {}
 
 /// > * > nest here
 /// lazy continuation
-//~^ ERROR: doc quote missing `>` marker
+//~^ ERROR: doc quote line without `>` marker
 fn four_point_1() {}
 
 /// * > nest here lazy continuation
@@ -43,5 +43,5 @@ fn five() {}
 
 /// 1. > nest here
 ///  lazy continuation (this results in strange indentation, but still works)
-//~^ ERROR: doc quote missing `>` marker
+//~^ ERROR: doc quote line without `>` marker
 fn six() {}
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.stderr b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.stderr
index 975184a01c3..d3390efdff3 100644
--- a/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.stderr
+++ b/src/tools/clippy/tests/ui/doc/doc_lazy_blockquote.stderr
@@ -1,4 +1,4 @@
-error: doc quote missing `>` marker
+error: doc quote line without `>` marker
   --> tests/ui/doc/doc_lazy_blockquote.rs:4:5
    |
 LL | /// lazy continuation
@@ -12,7 +12,7 @@ help: add markers to start of line
 LL | /// > lazy continuation
    |     +
 
-error: doc quote missing `>` marker
+error: doc quote line without `>` marker
   --> tests/ui/doc/doc_lazy_blockquote.rs:20:5
    |
 LL | /// > lazy continuation
@@ -24,7 +24,7 @@ help: add markers to start of line
 LL | /// > > lazy continuation
    |       +
 
-error: doc quote missing `>` marker
+error: doc quote line without `>` marker
   --> tests/ui/doc/doc_lazy_blockquote.rs:27:5
    |
 LL | /// lazy continuation
@@ -36,7 +36,7 @@ help: add markers to start of line
 LL | /// > > lazy continuation
    |     +++
 
-error: doc quote missing `>` marker
+error: doc quote line without `>` marker
   --> tests/ui/doc/doc_lazy_blockquote.rs:32:5
    |
 LL | /// lazy continuation
@@ -48,7 +48,7 @@ help: add markers to start of line
 LL | /// >     > lazy continuation
    |     +++++++
 
-error: doc quote missing `>` marker
+error: doc quote line without `>` marker
   --> tests/ui/doc/doc_lazy_blockquote.rs:37:5
    |
 LL | /// lazy continuation
@@ -60,7 +60,7 @@ help: add markers to start of line
 LL | /// >   > lazy continuation
    |     +++++
 
-error: doc quote missing `>` marker
+error: doc quote line without `>` marker
   --> tests/ui/doc/doc_lazy_blockquote.rs:45:5
    |
 LL | ///  lazy continuation (this results in strange indentation, but still works)
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed b/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed
index 409e6b0bc22..ea59ae4c01c 100644
--- a/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed
+++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.fixed
@@ -2,38 +2,41 @@
 
 /// 1. nest here
 ///    lazy continuation
-//~^ ERROR: doc list item missing indentation
+//~^ ERROR: doc list item without indentation
 fn one() {}
 
 /// 1. first line
 ///    lazy list continuations don't make warnings with this lint
-//~^ ERROR: doc list item missing indentation
-///    because they don't have the
-//~^ ERROR: doc list item missing indentation
+///
+//~^ ERROR: doc list item without indentation
+/// because they don't have the
+//~^ ERROR: doc list item without indentation
 fn two() {}
 
 ///   - nest here
 ///     lazy continuation
-//~^ ERROR: doc list item missing indentation
+//~^ ERROR: doc list item without indentation
 fn three() {}
 
 ///   - first line
 ///     lazy list continuations don't make warnings with this lint
-//~^ ERROR: doc list item missing indentation
-///     because they don't have the
-//~^ ERROR: doc list item missing indentation
+///
+//~^ ERROR: doc list item without indentation
+/// because they don't have the
+//~^ ERROR: doc list item without indentation
 fn four() {}
 
 ///   - nest here
 ///     lazy continuation
-//~^ ERROR: doc list item missing indentation
+//~^ ERROR: doc list item without indentation
 fn five() {}
 
 ///   - - first line
 ///       this will warn on the lazy continuation
-//~^ ERROR: doc list item missing indentation
-///       and so should this
-//~^ ERROR: doc list item missing indentation
+///
+//~^ ERROR: doc list item without indentation
+///     and so should this
+//~^ ERROR: doc list item without indentation
 fn six() {}
 
 ///   - - first line
@@ -54,7 +57,7 @@ fn seven() {}
 /// * `protocol_descriptors`: A Json Representation of the ProtocolDescriptors
 ///     to set up. Example:
 ///   'protocol_descriptors': [
-//~^ ERROR: doc list item missing indentation
+//~^ ERROR: doc list item without indentation
 ///      {
 ///          'protocol': 25,  # u64 Representation of ProtocolIdentifier::AVDTP
 ///          'params': [
@@ -73,5 +76,5 @@ fn seven() {}
 ///          }]
 ///      }
 ///   ]
-//~^ ERROR: doc list item missing indentation
+//~^ ERROR: doc list item without indentation
 fn eight() {}
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs b/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs
index 30ab448a113..3cc18e35780 100644
--- a/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs
+++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.rs
@@ -2,38 +2,38 @@
 
 /// 1. nest here
 /// lazy continuation
-//~^ ERROR: doc list item missing indentation
+//~^ ERROR: doc list item without indentation
 fn one() {}
 
 /// 1. first line
 /// lazy list continuations don't make warnings with this lint
-//~^ ERROR: doc list item missing indentation
+//~^ ERROR: doc list item without indentation
 /// because they don't have the
-//~^ ERROR: doc list item missing indentation
+//~^ ERROR: doc list item without indentation
 fn two() {}
 
 ///   - nest here
 /// lazy continuation
-//~^ ERROR: doc list item missing indentation
+//~^ ERROR: doc list item without indentation
 fn three() {}
 
 ///   - first line
 /// lazy list continuations don't make warnings with this lint
-//~^ ERROR: doc list item missing indentation
+//~^ ERROR: doc list item without indentation
 /// because they don't have the
-//~^ ERROR: doc list item missing indentation
+//~^ ERROR: doc list item without indentation
 fn four() {}
 
 ///   - nest here
 /// lazy continuation
-//~^ ERROR: doc list item missing indentation
+//~^ ERROR: doc list item without indentation
 fn five() {}
 
 ///   - - first line
 /// this will warn on the lazy continuation
-//~^ ERROR: doc list item missing indentation
+//~^ ERROR: doc list item without indentation
 ///     and so should this
-//~^ ERROR: doc list item missing indentation
+//~^ ERROR: doc list item without indentation
 fn six() {}
 
 ///   - - first line
@@ -54,7 +54,7 @@ fn seven() {}
 /// * `protocol_descriptors`: A Json Representation of the ProtocolDescriptors
 ///     to set up. Example:
 ///  'protocol_descriptors': [
-//~^ ERROR: doc list item missing indentation
+//~^ ERROR: doc list item without indentation
 ///      {
 ///          'protocol': 25,  # u64 Representation of ProtocolIdentifier::AVDTP
 ///          'params': [
@@ -73,5 +73,5 @@ fn seven() {}
 ///          }]
 ///      }
 ///  ]
-//~^ ERROR: doc list item missing indentation
+//~^ ERROR: doc list item without indentation
 fn eight() {}
diff --git a/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr b/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr
index ddfdc49340c..52aa74df894 100644
--- a/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr
+++ b/src/tools/clippy/tests/ui/doc/doc_lazy_list.stderr
@@ -1,4 +1,4 @@
-error: doc list item missing indentation
+error: doc list item without indentation
   --> tests/ui/doc/doc_lazy_list.rs:4:5
    |
 LL | /// lazy continuation
@@ -12,7 +12,7 @@ help: indent this line
 LL | ///    lazy continuation
    |     +++
 
-error: doc list item missing indentation
+error: doc list item without indentation
   --> tests/ui/doc/doc_lazy_list.rs:9:5
    |
 LL | /// lazy list continuations don't make warnings with this lint
@@ -24,19 +24,20 @@ help: indent this line
 LL | ///    lazy list continuations don't make warnings with this lint
    |     +++
 
-error: doc list item missing indentation
+error: doc list item without indentation
   --> tests/ui/doc/doc_lazy_list.rs:11:5
    |
 LL | /// because they don't have the
    |     ^
    |
-   = help: if this is supposed to be its own paragraph, add a blank line
-help: indent this line
+   = help: if this is intended to be part of the list, indent 3 spaces
+help: if this should be its own paragraph, add a blank doc comment line
+   |
+LL ~ /// lazy list continuations don't make warnings with this lint
+LL + ///
    |
-LL | ///    because they don't have the
-   |     +++
 
-error: doc list item missing indentation
+error: doc list item without indentation
   --> tests/ui/doc/doc_lazy_list.rs:16:5
    |
 LL | /// lazy continuation
@@ -48,7 +49,7 @@ help: indent this line
 LL | ///     lazy continuation
    |     ++++
 
-error: doc list item missing indentation
+error: doc list item without indentation
   --> tests/ui/doc/doc_lazy_list.rs:21:5
    |
 LL | /// lazy list continuations don't make warnings with this lint
@@ -60,19 +61,20 @@ help: indent this line
 LL | ///     lazy list continuations don't make warnings with this lint
    |     ++++
 
-error: doc list item missing indentation
+error: doc list item without indentation
   --> tests/ui/doc/doc_lazy_list.rs:23:5
    |
 LL | /// because they don't have the
    |     ^
    |
-   = help: if this is supposed to be its own paragraph, add a blank line
-help: indent this line
+   = help: if this is intended to be part of the list, indent 4 spaces
+help: if this should be its own paragraph, add a blank doc comment line
+   |
+LL ~ /// lazy list continuations don't make warnings with this lint
+LL + ///
    |
-LL | ///     because they don't have the
-   |     ++++
 
-error: doc list item missing indentation
+error: doc list item without indentation
   --> tests/ui/doc/doc_lazy_list.rs:28:5
    |
 LL | /// lazy continuation
@@ -84,7 +86,7 @@ help: indent this line
 LL | ///     lazy continuation
    |     ++++
 
-error: doc list item missing indentation
+error: doc list item without indentation
   --> tests/ui/doc/doc_lazy_list.rs:33:5
    |
 LL | /// this will warn on the lazy continuation
@@ -96,19 +98,20 @@ help: indent this line
 LL | ///       this will warn on the lazy continuation
    |     ++++++
 
-error: doc list item missing indentation
+error: doc list item without indentation
   --> tests/ui/doc/doc_lazy_list.rs:35:5
    |
 LL | ///     and so should this
    |     ^^^^
    |
-   = help: if this is supposed to be its own paragraph, add a blank line
-help: indent this line
+   = help: if this is intended to be part of the list, indent 2 spaces
+help: if this should be its own paragraph, add a blank doc comment line
+   |
+LL ~ /// this will warn on the lazy continuation
+LL + ///
    |
-LL | ///       and so should this
-   |         ++
 
-error: doc list item missing indentation
+error: doc list item without indentation
   --> tests/ui/doc/doc_lazy_list.rs:56:5
    |
 LL | ///  'protocol_descriptors': [
@@ -120,7 +123,7 @@ help: indent this line
 LL | ///   'protocol_descriptors': [
    |      +
 
-error: doc list item missing indentation
+error: doc list item without indentation
   --> tests/ui/doc/doc_lazy_list.rs:75:5
    |
 LL | ///  ]
diff --git a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs
index 6f7bab72040..04446787b6c 100644
--- a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs
+++ b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.rs
@@ -49,3 +49,20 @@ fn other_markdown() {}
 ///   pub struct Struct;
 ///   ```
 fn issue_7421() {}
+
+/// `
+//~^ ERROR: backticks are unbalanced
+fn escape_0() {}
+
+/// Escaped \` backticks don't count.
+fn escape_1() {}
+
+/// Escaped \` \` backticks don't count.
+fn escape_2() {}
+
+/// Escaped \` ` backticks don't count, but unescaped backticks do.
+//~^ ERROR: backticks are unbalanced
+fn escape_3() {}
+
+/// Backslashes ` \` within code blocks don't count.
+fn escape_4() {}
diff --git a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr
index 56ef2913623..50324010e97 100644
--- a/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr
+++ b/src/tools/clippy/tests/ui/doc/unbalanced_ticks.stderr
@@ -78,5 +78,21 @@ help: try
 LL | /// - This item needs `backticks_here`
    |                       ~~~~~~~~~~~~~~~~
 
-error: aborting due to 8 previous errors
+error: backticks are unbalanced
+  --> tests/ui/doc/unbalanced_ticks.rs:53:5
+   |
+LL | /// `
+   |     ^
+   |
+   = help: a backtick may be missing a pair
+
+error: backticks are unbalanced
+  --> tests/ui/doc/unbalanced_ticks.rs:63:5
+   |
+LL | /// Escaped \` ` backticks don't count, but unescaped backticks do.
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = help: a backtick may be missing a pair
+
+error: aborting due to 10 previous errors
 
diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.fixed b/src/tools/clippy/tests/ui/explicit_auto_deref.fixed
index e6ca4bb66cc..255b2c5a220 100644
--- a/src/tools/clippy/tests/ui/explicit_auto_deref.fixed
+++ b/src/tools/clippy/tests/ui/explicit_auto_deref.fixed
@@ -345,3 +345,39 @@ fn main() {
         let _ = &mut ({ *x.u }).x;
     }
 }
+
+mod issue_12969 {
+    use std::ops::Deref;
+
+    struct Wrapper<T>(T);
+
+    impl<T> Deref for Wrapper<T> {
+        type Target = T;
+
+        fn deref(&self) -> &T {
+            &self.0
+        }
+    }
+
+    fn foo(_bar: &str) {}
+
+    fn bar() {
+        let wrapped_bar = Wrapper("");
+
+        foo(&wrapped_bar);
+    }
+}
+
+mod issue_9841 {
+    fn takes_array_ref<T, const N: usize>(array: &&[T; N]) {
+        takes_slice(*array)
+    }
+
+    fn takes_array_ref_ref<T, const N: usize>(array: &&&[T; N]) {
+        takes_slice(**array)
+    }
+
+    fn takes_slice<T>(slice: &[T]) {
+        todo!()
+    }
+}
diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.rs b/src/tools/clippy/tests/ui/explicit_auto_deref.rs
index 7531e1f87b7..99906999f01 100644
--- a/src/tools/clippy/tests/ui/explicit_auto_deref.rs
+++ b/src/tools/clippy/tests/ui/explicit_auto_deref.rs
@@ -345,3 +345,39 @@ fn main() {
         let _ = &mut ({ *x.u }).x;
     }
 }
+
+mod issue_12969 {
+    use std::ops::Deref;
+
+    struct Wrapper<T>(T);
+
+    impl<T> Deref for Wrapper<T> {
+        type Target = T;
+
+        fn deref(&self) -> &T {
+            &self.0
+        }
+    }
+
+    fn foo(_bar: &str) {}
+
+    fn bar() {
+        let wrapped_bar = Wrapper("");
+
+        foo(&*wrapped_bar);
+    }
+}
+
+mod issue_9841 {
+    fn takes_array_ref<T, const N: usize>(array: &&[T; N]) {
+        takes_slice(*array)
+    }
+
+    fn takes_array_ref_ref<T, const N: usize>(array: &&&[T; N]) {
+        takes_slice(**array)
+    }
+
+    fn takes_slice<T>(slice: &[T]) {
+        todo!()
+    }
+}
diff --git a/src/tools/clippy/tests/ui/explicit_auto_deref.stderr b/src/tools/clippy/tests/ui/explicit_auto_deref.stderr
index 56a183de348..53784934f63 100644
--- a/src/tools/clippy/tests/ui/explicit_auto_deref.stderr
+++ b/src/tools/clippy/tests/ui/explicit_auto_deref.stderr
@@ -271,5 +271,11 @@ error: deref which would be done by auto-deref
 LL |         let _ = &mut (*{ x.u }).x;
    |                      ^^^^^^^^^^ help: try: `{ x.u }`
 
-error: aborting due to 45 previous errors
+error: deref which would be done by auto-deref
+  --> tests/ui/explicit_auto_deref.rs:367:13
+   |
+LL |         foo(&*wrapped_bar);
+   |             ^^^^^^^^^^^^^ help: try: `&wrapped_bar`
+
+error: aborting due to 46 previous errors
 
diff --git a/src/tools/clippy/tests/ui/float_cmp.rs b/src/tools/clippy/tests/ui/float_cmp.rs
index 1923ad7c677..78dd2c6c01c 100644
--- a/src/tools/clippy/tests/ui/float_cmp.rs
+++ b/src/tools/clippy/tests/ui/float_cmp.rs
@@ -71,19 +71,16 @@ fn main() {
     twice(ONE) != ONE;
     ONE as f64 != 2.0;
     //~^ ERROR: strict comparison of `f32` or `f64`
-    //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
     ONE as f64 != 0.0; // no error, comparison with zero is ok
 
     let x: f64 = 1.0;
 
     x == 1.0;
     //~^ ERROR: strict comparison of `f32` or `f64`
-    //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
     x != 0f64; // no error, comparison with zero is ok
 
     twice(x) != twice(ONE as f64);
     //~^ ERROR: strict comparison of `f32` or `f64`
-    //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
     x < 0.0; // no errors, lower or greater comparisons need no fuzzyness
     x > 0.0;
@@ -105,17 +102,14 @@ fn main() {
     ZERO_ARRAY[i] == NON_ZERO_ARRAY[j]; // ok, because lhs is zero regardless of i
     NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j];
     //~^ ERROR: strict comparison of `f32` or `f64`
-    //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
     let a1: [f32; 1] = [0.0];
     let a2: [f32; 1] = [1.1];
 
     a1 == a2;
     //~^ ERROR: strict comparison of `f32` or `f64` arrays
-    //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
     a1[0] == a2[0];
     //~^ ERROR: strict comparison of `f32` or `f64`
-    //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
     // no errors - comparing signums is ok
     let x32 = 3.21f32;
diff --git a/src/tools/clippy/tests/ui/float_cmp.stderr b/src/tools/clippy/tests/ui/float_cmp.stderr
index c8a0bde6e63..d10da8a99a9 100644
--- a/src/tools/clippy/tests/ui/float_cmp.stderr
+++ b/src/tools/clippy/tests/ui/float_cmp.stderr
@@ -4,49 +4,38 @@ error: strict comparison of `f32` or `f64`
 LL |     ONE as f64 != 2.0;
    |     ^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE as f64 - 2.0).abs() > error_margin`
    |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
    = note: `-D clippy::float-cmp` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::float_cmp)]`
 
 error: strict comparison of `f32` or `f64`
-  --> tests/ui/float_cmp.rs:79:5
+  --> tests/ui/float_cmp.rs:78:5
    |
 LL |     x == 1.0;
    |     ^^^^^^^^ help: consider comparing them within some margin of error: `(x - 1.0).abs() < error_margin`
-   |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64`
-  --> tests/ui/float_cmp.rs:84:5
+  --> tests/ui/float_cmp.rs:82:5
    |
 LL |     twice(x) != twice(ONE as f64);
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(twice(x) - twice(ONE as f64)).abs() > error_margin`
-   |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64`
-  --> tests/ui/float_cmp.rs:106:5
+  --> tests/ui/float_cmp.rs:103:5
    |
 LL |     NON_ZERO_ARRAY[i] == NON_ZERO_ARRAY[j];
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(NON_ZERO_ARRAY[i] - NON_ZERO_ARRAY[j]).abs() < error_margin`
-   |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64` arrays
-  --> tests/ui/float_cmp.rs:113:5
+  --> tests/ui/float_cmp.rs:109:5
    |
 LL |     a1 == a2;
    |     ^^^^^^^^
-   |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64`
-  --> tests/ui/float_cmp.rs:116:5
+  --> tests/ui/float_cmp.rs:111:5
    |
 LL |     a1[0] == a2[0];
    |     ^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(a1[0] - a2[0]).abs() < error_margin`
-   |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui/float_cmp_const.rs b/src/tools/clippy/tests/ui/float_cmp_const.rs
index 47ea0e19c68..08180556437 100644
--- a/src/tools/clippy/tests/ui/float_cmp_const.rs
+++ b/src/tools/clippy/tests/ui/float_cmp_const.rs
@@ -15,28 +15,21 @@ fn main() {
     // has errors
     1f32 == ONE;
     //~^ ERROR: strict comparison of `f32` or `f64` constant
-    //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
     TWO == ONE;
     //~^ ERROR: strict comparison of `f32` or `f64` constant
-    //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
     TWO != ONE;
     //~^ ERROR: strict comparison of `f32` or `f64` constant
-    //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
     ONE + ONE == TWO;
     //~^ ERROR: strict comparison of `f32` or `f64` constant
-    //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
     let x = 1;
     x as f32 == ONE;
     //~^ ERROR: strict comparison of `f32` or `f64` constant
-    //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
     let v = 0.9;
     v == ONE;
     //~^ ERROR: strict comparison of `f32` or `f64` constant
-    //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
     v != ONE;
     //~^ ERROR: strict comparison of `f32` or `f64` constant
-    //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
     // no errors, lower than or greater than comparisons
     v < ONE;
@@ -70,5 +63,4 @@ fn main() {
     // has errors
     NON_ZERO_ARRAY == NON_ZERO_ARRAY2;
     //~^ ERROR: strict comparison of `f32` or `f64` constant arrays
-    //~| NOTE: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 }
diff --git a/src/tools/clippy/tests/ui/float_cmp_const.stderr b/src/tools/clippy/tests/ui/float_cmp_const.stderr
index bffd2acc2e0..4f88746e958 100644
--- a/src/tools/clippy/tests/ui/float_cmp_const.stderr
+++ b/src/tools/clippy/tests/ui/float_cmp_const.stderr
@@ -4,65 +4,50 @@ error: strict comparison of `f32` or `f64` constant
 LL |     1f32 == ONE;
    |     ^^^^^^^^^^^ help: consider comparing them within some margin of error: `(1f32 - ONE).abs() < error_margin`
    |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
    = note: `-D clippy::float-cmp-const` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::float_cmp_const)]`
 
 error: strict comparison of `f32` or `f64` constant
-  --> tests/ui/float_cmp_const.rs:19:5
+  --> tests/ui/float_cmp_const.rs:18:5
    |
 LL |     TWO == ONE;
    |     ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() < error_margin`
-   |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64` constant
-  --> tests/ui/float_cmp_const.rs:22:5
+  --> tests/ui/float_cmp_const.rs:20:5
    |
 LL |     TWO != ONE;
    |     ^^^^^^^^^^ help: consider comparing them within some margin of error: `(TWO - ONE).abs() > error_margin`
-   |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64` constant
-  --> tests/ui/float_cmp_const.rs:25:5
+  --> tests/ui/float_cmp_const.rs:22:5
    |
 LL |     ONE + ONE == TWO;
    |     ^^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(ONE + ONE - TWO).abs() < error_margin`
-   |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64` constant
-  --> tests/ui/float_cmp_const.rs:29:5
+  --> tests/ui/float_cmp_const.rs:25:5
    |
 LL |     x as f32 == ONE;
    |     ^^^^^^^^^^^^^^^ help: consider comparing them within some margin of error: `(x as f32 - ONE).abs() < error_margin`
-   |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64` constant
-  --> tests/ui/float_cmp_const.rs:34:5
+  --> tests/ui/float_cmp_const.rs:29:5
    |
 LL |     v == ONE;
    |     ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() < error_margin`
-   |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64` constant
-  --> tests/ui/float_cmp_const.rs:37:5
+  --> tests/ui/float_cmp_const.rs:31:5
    |
 LL |     v != ONE;
    |     ^^^^^^^^ help: consider comparing them within some margin of error: `(v - ONE).abs() > error_margin`
-   |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: strict comparison of `f32` or `f64` constant arrays
-  --> tests/ui/float_cmp_const.rs:71:5
+  --> tests/ui/float_cmp_const.rs:64:5
    |
 LL |     NON_ZERO_ARRAY == NON_ZERO_ARRAY2;
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-   |
-   = note: `f32::EPSILON` and `f64::EPSILON` are available for the `error_margin`
 
 error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/numbered_fields.fixed b/src/tools/clippy/tests/ui/init_numbered_fields.fixed
index 108520eed38..dca4e8da4d2 100644
--- a/src/tools/clippy/tests/ui/numbered_fields.fixed
+++ b/src/tools/clippy/tests/ui/init_numbered_fields.fixed
@@ -39,4 +39,13 @@ fn main() {
     struct TupleStructVec(Vec<usize>);
 
     let _ = TupleStructVec(vec![0, 1, 2, 3]);
+
+    {
+        struct S(i32, i32);
+        let mut iter = [1i32, 1i32].into_iter();
+        let _ = S {
+            1: iter.next().unwrap(),
+            0: iter.next().unwrap(),
+        };
+    }
 }
diff --git a/src/tools/clippy/tests/ui/numbered_fields.rs b/src/tools/clippy/tests/ui/init_numbered_fields.rs
index c718661a682..8cb34705b4f 100644
--- a/src/tools/clippy/tests/ui/numbered_fields.rs
+++ b/src/tools/clippy/tests/ui/init_numbered_fields.rs
@@ -47,4 +47,13 @@ fn main() {
     struct TupleStructVec(Vec<usize>);
 
     let _ = TupleStructVec { 0: vec![0, 1, 2, 3] };
+
+    {
+        struct S(i32, i32);
+        let mut iter = [1i32, 1i32].into_iter();
+        let _ = S {
+            1: iter.next().unwrap(),
+            0: iter.next().unwrap(),
+        };
+    }
 }
diff --git a/src/tools/clippy/tests/ui/numbered_fields.stderr b/src/tools/clippy/tests/ui/init_numbered_fields.stderr
index 9d3f59cd376..f176e0c2ff3 100644
--- a/src/tools/clippy/tests/ui/numbered_fields.stderr
+++ b/src/tools/clippy/tests/ui/init_numbered_fields.stderr
@@ -1,5 +1,5 @@
 error: used a field initializer for a tuple struct
-  --> tests/ui/numbered_fields.rs:17:13
+  --> tests/ui/init_numbered_fields.rs:17:13
    |
 LL |       let _ = TupleStruct {
    |  _____________^
@@ -7,13 +7,13 @@ LL | |         0: 1u32,
 LL | |         1: 42,
 LL | |         2: 23u8,
 LL | |     };
-   | |_____^ help: try: `TupleStruct(1u32, 42, 23u8)`
+   | |_____^ help: use tuple initialization: `TupleStruct(1u32, 42, 23u8)`
    |
    = note: `-D clippy::init-numbered-fields` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::init_numbered_fields)]`
 
 error: used a field initializer for a tuple struct
-  --> tests/ui/numbered_fields.rs:24:13
+  --> tests/ui/init_numbered_fields.rs:24:13
    |
 LL |       let _ = TupleStruct {
    |  _____________^
@@ -21,13 +21,13 @@ LL | |         0: 1u32,
 LL | |         2: 2u8,
 LL | |         1: 3u32,
 LL | |     };
-   | |_____^ help: try: `TupleStruct(1u32, 3u32, 2u8)`
+   | |_____^ help: use tuple initialization: `TupleStruct(1u32, 3u32, 2u8)`
 
 error: used a field initializer for a tuple struct
-  --> tests/ui/numbered_fields.rs:49:13
+  --> tests/ui/init_numbered_fields.rs:49:13
    |
 LL |     let _ = TupleStructVec { 0: vec![0, 1, 2, 3] };
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `TupleStructVec(vec![0, 1, 2, 3])`
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use tuple initialization: `TupleStructVec(vec![0, 1, 2, 3])`
 
 error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/into_iter_without_iter.rs b/src/tools/clippy/tests/ui/into_iter_without_iter.rs
index c8b9076041a..109259d6975 100644
--- a/src/tools/clippy/tests/ui/into_iter_without_iter.rs
+++ b/src/tools/clippy/tests/ui/into_iter_without_iter.rs
@@ -185,3 +185,42 @@ pub mod issue11635 {
         }
     }
 }
+
+pub mod issue12964 {
+    pub struct MyIter<'a, T: 'a> {
+        iter: std::slice::Iter<'a, T>,
+    }
+
+    impl<'a, T> Iterator for MyIter<'a, T> {
+        type Item = &'a T;
+
+        fn next(&mut self) -> Option<Self::Item> {
+            self.iter.next()
+        }
+    }
+
+    pub struct MyContainer<T> {
+        inner: Vec<T>,
+    }
+
+    impl<T> MyContainer<T> {}
+
+    impl<T> MyContainer<T> {
+        #[must_use]
+        pub fn iter(&self) -> MyIter<'_, T> {
+            <&Self as IntoIterator>::into_iter(self)
+        }
+    }
+
+    impl<'a, T> IntoIterator for &'a MyContainer<T> {
+        type Item = &'a T;
+
+        type IntoIter = MyIter<'a, T>;
+
+        fn into_iter(self) -> Self::IntoIter {
+            Self::IntoIter {
+                iter: self.inner.as_slice().iter(),
+            }
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/iter_next_loop.rs b/src/tools/clippy/tests/ui/iter_next_loop.rs
index 548b799de44..d425f4da0e8 100644
--- a/src/tools/clippy/tests/ui/iter_next_loop.rs
+++ b/src/tools/clippy/tests/ui/iter_next_loop.rs
@@ -3,7 +3,7 @@
 
 fn main() {
     let x = [1, 2, 3, 4];
-    for _ in vec.iter().next() {}
+    for _ in x.iter().next() {}
 
     struct Unrelated(&'static [u8]);
     impl Unrelated {
diff --git a/src/tools/clippy/tests/ui/iter_next_loop.stderr b/src/tools/clippy/tests/ui/iter_next_loop.stderr
index 85c23f4e709..acc55031c3b 100644
--- a/src/tools/clippy/tests/ui/iter_next_loop.stderr
+++ b/src/tools/clippy/tests/ui/iter_next_loop.stderr
@@ -1,9 +1,11 @@
-error[E0423]: expected value, found macro `vec`
+error: you are iterating over `Iterator::next()` which is an Option; this will compile but is probably not what you want
   --> tests/ui/iter_next_loop.rs:6:14
    |
-LL |     for _ in vec.iter().next() {}
-   |              ^^^ not a value
+LL |     for _ in x.iter().next() {}
+   |              ^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::iter-next-loop` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::iter_next_loop)]`
 
 error: aborting due to 1 previous error
 
-For more information about this error, try `rustc --explain E0423`.
diff --git a/src/tools/clippy/tests/ui/manual_inspect.stderr b/src/tools/clippy/tests/ui/manual_inspect.stderr
index 8548c0cd294..0559b3bd661 100644
--- a/src/tools/clippy/tests/ui/manual_inspect.stderr
+++ b/src/tools/clippy/tests/ui/manual_inspect.stderr
@@ -1,4 +1,4 @@
-error: 
+error: using `map` over `inspect`
   --> tests/ui/manual_inspect.rs:5:21
    |
 LL |     let _ = Some(0).map(|x| {
@@ -12,7 +12,7 @@ LL ~     let _ = Some(0).inspect(|&x| {
 LL ~         println!("{}", x);
    |
 
-error: 
+error: using `map` over `inspect`
   --> tests/ui/manual_inspect.rs:10:21
    |
 LL |     let _ = Some(0).map(|x| {
@@ -24,7 +24,7 @@ LL ~     let _ = Some(0).inspect(|&x| {
 LL ~         println!("{x}");
    |
 
-error: 
+error: using `map` over `inspect`
   --> tests/ui/manual_inspect.rs:15:21
    |
 LL |     let _ = Some(0).map(|x| {
@@ -36,7 +36,7 @@ LL ~     let _ = Some(0).inspect(|&x| {
 LL ~         println!("{}", x * 5 + 1);
    |
 
-error: 
+error: using `map` over `inspect`
   --> tests/ui/manual_inspect.rs:20:21
    |
 LL |     let _ = Some(0).map(|x| {
@@ -50,7 +50,7 @@ LL |             panic!();
 LL ~         }
    |
 
-error: 
+error: using `map` over `inspect`
   --> tests/ui/manual_inspect.rs:27:21
    |
 LL |     let _ = Some(0).map(|x| {
@@ -65,7 +65,7 @@ LL |             panic!();
 LL ~         }
    |
 
-error: 
+error: using `map` over `inspect`
   --> tests/ui/manual_inspect.rs:78:41
    |
 LL |     let _ = Some((String::new(), 0u32)).map(|x| {
@@ -80,7 +80,7 @@ LL |             panic!();
 LL ~         }
    |
 
-error: 
+error: using `map` over `inspect`
   --> tests/ui/manual_inspect.rs:104:33
    |
 LL |     let _ = Some(String::new()).map(|x| {
@@ -98,7 +98,7 @@ LL |         }
 LL ~         println!("test");
    |
 
-error: 
+error: using `map` over `inspect`
   --> tests/ui/manual_inspect.rs:115:21
    |
 LL |     let _ = Some(0).map(|x| {
@@ -113,7 +113,7 @@ LL |             panic!();
 LL ~         }
    |
 
-error: 
+error: using `map` over `inspect`
   --> tests/ui/manual_inspect.rs:130:46
    |
 LL |         let _ = Some(Cell2(Cell::new(0u32))).map(|x| {
@@ -125,7 +125,7 @@ LL ~         let _ = Some(Cell2(Cell::new(0u32))).inspect(|x| {
 LL ~             x.0.set(1);
    |
 
-error: 
+error: using `map` over `inspect`
   --> tests/ui/manual_inspect.rs:146:34
    |
 LL |     let _: Result<_, ()> = Ok(0).map(|x| {
@@ -137,7 +137,7 @@ LL ~     let _: Result<_, ()> = Ok(0).inspect(|&x| {
 LL ~         println!("{}", x);
    |
 
-error: 
+error: using `map_err` over `inspect_err`
   --> tests/ui/manual_inspect.rs:151:35
    |
 LL |     let _: Result<(), _> = Err(0).map_err(|x| {
@@ -166,7 +166,7 @@ LL | |         .count();
    = note: `-D clippy::suspicious-map` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::suspicious_map)]`
 
-error: 
+error: using `map` over `inspect`
   --> tests/ui/manual_inspect.rs:158:10
    |
 LL |         .map(|x| {
diff --git a/src/tools/clippy/tests/ui/manual_rotate.fixed b/src/tools/clippy/tests/ui/manual_rotate.fixed
new file mode 100644
index 00000000000..5d33838a318
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_rotate.fixed
@@ -0,0 +1,31 @@
+#![warn(clippy::manual_rotate)]
+#![allow(unused)]
+fn main() {
+    let (x_u8, x_u16, x_u32, x_u64) = (1u8, 1u16, 1u32, 1u64);
+    let (x_i8, x_i16, x_i32, x_i64) = (1i8, 1i16, 1i32, 1i64);
+    let a_u32 = 1u32;
+    // True positives
+    let y_u8 = x_u8.rotate_right(3);
+    let y_u16 = x_u16.rotate_right(7);
+    let y_u32 = x_u32.rotate_right(8);
+    let y_u64 = x_u64.rotate_right(9);
+    let y_i8 = x_i8.rotate_right(3);
+    let y_i16 = x_i16.rotate_right(7);
+    let y_i32 = x_i32.rotate_right(8);
+    let y_i64 = x_i64.rotate_right(9);
+    // Plus also works instead of |
+    let y_u32_plus = x_u32.rotate_right(8);
+    // Complex expression
+    let y_u32_complex = (x_u32 | 3256).rotate_right(8);
+    let y_u64_as = (x_u32 as u64).rotate_right(8);
+
+    // False positives - can't be replaced with a rotation
+    let y_u8_false = (x_u8 >> 6) | (x_u8 << 3);
+    let y_u32_false = (x_u32 >> 8) | (x_u32 >> 24);
+    let y_u64_false2 = (x_u64 >> 9) & (x_u64 << 55);
+    // Variable mismatch
+    let y_u32_wrong_vars = (x_u32 >> 8) | (a_u32 << 24);
+    // Has side effects and therefore should not be matched
+    let mut l = vec![12_u8, 34];
+    let y = (l.pop().unwrap() << 3) + (l.pop().unwrap() >> 5);
+}
diff --git a/src/tools/clippy/tests/ui/manual_rotate.rs b/src/tools/clippy/tests/ui/manual_rotate.rs
new file mode 100644
index 00000000000..5377491fb1a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_rotate.rs
@@ -0,0 +1,31 @@
+#![warn(clippy::manual_rotate)]
+#![allow(unused)]
+fn main() {
+    let (x_u8, x_u16, x_u32, x_u64) = (1u8, 1u16, 1u32, 1u64);
+    let (x_i8, x_i16, x_i32, x_i64) = (1i8, 1i16, 1i32, 1i64);
+    let a_u32 = 1u32;
+    // True positives
+    let y_u8 = (x_u8 >> 3) | (x_u8 << 5);
+    let y_u16 = (x_u16 >> 7) | (x_u16 << 9);
+    let y_u32 = (x_u32 >> 8) | (x_u32 << 24);
+    let y_u64 = (x_u64 >> 9) | (x_u64 << 55);
+    let y_i8 = (x_i8 >> 3) | (x_i8 << 5);
+    let y_i16 = (x_i16 >> 7) | (x_i16 << 9);
+    let y_i32 = (x_i32 >> 8) | (x_i32 << 24);
+    let y_i64 = (x_i64 >> 9) | (x_i64 << 55);
+    // Plus also works instead of |
+    let y_u32_plus = (x_u32 >> 8) + (x_u32 << 24);
+    // Complex expression
+    let y_u32_complex = ((x_u32 | 3256) >> 8) | ((x_u32 | 3256) << 24);
+    let y_u64_as = (x_u32 as u64 >> 8) | ((x_u32 as u64) << 56);
+
+    // False positives - can't be replaced with a rotation
+    let y_u8_false = (x_u8 >> 6) | (x_u8 << 3);
+    let y_u32_false = (x_u32 >> 8) | (x_u32 >> 24);
+    let y_u64_false2 = (x_u64 >> 9) & (x_u64 << 55);
+    // Variable mismatch
+    let y_u32_wrong_vars = (x_u32 >> 8) | (a_u32 << 24);
+    // Has side effects and therefore should not be matched
+    let mut l = vec![12_u8, 34];
+    let y = (l.pop().unwrap() << 3) + (l.pop().unwrap() >> 5);
+}
diff --git a/src/tools/clippy/tests/ui/manual_rotate.stderr b/src/tools/clippy/tests/ui/manual_rotate.stderr
new file mode 100644
index 00000000000..52da0861f70
--- /dev/null
+++ b/src/tools/clippy/tests/ui/manual_rotate.stderr
@@ -0,0 +1,71 @@
+error: there is no need to manually implement bit rotation
+  --> tests/ui/manual_rotate.rs:8:16
+   |
+LL |     let y_u8 = (x_u8 >> 3) | (x_u8 << 5);
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u8.rotate_right(3)`
+   |
+   = note: `-D clippy::manual-rotate` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::manual_rotate)]`
+
+error: there is no need to manually implement bit rotation
+  --> tests/ui/manual_rotate.rs:9:17
+   |
+LL |     let y_u16 = (x_u16 >> 7) | (x_u16 << 9);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u16.rotate_right(7)`
+
+error: there is no need to manually implement bit rotation
+  --> tests/ui/manual_rotate.rs:10:17
+   |
+LL |     let y_u32 = (x_u32 >> 8) | (x_u32 << 24);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u32.rotate_right(8)`
+
+error: there is no need to manually implement bit rotation
+  --> tests/ui/manual_rotate.rs:11:17
+   |
+LL |     let y_u64 = (x_u64 >> 9) | (x_u64 << 55);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u64.rotate_right(9)`
+
+error: there is no need to manually implement bit rotation
+  --> tests/ui/manual_rotate.rs:12:16
+   |
+LL |     let y_i8 = (x_i8 >> 3) | (x_i8 << 5);
+   |                ^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i8.rotate_right(3)`
+
+error: there is no need to manually implement bit rotation
+  --> tests/ui/manual_rotate.rs:13:17
+   |
+LL |     let y_i16 = (x_i16 >> 7) | (x_i16 << 9);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i16.rotate_right(7)`
+
+error: there is no need to manually implement bit rotation
+  --> tests/ui/manual_rotate.rs:14:17
+   |
+LL |     let y_i32 = (x_i32 >> 8) | (x_i32 << 24);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i32.rotate_right(8)`
+
+error: there is no need to manually implement bit rotation
+  --> tests/ui/manual_rotate.rs:15:17
+   |
+LL |     let y_i64 = (x_i64 >> 9) | (x_i64 << 55);
+   |                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_i64.rotate_right(9)`
+
+error: there is no need to manually implement bit rotation
+  --> tests/ui/manual_rotate.rs:17:22
+   |
+LL |     let y_u32_plus = (x_u32 >> 8) + (x_u32 << 24);
+   |                      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `x_u32.rotate_right(8)`
+
+error: there is no need to manually implement bit rotation
+  --> tests/ui/manual_rotate.rs:19:25
+   |
+LL |     let y_u32_complex = ((x_u32 | 3256) >> 8) | ((x_u32 | 3256) << 24);
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `(x_u32 | 3256).rotate_right(8)`
+
+error: there is no need to manually implement bit rotation
+  --> tests/ui/manual_rotate.rs:20:20
+   |
+LL |     let y_u64_as = (x_u32 as u64 >> 8) | ((x_u32 as u64) << 56);
+   |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: this expression can be rewritten as: `(x_u32 as u64).rotate_right(8)`
+
+error: aborting due to 11 previous errors
+
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
index 2750e0cdf3f..2c6e1e92da0 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/cant_be_const.rs
@@ -7,6 +7,7 @@
 
 #![warn(clippy::missing_const_for_fn)]
 #![feature(start)]
+#![feature(type_alias_impl_trait)]
 
 extern crate helper;
 extern crate proc_macros;
@@ -180,4 +181,21 @@ mod msrv {
             unsafe { *self.1 as usize }
         }
     }
+
+    #[clippy::msrv = "1.61"]
+    extern "C" fn c() {}
+}
+
+mod with_extern {
+    extern "C-unwind" fn c_unwind() {}
+    extern "system" fn system() {}
+    extern "system-unwind" fn system_unwind() {}
+}
+
+mod with_ty_alias {
+    type Foo = impl std::fmt::Debug;
+
+    fn foo(_: Foo) {
+        let _: Foo = 1;
+    }
 }
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed
index f8fc935f367..dbd739eee13 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed
@@ -143,6 +143,21 @@ mod msrv {
         let bar = Bar { val: 1 };
         let _ = unsafe { bar.val };
     }
+
+    #[clippy::msrv = "1.62"]
+    mod with_extern {
+        const extern "C" fn c() {}
+        //~^ ERROR: this could be a `const fn`
+
+        #[rustfmt::skip]
+        const extern fn implicit_c() {}
+        //~^ ERROR: this could be a `const fn`
+
+        // any item functions in extern block won't trigger this lint
+        extern "C" {
+            fn c_in_block();
+        }
+    }
 }
 
 mod issue12677 {
@@ -174,3 +189,18 @@ mod issue12677 {
         }
     }
 }
+
+mod with_ty_alias {
+    trait FooTrait {
+        type Foo: std::fmt::Debug;
+        fn bar(_: Self::Foo) {}
+    }
+    impl FooTrait for () {
+        type Foo = i32;
+    }
+    // NOTE: When checking the type of a function param, make sure it is not an alias with
+    // `AliasTyKind::Projection` before calling `TyCtxt::type_of` to find out what the actual type
+    // is. Because the associate ty could have no default, therefore would cause ICE, as demostrated
+    // in this test.
+    const fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {}
+}
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
index 5e4e2c58e5a..4ac56f4c803 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs
@@ -143,6 +143,21 @@ mod msrv {
         let bar = Bar { val: 1 };
         let _ = unsafe { bar.val };
     }
+
+    #[clippy::msrv = "1.62"]
+    mod with_extern {
+        extern "C" fn c() {}
+        //~^ ERROR: this could be a `const fn`
+
+        #[rustfmt::skip]
+        extern fn implicit_c() {}
+        //~^ ERROR: this could be a `const fn`
+
+        // any item functions in extern block won't trigger this lint
+        extern "C" {
+            fn c_in_block();
+        }
+    }
 }
 
 mod issue12677 {
@@ -174,3 +189,18 @@ mod issue12677 {
         }
     }
 }
+
+mod with_ty_alias {
+    trait FooTrait {
+        type Foo: std::fmt::Debug;
+        fn bar(_: Self::Foo) {}
+    }
+    impl FooTrait for () {
+        type Foo = i32;
+    }
+    // NOTE: When checking the type of a function param, make sure it is not an alias with
+    // `AliasTyKind::Projection` before calling `TyCtxt::type_of` to find out what the actual type
+    // is. Because the associate ty could have no default, therefore would cause ICE, as demostrated
+    // in this test.
+    fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {}
+}
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
index 8302b074127..fb4db703103 100644
--- a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr
@@ -211,7 +211,29 @@ LL |     const fn union_access_can_be_const() {
    |     +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:155:9
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:149:9
+   |
+LL |         extern "C" fn c() {}
+   |         ^^^^^^^^^^^^^^^^^^^^
+   |
+help: make the function `const`
+   |
+LL |         const extern "C" fn c() {}
+   |         +++++
+
+error: this could be a `const fn`
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:153:9
+   |
+LL |         extern fn implicit_c() {}
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: make the function `const`
+   |
+LL |         const extern fn implicit_c() {}
+   |         +++++
+
+error: this could be a `const fn`
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:170:9
    |
 LL | /         pub fn new(strings: Vec<String>) -> Self {
 LL | |             Self { strings }
@@ -224,7 +246,7 @@ LL |         pub const fn new(strings: Vec<String>) -> Self {
    |             +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:160:9
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:175:9
    |
 LL | /         pub fn empty() -> Self {
 LL | |             Self { strings: Vec::new() }
@@ -237,7 +259,7 @@ LL |         pub const fn empty() -> Self {
    |             +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:171:9
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:186:9
    |
 LL | /         pub fn new(text: String) -> Self {
 LL | |             let vec = Vec::new();
@@ -250,5 +272,16 @@ help: make the function `const`
 LL |         pub const fn new(text: String) -> Self {
    |             +++++
 
-error: aborting due to 18 previous errors
+error: this could be a `const fn`
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:205:5
+   |
+LL |     fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {}
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: make the function `const`
+   |
+LL |     const fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {}
+   |     +++++
+
+error: aborting due to 21 previous errors
 
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.fixed b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.fixed
new file mode 100644
index 00000000000..c103db536ab
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.fixed
@@ -0,0 +1,14 @@
+#![warn(clippy::missing_const_for_fn)]
+#![allow(unsupported_calling_conventions)]
+#![feature(const_extern_fn)]
+
+const extern "C-unwind" fn c_unwind() {}
+//~^ ERROR: this could be a `const fn`
+const extern "system" fn system() {}
+//~^ ERROR: this could be a `const fn`
+const extern "system-unwind" fn system_unwind() {}
+//~^ ERROR: this could be a `const fn`
+pub const extern "stdcall" fn std_call() {}
+//~^ ERROR: this could be a `const fn`
+pub const extern "stdcall-unwind" fn std_call_unwind() {}
+//~^ ERROR: this could be a `const fn`
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.rs b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.rs
new file mode 100644
index 00000000000..0f7020ae559
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.rs
@@ -0,0 +1,14 @@
+#![warn(clippy::missing_const_for_fn)]
+#![allow(unsupported_calling_conventions)]
+#![feature(const_extern_fn)]
+
+extern "C-unwind" fn c_unwind() {}
+//~^ ERROR: this could be a `const fn`
+extern "system" fn system() {}
+//~^ ERROR: this could be a `const fn`
+extern "system-unwind" fn system_unwind() {}
+//~^ ERROR: this could be a `const fn`
+pub extern "stdcall" fn std_call() {}
+//~^ ERROR: this could be a `const fn`
+pub extern "stdcall-unwind" fn std_call_unwind() {}
+//~^ ERROR: this could be a `const fn`
diff --git a/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.stderr b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.stderr
new file mode 100644
index 00000000000..036094a367b
--- /dev/null
+++ b/src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.stderr
@@ -0,0 +1,59 @@
+error: this could be a `const fn`
+  --> tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.rs:5:1
+   |
+LL | extern "C-unwind" fn c_unwind() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::missing-const-for-fn` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::missing_const_for_fn)]`
+help: make the function `const`
+   |
+LL | const extern "C-unwind" fn c_unwind() {}
+   | +++++
+
+error: this could be a `const fn`
+  --> tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.rs:7:1
+   |
+LL | extern "system" fn system() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: make the function `const`
+   |
+LL | const extern "system" fn system() {}
+   | +++++
+
+error: this could be a `const fn`
+  --> tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.rs:9:1
+   |
+LL | extern "system-unwind" fn system_unwind() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: make the function `const`
+   |
+LL | const extern "system-unwind" fn system_unwind() {}
+   | +++++
+
+error: this could be a `const fn`
+  --> tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.rs:11:1
+   |
+LL | pub extern "stdcall" fn std_call() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: make the function `const`
+   |
+LL | pub const extern "stdcall" fn std_call() {}
+   |     +++++
+
+error: this could be a `const fn`
+  --> tests/ui/missing_const_for_fn/could_be_const_with_const_extern_fn.rs:13:1
+   |
+LL | pub extern "stdcall-unwind" fn std_call_unwind() {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: make the function `const`
+   |
+LL | pub const extern "stdcall-unwind" fn std_call_unwind() {}
+   |     +++++
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/thread_local_initializer_can_be_made_const.fixed b/src/tools/clippy/tests/ui/missing_const_for_thread_local.fixed
index 4c9bd0bd863..90b31b9b5d2 100644
--- a/src/tools/clippy/tests/ui/thread_local_initializer_can_be_made_const.fixed
+++ b/src/tools/clippy/tests/ui/missing_const_for_thread_local.fixed
@@ -1,4 +1,4 @@
-#![warn(clippy::thread_local_initializer_can_be_made_const)]
+#![warn(clippy::missing_const_for_thread_local)]
 
 use std::cell::{Cell, RefCell};
 
diff --git a/src/tools/clippy/tests/ui/thread_local_initializer_can_be_made_const.rs b/src/tools/clippy/tests/ui/missing_const_for_thread_local.rs
index eb336f0dd19..f97e4848fd7 100644
--- a/src/tools/clippy/tests/ui/thread_local_initializer_can_be_made_const.rs
+++ b/src/tools/clippy/tests/ui/missing_const_for_thread_local.rs
@@ -1,4 +1,4 @@
-#![warn(clippy::thread_local_initializer_can_be_made_const)]
+#![warn(clippy::missing_const_for_thread_local)]
 
 use std::cell::{Cell, RefCell};
 
diff --git a/src/tools/clippy/tests/ui/thread_local_initializer_can_be_made_const.stderr b/src/tools/clippy/tests/ui/missing_const_for_thread_local.stderr
index b4f8bd822b0..c143a37454f 100644
--- a/src/tools/clippy/tests/ui/thread_local_initializer_can_be_made_const.stderr
+++ b/src/tools/clippy/tests/ui/missing_const_for_thread_local.stderr
@@ -1,38 +1,38 @@
 error: initializer for `thread_local` value can be made `const`
-  --> tests/ui/thread_local_initializer_can_be_made_const.rs:8:41
+  --> tests/ui/missing_const_for_thread_local.rs:8:41
    |
 LL |         static BUF_1: RefCell<String> = RefCell::new(String::new());
    |                                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `const { RefCell::new(String::new()) }`
    |
-   = note: `-D clippy::thread-local-initializer-can-be-made-const` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::thread_local_initializer_can_be_made_const)]`
+   = note: `-D clippy::missing-const-for-thread-local` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::missing_const_for_thread_local)]`
 
 error: initializer for `thread_local` value can be made `const`
-  --> tests/ui/thread_local_initializer_can_be_made_const.rs:18:29
+  --> tests/ui/missing_const_for_thread_local.rs:18:29
    |
 LL |         static SIMPLE:i32 = 1;
    |                             ^ help: replace with: `const { 1 }`
 
 error: initializer for `thread_local` value can be made `const`
-  --> tests/ui/thread_local_initializer_can_be_made_const.rs:24:59
+  --> tests/ui/missing_const_for_thread_local.rs:24:59
    |
 LL |         static BUF_3_CAN_BE_MADE_CONST: RefCell<String> = RefCell::new(String::new());
    |                                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `const { RefCell::new(String::new()) }`
 
 error: initializer for `thread_local` value can be made `const`
-  --> tests/ui/thread_local_initializer_can_be_made_const.rs:26:59
+  --> tests/ui/missing_const_for_thread_local.rs:26:59
    |
 LL |         static BUF_4_CAN_BE_MADE_CONST: RefCell<String> = RefCell::new(String::new());
    |                                                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace with: `const { RefCell::new(String::new()) }`
 
 error: initializer for `thread_local` value can be made `const`
-  --> tests/ui/thread_local_initializer_can_be_made_const.rs:32:31
+  --> tests/ui/missing_const_for_thread_local.rs:32:31
    |
 LL |         static PEEL_ME: i32 = { 1 };
    |                               ^^^^^ help: replace with: `const { 1 }`
 
 error: initializer for `thread_local` value can be made `const`
-  --> tests/ui/thread_local_initializer_can_be_made_const.rs:34:36
+  --> tests/ui/missing_const_for_thread_local.rs:34:36
    |
 LL |         static PEEL_ME_MANY: i32 = { let x = 1; x * x };
    |                                    ^^^^^^^^^^^^^^^^^^^^ help: replace with: `const { { let x = 1; x * x } }`
diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs
index eee62122fdf..162ec82aede 100644
--- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs
+++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.rs
@@ -232,43 +232,48 @@ async fn async_vec2(b: &mut Vec<bool>) {
 }
 fn non_mut(n: &str) {}
 //Should warn
-pub async fn call_in_closure1(n: &mut str) {
+async fn call_in_closure1(n: &mut str) {
     (|| non_mut(n))()
 }
 fn str_mut(str: &mut String) -> bool {
     str.pop().is_some()
 }
 //Should not warn
-pub async fn call_in_closure2(str: &mut String) {
+async fn call_in_closure2(str: &mut String) {
     (|| str_mut(str))();
 }
 
 // Should not warn.
-pub async fn closure(n: &mut usize) -> impl '_ + FnMut() {
+async fn closure(n: &mut usize) -> impl '_ + FnMut() {
     || {
         *n += 1;
     }
 }
 
 // Should warn.
-pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize {
+fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize {
     //~^ ERROR: this argument is a mutable reference, but not used mutably
     || *n + 1
 }
 
 // Should not warn.
-pub async fn closure3(n: &mut usize) {
+async fn closure3(n: &mut usize) {
     (|| *n += 1)();
 }
 
 // Should warn.
-pub async fn closure4(n: &mut usize) {
+async fn closure4(n: &mut usize) {
     //~^ ERROR: this argument is a mutable reference, but not used mutably
     (|| {
         let _x = *n + 1;
     })();
 }
 
+// Should not warn: pub
+pub fn pub_foo(s: &mut Vec<u32>, b: &u32, x: &mut u32) {
+    *x += *b + s.len() as u32;
+}
+
 // Should not warn.
 async fn _f(v: &mut Vec<()>) {
     let x = || v.pop();
@@ -365,4 +370,5 @@ fn main() {
     used_as_path;
     let _: fn(&mut u32) = passed_as_local;
     let _ = if v[0] == 0 { ty_unify_1 } else { ty_unify_2 };
+    pub_foo(&mut v, &0, &mut u);
 }
diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr
index 51e3ba37ded..f462fa9099e 100644
--- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr
+++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut.stderr
@@ -108,109 +108,103 @@ LL | async fn inner_async3(x: &mut i32, y: &mut u32) {
    |                          ^^^^^^^^ help: consider changing to: `&i32`
 
 error: this argument is a mutable reference, but not used mutably
-  --> tests/ui/needless_pass_by_ref_mut.rs:235:34
+  --> tests/ui/needless_pass_by_ref_mut.rs:235:30
    |
-LL | pub async fn call_in_closure1(n: &mut str) {
-   |                                  ^^^^^^^^ help: consider changing to: `&str`
-   |
-   = warning: changing this function will impact semver compatibility
+LL | async fn call_in_closure1(n: &mut str) {
+   |                              ^^^^^^^^ help: consider changing to: `&str`
 
 error: this argument is a mutable reference, but not used mutably
-  --> tests/ui/needless_pass_by_ref_mut.rs:254:20
-   |
-LL | pub fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize {
-   |                    ^^^^^^^^^^ help: consider changing to: `&usize`
+  --> tests/ui/needless_pass_by_ref_mut.rs:254:16
    |
-   = warning: changing this function will impact semver compatibility
+LL | fn closure2(n: &mut usize) -> impl '_ + FnMut() -> usize {
+   |                ^^^^^^^^^^ help: consider changing to: `&usize`
 
 error: this argument is a mutable reference, but not used mutably
-  --> tests/ui/needless_pass_by_ref_mut.rs:265:26
-   |
-LL | pub async fn closure4(n: &mut usize) {
-   |                          ^^^^^^^^^^ help: consider changing to: `&usize`
+  --> tests/ui/needless_pass_by_ref_mut.rs:265:22
    |
-   = warning: changing this function will impact semver compatibility
+LL | async fn closure4(n: &mut usize) {
+   |                      ^^^^^^^^^^ help: consider changing to: `&usize`
 
 error: this argument is a mutable reference, but not used mutably
-  --> tests/ui/needless_pass_by_ref_mut.rs:314:12
+  --> tests/ui/needless_pass_by_ref_mut.rs:319:12
    |
 LL |     fn bar(&mut self) {}
    |            ^^^^^^^^^ help: consider changing to: `&self`
 
 error: this argument is a mutable reference, but not used mutably
-  --> tests/ui/needless_pass_by_ref_mut.rs:316:18
+  --> tests/ui/needless_pass_by_ref_mut.rs:321:18
    |
 LL |     async fn foo(&mut self, u: &mut i32, v: &mut u32) {
    |                  ^^^^^^^^^ help: consider changing to: `&self`
 
 error: this argument is a mutable reference, but not used mutably
-  --> tests/ui/needless_pass_by_ref_mut.rs:316:45
+  --> tests/ui/needless_pass_by_ref_mut.rs:321:45
    |
 LL |     async fn foo(&mut self, u: &mut i32, v: &mut u32) {
    |                                             ^^^^^^^^ help: consider changing to: `&u32`
 
 error: this argument is a mutable reference, but not used mutably
-  --> tests/ui/needless_pass_by_ref_mut.rs:324:46
+  --> tests/ui/needless_pass_by_ref_mut.rs:329:46
    |
 LL |     async fn foo2(&mut self, u: &mut i32, v: &mut u32) {
    |                                              ^^^^^^^^ help: consider changing to: `&u32`
 
 error: this argument is a mutable reference, but not used mutably
-  --> tests/ui/needless_pass_by_ref_mut.rs:340:18
+  --> tests/ui/needless_pass_by_ref_mut.rs:345:18
    |
 LL | fn _empty_tup(x: &mut (())) {}
    |                  ^^^^^^^^^ help: consider changing to: `&()`
 
 error: this argument is a mutable reference, but not used mutably
-  --> tests/ui/needless_pass_by_ref_mut.rs:341:19
+  --> tests/ui/needless_pass_by_ref_mut.rs:346:19
    |
 LL | fn _single_tup(x: &mut ((i32,))) {}
    |                   ^^^^^^^^^^^^^ help: consider changing to: `&(i32,)`
 
 error: this argument is a mutable reference, but not used mutably
-  --> tests/ui/needless_pass_by_ref_mut.rs:342:18
+  --> tests/ui/needless_pass_by_ref_mut.rs:347:18
    |
 LL | fn _multi_tup(x: &mut ((i32, u32))) {}
    |                  ^^^^^^^^^^^^^^^^^ help: consider changing to: `&(i32, u32)`
 
 error: this argument is a mutable reference, but not used mutably
-  --> tests/ui/needless_pass_by_ref_mut.rs:343:11
+  --> tests/ui/needless_pass_by_ref_mut.rs:348:11
    |
 LL | fn _fn(x: &mut (fn())) {}
    |           ^^^^^^^^^^^ help: consider changing to: `&fn()`
 
 error: this argument is a mutable reference, but not used mutably
-  --> tests/ui/needless_pass_by_ref_mut.rs:345:23
+  --> tests/ui/needless_pass_by_ref_mut.rs:350:23
    |
 LL | fn _extern_rust_fn(x: &mut extern "Rust" fn()) {}
    |                       ^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&extern "Rust" fn()`
 
 error: this argument is a mutable reference, but not used mutably
-  --> tests/ui/needless_pass_by_ref_mut.rs:346:20
+  --> tests/ui/needless_pass_by_ref_mut.rs:351:20
    |
 LL | fn _extern_c_fn(x: &mut extern "C" fn()) {}
    |                    ^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&extern "C" fn()`
 
 error: this argument is a mutable reference, but not used mutably
-  --> tests/ui/needless_pass_by_ref_mut.rs:347:18
+  --> tests/ui/needless_pass_by_ref_mut.rs:352:18
    |
 LL | fn _unsafe_fn(x: &mut unsafe fn()) {}
    |                  ^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe fn()`
 
 error: this argument is a mutable reference, but not used mutably
-  --> tests/ui/needless_pass_by_ref_mut.rs:348:25
+  --> tests/ui/needless_pass_by_ref_mut.rs:353:25
    |
 LL | fn _unsafe_extern_fn(x: &mut unsafe extern "C" fn()) {}
    |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn()`
 
 error: this argument is a mutable reference, but not used mutably
-  --> tests/ui/needless_pass_by_ref_mut.rs:349:20
+  --> tests/ui/needless_pass_by_ref_mut.rs:354:20
    |
 LL | fn _fn_with_arg(x: &mut unsafe extern "C" fn(i32)) {}
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn(i32)`
 
 error: this argument is a mutable reference, but not used mutably
-  --> tests/ui/needless_pass_by_ref_mut.rs:350:20
+  --> tests/ui/needless_pass_by_ref_mut.rs:355:20
    |
 LL | fn _fn_with_ret(x: &mut unsafe extern "C" fn() -> (i32)) {}
    |                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider changing to: `&unsafe extern "C" fn() -> (i32)`
diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut2.fixed b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut2.fixed
index 3c2576213cd..f26b39ea6a1 100644
--- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut2.fixed
+++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut2.fixed
@@ -5,7 +5,7 @@
 #![allow(clippy::redundant_closure_call)]
 #![warn(clippy::needless_pass_by_ref_mut)]
 
-pub async fn inner_async3(x: &i32, y: &mut u32) {
+async fn inner_async3(x: &i32, y: &mut u32) {
     //~^ ERROR: this argument is a mutable reference, but not used mutably
     async {
         *y += 1;
@@ -13,7 +13,7 @@ pub async fn inner_async3(x: &i32, y: &mut u32) {
     .await;
 }
 
-pub async fn inner_async4(u: &mut i32, v: &u32) {
+async fn inner_async4(u: &mut i32, v: &u32) {
     //~^ ERROR: this argument is a mutable reference, but not used mutably
     async {
         *u += 1;
diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut2.rs b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut2.rs
index 34b0b564deb..4220215b1fe 100644
--- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut2.rs
+++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut2.rs
@@ -5,7 +5,7 @@
 #![allow(clippy::redundant_closure_call)]
 #![warn(clippy::needless_pass_by_ref_mut)]
 
-pub async fn inner_async3(x: &mut i32, y: &mut u32) {
+async fn inner_async3(x: &mut i32, y: &mut u32) {
     //~^ ERROR: this argument is a mutable reference, but not used mutably
     async {
         *y += 1;
@@ -13,7 +13,7 @@ pub async fn inner_async3(x: &mut i32, y: &mut u32) {
     .await;
 }
 
-pub async fn inner_async4(u: &mut i32, v: &mut u32) {
+async fn inner_async4(u: &mut i32, v: &mut u32) {
     //~^ ERROR: this argument is a mutable reference, but not used mutably
     async {
         *u += 1;
diff --git a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut2.stderr b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut2.stderr
index c8753603225..1c0136cf5d5 100644
--- a/src/tools/clippy/tests/ui/needless_pass_by_ref_mut2.stderr
+++ b/src/tools/clippy/tests/ui/needless_pass_by_ref_mut2.stderr
@@ -1,20 +1,17 @@
 error: this argument is a mutable reference, but not used mutably
-  --> tests/ui/needless_pass_by_ref_mut2.rs:8:30
+  --> tests/ui/needless_pass_by_ref_mut2.rs:8:26
    |
-LL | pub async fn inner_async3(x: &mut i32, y: &mut u32) {
-   |                              ^^^^^^^^ help: consider changing to: `&i32`
+LL | async fn inner_async3(x: &mut i32, y: &mut u32) {
+   |                          ^^^^^^^^ help: consider changing to: `&i32`
    |
-   = warning: changing this function will impact semver compatibility
    = note: `-D clippy::needless-pass-by-ref-mut` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::needless_pass_by_ref_mut)]`
 
 error: this argument is a mutable reference, but not used mutably
-  --> tests/ui/needless_pass_by_ref_mut2.rs:16:43
+  --> tests/ui/needless_pass_by_ref_mut2.rs:16:39
    |
-LL | pub async fn inner_async4(u: &mut i32, v: &mut u32) {
-   |                                           ^^^^^^^^ help: consider changing to: `&u32`
-   |
-   = warning: changing this function will impact semver compatibility
+LL | async fn inner_async4(u: &mut i32, v: &mut u32) {
+   |                                       ^^^^^^^^ help: consider changing to: `&u32`
 
 error: aborting due to 2 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_return.fixed b/src/tools/clippy/tests/ui/needless_return.fixed
index 853f685f04c..fc4129e1db8 100644
--- a/src/tools/clippy/tests/ui/needless_return.fixed
+++ b/src/tools/clippy/tests/ui/needless_return.fixed
@@ -228,12 +228,41 @@ fn needless_return_macro() -> String {
     format!("Hello {}", "world!")
 }
 
-fn issue_9361() -> i32 {
-    let n = 1;
-    #[allow(clippy::arithmetic_side_effects)]
+fn issue_9361(n: i32) -> i32 {
+    #[expect(clippy::arithmetic_side_effects)]
     return n + n;
 }
 
+mod issue_12998 {
+    fn expect_lint() -> i32 {
+        let x = 1;
+
+        #[expect(clippy::needless_return)]
+        return x;
+    }
+
+    fn expect_group() -> i32 {
+        let x = 1;
+
+        #[expect(clippy::style)]
+        return x;
+    }
+
+    fn expect_all() -> i32 {
+        let x = 1;
+
+        #[expect(clippy::all)]
+        return x;
+    }
+
+    fn expect_warnings() -> i32 {
+        let x = 1;
+
+        #[expect(warnings)]
+        return x;
+    }
+}
+
 fn issue8336(x: i32) -> bool {
     if x > 0 {
         println!("something");
diff --git a/src/tools/clippy/tests/ui/needless_return.rs b/src/tools/clippy/tests/ui/needless_return.rs
index e9c1e0e8ae8..61c7a02008f 100644
--- a/src/tools/clippy/tests/ui/needless_return.rs
+++ b/src/tools/clippy/tests/ui/needless_return.rs
@@ -236,12 +236,41 @@ fn needless_return_macro() -> String {
     return format!("Hello {}", "world!");
 }
 
-fn issue_9361() -> i32 {
-    let n = 1;
-    #[allow(clippy::arithmetic_side_effects)]
+fn issue_9361(n: i32) -> i32 {
+    #[expect(clippy::arithmetic_side_effects)]
     return n + n;
 }
 
+mod issue_12998 {
+    fn expect_lint() -> i32 {
+        let x = 1;
+
+        #[expect(clippy::needless_return)]
+        return x;
+    }
+
+    fn expect_group() -> i32 {
+        let x = 1;
+
+        #[expect(clippy::style)]
+        return x;
+    }
+
+    fn expect_all() -> i32 {
+        let x = 1;
+
+        #[expect(clippy::all)]
+        return x;
+    }
+
+    fn expect_warnings() -> i32 {
+        let x = 1;
+
+        #[expect(warnings)]
+        return x;
+    }
+}
+
 fn issue8336(x: i32) -> bool {
     if x > 0 {
         println!("something");
diff --git a/src/tools/clippy/tests/ui/needless_return.stderr b/src/tools/clippy/tests/ui/needless_return.stderr
index 6c891fe7ad3..ea9c230eafd 100644
--- a/src/tools/clippy/tests/ui/needless_return.stderr
+++ b/src/tools/clippy/tests/ui/needless_return.stderr
@@ -483,7 +483,7 @@ LL +     format!("Hello {}", "world!")
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:248:9
+  --> tests/ui/needless_return.rs:277:9
    |
 LL |         return true;
    |         ^^^^^^^^^^^
@@ -497,7 +497,7 @@ LL ~     }
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:250:9
+  --> tests/ui/needless_return.rs:279:9
    |
 LL |         return false;
    |         ^^^^^^^^^^^^
@@ -509,7 +509,7 @@ LL ~     }
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:257:13
+  --> tests/ui/needless_return.rs:286:13
    |
 LL |             return 10;
    |             ^^^^^^^^^
@@ -524,7 +524,7 @@ LL ~     }
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:260:13
+  --> tests/ui/needless_return.rs:289:13
    |
 LL |             return 100;
    |             ^^^^^^^^^^
@@ -537,7 +537,7 @@ LL ~     }
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:268:9
+  --> tests/ui/needless_return.rs:297:9
    |
 LL |         return 0;
    |         ^^^^^^^^
@@ -549,7 +549,7 @@ LL ~     }
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:275:13
+  --> tests/ui/needless_return.rs:304:13
    |
 LL |             return *(x as *const isize);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -564,7 +564,7 @@ LL ~     }
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:277:13
+  --> tests/ui/needless_return.rs:306:13
    |
 LL |             return !*(x as *const isize);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -577,7 +577,7 @@ LL ~     }
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:284:20
+  --> tests/ui/needless_return.rs:313:20
    |
 LL |           let _ = 42;
    |  ____________________^
@@ -594,7 +594,7 @@ LL +         let _ = 42;
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:291:20
+  --> tests/ui/needless_return.rs:320:20
    |
 LL |         let _ = 42; return;
    |                    ^^^^^^^
@@ -606,7 +606,7 @@ LL +         let _ = 42;
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:303:9
+  --> tests/ui/needless_return.rs:332:9
    |
 LL |         return Ok(format!("ok!"));
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -618,7 +618,7 @@ LL +         Ok(format!("ok!"))
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:305:9
+  --> tests/ui/needless_return.rs:334:9
    |
 LL |         return Err(format!("err!"));
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -630,7 +630,7 @@ LL +         Err(format!("err!"))
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:311:9
+  --> tests/ui/needless_return.rs:340:9
    |
 LL |         return if true { 1 } else { 2 };
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -642,7 +642,7 @@ LL +         if true { 1 } else { 2 }
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:315:9
+  --> tests/ui/needless_return.rs:344:9
    |
 LL |         return if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else { 5 };
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -654,7 +654,7 @@ LL +         (if b1 { 0 } else { 1 } | if b2 { 2 } else { 3 } | if b3 { 4 } else
    |
 
 error: unneeded `return` statement
-  --> tests/ui/needless_return.rs:336:5
+  --> tests/ui/needless_return.rs:365:5
    |
 LL |     return { "a".to_string() } + "b" + { "c" };
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/overflow_check_conditional.rs b/src/tools/clippy/tests/ui/overflow_check_conditional.rs
deleted file mode 100644
index a70bb3bc47b..00000000000
--- a/src/tools/clippy/tests/ui/overflow_check_conditional.rs
+++ /dev/null
@@ -1,36 +0,0 @@
-#![warn(clippy::overflow_check_conditional)]
-#![allow(clippy::needless_if)]
-
-fn test(a: u32, b: u32, c: u32) {
-    if a + b < a {}
-    //~^ ERROR: you are trying to use classic C overflow conditions that will fail in Rust
-    //~| NOTE: `-D clippy::overflow-check-conditional` implied by `-D warnings`
-    if a > a + b {}
-    //~^ ERROR: you are trying to use classic C overflow conditions that will fail in Rust
-    if a + b < b {}
-    //~^ ERROR: you are trying to use classic C overflow conditions that will fail in Rust
-    if b > a + b {}
-    //~^ ERROR: you are trying to use classic C overflow conditions that will fail in Rust
-    if a - b > b {}
-    //~^ ERROR: you are trying to use classic C underflow conditions that will fail in Rus
-    if b < a - b {}
-    //~^ ERROR: you are trying to use classic C underflow conditions that will fail in Rus
-    if a - b > a {}
-    //~^ ERROR: you are trying to use classic C underflow conditions that will fail in Rus
-    if a < a - b {}
-    //~^ ERROR: you are trying to use classic C underflow conditions that will fail in Rus
-    if a + b < c {}
-    if c > a + b {}
-    if a - b < c {}
-    if c > a - b {}
-    let i = 1.1;
-    let j = 2.2;
-    if i + j < i {}
-    if i - j < i {}
-    if i > i + j {}
-    if i - j < i {}
-}
-
-fn main() {
-    test(1, 2, 3)
-}
diff --git a/src/tools/clippy/tests/ui/overflow_check_conditional.stderr b/src/tools/clippy/tests/ui/overflow_check_conditional.stderr
deleted file mode 100644
index c14532bad5a..00000000000
--- a/src/tools/clippy/tests/ui/overflow_check_conditional.stderr
+++ /dev/null
@@ -1,53 +0,0 @@
-error: you are trying to use classic C overflow conditions that will fail in Rust
-  --> tests/ui/overflow_check_conditional.rs:5:8
-   |
-LL |     if a + b < a {}
-   |        ^^^^^^^^^
-   |
-   = note: `-D clippy::overflow-check-conditional` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::overflow_check_conditional)]`
-
-error: you are trying to use classic C overflow conditions that will fail in Rust
-  --> tests/ui/overflow_check_conditional.rs:8:8
-   |
-LL |     if a > a + b {}
-   |        ^^^^^^^^^
-
-error: you are trying to use classic C overflow conditions that will fail in Rust
-  --> tests/ui/overflow_check_conditional.rs:10:8
-   |
-LL |     if a + b < b {}
-   |        ^^^^^^^^^
-
-error: you are trying to use classic C overflow conditions that will fail in Rust
-  --> tests/ui/overflow_check_conditional.rs:12:8
-   |
-LL |     if b > a + b {}
-   |        ^^^^^^^^^
-
-error: you are trying to use classic C underflow conditions that will fail in Rust
-  --> tests/ui/overflow_check_conditional.rs:14:8
-   |
-LL |     if a - b > b {}
-   |        ^^^^^^^^^
-
-error: you are trying to use classic C underflow conditions that will fail in Rust
-  --> tests/ui/overflow_check_conditional.rs:16:8
-   |
-LL |     if b < a - b {}
-   |        ^^^^^^^^^
-
-error: you are trying to use classic C underflow conditions that will fail in Rust
-  --> tests/ui/overflow_check_conditional.rs:18:8
-   |
-LL |     if a - b > a {}
-   |        ^^^^^^^^^
-
-error: you are trying to use classic C underflow conditions that will fail in Rust
-  --> tests/ui/overflow_check_conditional.rs:20:8
-   |
-LL |     if a < a - b {}
-   |        ^^^^^^^^^
-
-error: aborting due to 8 previous errors
-
diff --git a/src/tools/clippy/tests/ui/panicking_overflow_checks.rs b/src/tools/clippy/tests/ui/panicking_overflow_checks.rs
new file mode 100644
index 00000000000..dc2ddeada1e
--- /dev/null
+++ b/src/tools/clippy/tests/ui/panicking_overflow_checks.rs
@@ -0,0 +1,27 @@
+#![warn(clippy::panicking_overflow_checks)]
+#![allow(clippy::needless_if)]
+
+fn test(a: u32, b: u32, c: u32) {
+    if a + b < a {} //~ panicking_overflow_checks
+    if a > a + b {} //~ panicking_overflow_checks
+    if a + b < b {} //~ panicking_overflow_checks
+    if b > a + b {} //~ panicking_overflow_checks
+    if a - b > b {}
+    if b < a - b {}
+    if a - b > a {} //~ panicking_overflow_checks
+    if a < a - b {} //~ panicking_overflow_checks
+    if a + b < c {}
+    if c > a + b {}
+    if a - b < c {}
+    if c > a - b {}
+    let i = 1.1;
+    let j = 2.2;
+    if i + j < i {}
+    if i - j < i {}
+    if i > i + j {}
+    if i - j < i {}
+}
+
+fn main() {
+    test(1, 2, 3)
+}
diff --git a/src/tools/clippy/tests/ui/panicking_overflow_checks.stderr b/src/tools/clippy/tests/ui/panicking_overflow_checks.stderr
new file mode 100644
index 00000000000..1fae0457889
--- /dev/null
+++ b/src/tools/clippy/tests/ui/panicking_overflow_checks.stderr
@@ -0,0 +1,41 @@
+error: you are trying to use classic C overflow conditions that will fail in Rust
+  --> tests/ui/panicking_overflow_checks.rs:5:8
+   |
+LL |     if a + b < a {}
+   |        ^^^^^^^^^
+   |
+   = note: `-D clippy::panicking-overflow-checks` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::panicking_overflow_checks)]`
+
+error: you are trying to use classic C overflow conditions that will fail in Rust
+  --> tests/ui/panicking_overflow_checks.rs:6:8
+   |
+LL |     if a > a + b {}
+   |        ^^^^^^^^^
+
+error: you are trying to use classic C overflow conditions that will fail in Rust
+  --> tests/ui/panicking_overflow_checks.rs:7:8
+   |
+LL |     if a + b < b {}
+   |        ^^^^^^^^^
+
+error: you are trying to use classic C overflow conditions that will fail in Rust
+  --> tests/ui/panicking_overflow_checks.rs:8:8
+   |
+LL |     if b > a + b {}
+   |        ^^^^^^^^^
+
+error: you are trying to use classic C overflow conditions that will fail in Rust
+  --> tests/ui/panicking_overflow_checks.rs:11:8
+   |
+LL |     if a - b > a {}
+   |        ^^^^^^^^^
+
+error: you are trying to use classic C overflow conditions that will fail in Rust
+  --> tests/ui/panicking_overflow_checks.rs:12:8
+   |
+LL |     if a < a - b {}
+   |        ^^^^^^^^^
+
+error: aborting due to 6 previous errors
+
diff --git a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
index e162f35baf5..18462620b0a 100644
--- a/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
+++ b/src/tools/clippy/tests/ui/ptr_as_ptr.stderr
@@ -1,4 +1,4 @@
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:18:33
    |
 LL |         *unsafe { Box::from_raw(Box::into_raw(Box::new(o)) as *mut super::issue_11278_a::T<String>) }
@@ -7,37 +7,37 @@ LL |         *unsafe { Box::from_raw(Box::into_raw(Box::new(o)) as *mut super::i
    = note: `-D clippy::ptr-as-ptr` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::ptr_as_ptr)]`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:27:13
    |
 LL |     let _ = ptr as *const i32;
    |             ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::<i32>()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:28:13
    |
 LL |     let _ = mut_ptr as *mut i32;
    |             ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:33:17
    |
 LL |         let _ = *ptr_ptr as *const i32;
    |                 ^^^^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `(*ptr_ptr).cast::<i32>()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:46:25
    |
 LL |     let _: *const i32 = ptr as *const _;
    |                         ^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:47:23
    |
 LL |     let _: *mut i32 = mut_ptr as _;
    |                       ^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:50:21
    |
 LL |     let _ = inline!($ptr as *const i32);
@@ -45,157 +45,157 @@ LL |     let _ = inline!($ptr as *const i32);
    |
    = note: this error originates in the macro `__inline_mac_fn_main` (in Nightly builds, run with -Z macro-backtrace for more info)
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:71:13
    |
 LL |     let _ = ptr as *const i32;
    |             ^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `ptr.cast::<i32>()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:72:13
    |
 LL |     let _ = mut_ptr as *mut i32;
    |             ^^^^^^^^^^^^^^^^^^^ help: try `pointer::cast`, a safer alternative: `mut_ptr.cast::<i32>()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:79:9
    |
 LL |         ptr::null_mut() as *mut u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut::<u32>()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:83:9
    |
 LL |         std::ptr::null_mut() as *mut u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut::<u32>()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:88:9
    |
 LL |         ptr::null_mut() as *mut u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut::<u32>()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:92:9
    |
 LL |         core::ptr::null_mut() as *mut u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut::<u32>()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:97:9
    |
 LL |         ptr::null() as *const u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null::<u32>()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:101:9
    |
 LL |         std::ptr::null() as *const u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null::<u32>()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:106:9
    |
 LL |         ptr::null() as *const u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null::<u32>()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:110:9
    |
 LL |         core::ptr::null() as *const u32
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null::<u32>()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:117:9
    |
 LL |         ptr::null_mut() as *mut _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:121:9
    |
 LL |         std::ptr::null_mut() as *mut _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:126:9
    |
 LL |         ptr::null_mut() as *mut _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:130:9
    |
 LL |         core::ptr::null_mut() as *mut _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:135:9
    |
 LL |         ptr::null() as *const _
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:139:9
    |
 LL |         std::ptr::null() as *const _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:144:9
    |
 LL |         ptr::null() as *const _
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:148:9
    |
 LL |         core::ptr::null() as *const _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:155:9
    |
 LL |         ptr::null_mut() as _
    |         ^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:159:9
    |
 LL |         std::ptr::null_mut() as _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null_mut()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:164:9
    |
 LL |         ptr::null_mut() as _
    |         ^^^^^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null_mut()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:168:9
    |
 LL |         core::ptr::null_mut() as _
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `core::ptr::null_mut()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:173:9
    |
 LL |         ptr::null() as _
    |         ^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:177:9
    |
 LL |         std::ptr::null() as _
    |         ^^^^^^^^^^^^^^^^^^^^^ help: try call directly: `std::ptr::null()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:182:9
    |
 LL |         ptr::null() as _
    |         ^^^^^^^^^^^^^^^^ help: try call directly: `ptr::null()`
 
-error: `as` casting between raw pointers without changing its mutability
+error: `as` casting between raw pointers without changing their constness
   --> tests/ui/ptr_as_ptr.rs:186:9
    |
 LL |         core::ptr::null() as _
diff --git a/src/tools/clippy/tests/ui/rename.fixed b/src/tools/clippy/tests/ui/rename.fixed
index 24d0f797542..d70c9f8d06c 100644
--- a/src/tools/clippy/tests/ui/rename.fixed
+++ b/src/tools/clippy/tests/ui/rename.fixed
@@ -24,9 +24,11 @@
 #![allow(clippy::expect_used)]
 #![allow(clippy::map_unwrap_or)]
 #![allow(clippy::unwrap_used)]
+#![allow(clippy::panicking_overflow_checks)]
 #![allow(clippy::needless_borrow)]
 #![allow(clippy::single_char_add_str)]
 #![allow(clippy::module_name_repetitions)]
+#![allow(clippy::missing_const_for_thread_local)]
 #![allow(clippy::recursive_format_impl)]
 #![allow(clippy::unwrap_or_default)]
 #![allow(clippy::invisible_characters)]
@@ -77,12 +79,14 @@
 #![warn(clippy::map_unwrap_or)]
 #![warn(clippy::map_unwrap_or)]
 #![warn(clippy::unwrap_used)]
+#![warn(clippy::panicking_overflow_checks)]
 #![warn(clippy::needless_borrow)]
 #![warn(clippy::expect_used)]
 #![warn(clippy::map_unwrap_or)]
 #![warn(clippy::unwrap_used)]
 #![warn(clippy::single_char_add_str)]
 #![warn(clippy::module_name_repetitions)]
+#![warn(clippy::missing_const_for_thread_local)]
 #![warn(clippy::recursive_format_impl)]
 #![warn(clippy::unwrap_or_default)]
 #![warn(clippy::invisible_characters)]
diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs
index be8da2fa1a3..8d0ac3c8f95 100644
--- a/src/tools/clippy/tests/ui/rename.rs
+++ b/src/tools/clippy/tests/ui/rename.rs
@@ -24,9 +24,11 @@
 #![allow(clippy::expect_used)]
 #![allow(clippy::map_unwrap_or)]
 #![allow(clippy::unwrap_used)]
+#![allow(clippy::panicking_overflow_checks)]
 #![allow(clippy::needless_borrow)]
 #![allow(clippy::single_char_add_str)]
 #![allow(clippy::module_name_repetitions)]
+#![allow(clippy::missing_const_for_thread_local)]
 #![allow(clippy::recursive_format_impl)]
 #![allow(clippy::unwrap_or_default)]
 #![allow(clippy::invisible_characters)]
@@ -77,12 +79,14 @@
 #![warn(clippy::option_map_unwrap_or)]
 #![warn(clippy::option_map_unwrap_or_else)]
 #![warn(clippy::option_unwrap_used)]
+#![warn(clippy::overflow_check_conditional)]
 #![warn(clippy::ref_in_deref)]
 #![warn(clippy::result_expect_used)]
 #![warn(clippy::result_map_unwrap_or_else)]
 #![warn(clippy::result_unwrap_used)]
 #![warn(clippy::single_char_push_str)]
 #![warn(clippy::stutter)]
+#![warn(clippy::thread_local_initializer_can_be_made_const)]
 #![warn(clippy::to_string_in_display)]
 #![warn(clippy::unwrap_or_else_default)]
 #![warn(clippy::zero_width_space)]
diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr
index 777ac20153d..d6637324a03 100644
--- a/src/tools/clippy/tests/ui/rename.stderr
+++ b/src/tools/clippy/tests/ui/rename.stderr
@@ -1,5 +1,5 @@
 error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range`
-  --> tests/ui/rename.rs:56:9
+  --> tests/ui/rename.rs:58:9
    |
 LL | #![warn(clippy::almost_complete_letter_range)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range`
@@ -8,346 +8,358 @@ LL | #![warn(clippy::almost_complete_letter_range)]
    = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]`
 
 error: lint `clippy::blacklisted_name` has been renamed to `clippy::disallowed_names`
-  --> tests/ui/rename.rs:57:9
+  --> tests/ui/rename.rs:59:9
    |
 LL | #![warn(clippy::blacklisted_name)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_names`
 
 error: lint `clippy::block_in_if_condition_expr` has been renamed to `clippy::blocks_in_conditions`
-  --> tests/ui/rename.rs:58:9
+  --> tests/ui/rename.rs:60:9
    |
 LL | #![warn(clippy::block_in_if_condition_expr)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions`
 
 error: lint `clippy::block_in_if_condition_stmt` has been renamed to `clippy::blocks_in_conditions`
-  --> tests/ui/rename.rs:59:9
+  --> tests/ui/rename.rs:61:9
    |
 LL | #![warn(clippy::block_in_if_condition_stmt)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions`
 
 error: lint `clippy::blocks_in_if_conditions` has been renamed to `clippy::blocks_in_conditions`
-  --> tests/ui/rename.rs:60:9
+  --> tests/ui/rename.rs:62:9
    |
 LL | #![warn(clippy::blocks_in_if_conditions)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::blocks_in_conditions`
 
 error: lint `clippy::box_vec` has been renamed to `clippy::box_collection`
-  --> tests/ui/rename.rs:61:9
+  --> tests/ui/rename.rs:63:9
    |
 LL | #![warn(clippy::box_vec)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::box_collection`
 
 error: lint `clippy::const_static_lifetime` has been renamed to `clippy::redundant_static_lifetimes`
-  --> tests/ui/rename.rs:62:9
+  --> tests/ui/rename.rs:64:9
    |
 LL | #![warn(clippy::const_static_lifetime)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::redundant_static_lifetimes`
 
 error: lint `clippy::cyclomatic_complexity` has been renamed to `clippy::cognitive_complexity`
-  --> tests/ui/rename.rs:63:9
+  --> tests/ui/rename.rs:65:9
    |
 LL | #![warn(clippy::cyclomatic_complexity)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::cognitive_complexity`
 
 error: lint `clippy::derive_hash_xor_eq` has been renamed to `clippy::derived_hash_with_manual_eq`
-  --> tests/ui/rename.rs:64:9
+  --> tests/ui/rename.rs:66:9
    |
 LL | #![warn(clippy::derive_hash_xor_eq)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::derived_hash_with_manual_eq`
 
 error: lint `clippy::disallowed_method` has been renamed to `clippy::disallowed_methods`
-  --> tests/ui/rename.rs:65:9
+  --> tests/ui/rename.rs:67:9
    |
 LL | #![warn(clippy::disallowed_method)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_methods`
 
 error: lint `clippy::disallowed_type` has been renamed to `clippy::disallowed_types`
-  --> tests/ui/rename.rs:66:9
+  --> tests/ui/rename.rs:68:9
    |
 LL | #![warn(clippy::disallowed_type)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::disallowed_types`
 
 error: lint `clippy::eval_order_dependence` has been renamed to `clippy::mixed_read_write_in_expression`
-  --> tests/ui/rename.rs:67:9
+  --> tests/ui/rename.rs:69:9
    |
 LL | #![warn(clippy::eval_order_dependence)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::mixed_read_write_in_expression`
 
 error: lint `clippy::identity_conversion` has been renamed to `clippy::useless_conversion`
-  --> tests/ui/rename.rs:68:9
+  --> tests/ui/rename.rs:70:9
    |
 LL | #![warn(clippy::identity_conversion)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::useless_conversion`
 
 error: lint `clippy::if_let_some_result` has been renamed to `clippy::match_result_ok`
-  --> tests/ui/rename.rs:69:9
+  --> tests/ui/rename.rs:71:9
    |
 LL | #![warn(clippy::if_let_some_result)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::match_result_ok`
 
 error: lint `clippy::incorrect_clone_impl_on_copy_type` has been renamed to `clippy::non_canonical_clone_impl`
-  --> tests/ui/rename.rs:70:9
+  --> tests/ui/rename.rs:72:9
    |
 LL | #![warn(clippy::incorrect_clone_impl_on_copy_type)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_clone_impl`
 
 error: lint `clippy::incorrect_partial_ord_impl_on_ord_type` has been renamed to `clippy::non_canonical_partial_ord_impl`
-  --> tests/ui/rename.rs:71:9
+  --> tests/ui/rename.rs:73:9
    |
 LL | #![warn(clippy::incorrect_partial_ord_impl_on_ord_type)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::non_canonical_partial_ord_impl`
 
 error: lint `clippy::integer_arithmetic` has been renamed to `clippy::arithmetic_side_effects`
-  --> tests/ui/rename.rs:72:9
+  --> tests/ui/rename.rs:74:9
    |
 LL | #![warn(clippy::integer_arithmetic)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::arithmetic_side_effects`
 
 error: lint `clippy::logic_bug` has been renamed to `clippy::overly_complex_bool_expr`
-  --> tests/ui/rename.rs:73:9
+  --> tests/ui/rename.rs:75:9
    |
 LL | #![warn(clippy::logic_bug)]
    |         ^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::overly_complex_bool_expr`
 
 error: lint `clippy::new_without_default_derive` has been renamed to `clippy::new_without_default`
-  --> tests/ui/rename.rs:74:9
+  --> tests/ui/rename.rs:76:9
    |
 LL | #![warn(clippy::new_without_default_derive)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::new_without_default`
 
 error: lint `clippy::option_and_then_some` has been renamed to `clippy::bind_instead_of_map`
-  --> tests/ui/rename.rs:75:9
+  --> tests/ui/rename.rs:77:9
    |
 LL | #![warn(clippy::option_and_then_some)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::bind_instead_of_map`
 
 error: lint `clippy::option_expect_used` has been renamed to `clippy::expect_used`
-  --> tests/ui/rename.rs:76:9
+  --> tests/ui/rename.rs:78:9
    |
 LL | #![warn(clippy::option_expect_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
 error: lint `clippy::option_map_unwrap_or` has been renamed to `clippy::map_unwrap_or`
-  --> tests/ui/rename.rs:77:9
+  --> tests/ui/rename.rs:79:9
    |
 LL | #![warn(clippy::option_map_unwrap_or)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::option_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-  --> tests/ui/rename.rs:78:9
+  --> tests/ui/rename.rs:80:9
    |
 LL | #![warn(clippy::option_map_unwrap_or_else)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::option_unwrap_used` has been renamed to `clippy::unwrap_used`
-  --> tests/ui/rename.rs:79:9
+  --> tests/ui/rename.rs:81:9
    |
 LL | #![warn(clippy::option_unwrap_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
+error: lint `clippy::overflow_check_conditional` has been renamed to `clippy::panicking_overflow_checks`
+  --> tests/ui/rename.rs:82:9
+   |
+LL | #![warn(clippy::overflow_check_conditional)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::panicking_overflow_checks`
+
 error: lint `clippy::ref_in_deref` has been renamed to `clippy::needless_borrow`
-  --> tests/ui/rename.rs:80:9
+  --> tests/ui/rename.rs:83:9
    |
 LL | #![warn(clippy::ref_in_deref)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::needless_borrow`
 
 error: lint `clippy::result_expect_used` has been renamed to `clippy::expect_used`
-  --> tests/ui/rename.rs:81:9
+  --> tests/ui/rename.rs:84:9
    |
 LL | #![warn(clippy::result_expect_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::expect_used`
 
 error: lint `clippy::result_map_unwrap_or_else` has been renamed to `clippy::map_unwrap_or`
-  --> tests/ui/rename.rs:82:9
+  --> tests/ui/rename.rs:85:9
    |
 LL | #![warn(clippy::result_map_unwrap_or_else)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::map_unwrap_or`
 
 error: lint `clippy::result_unwrap_used` has been renamed to `clippy::unwrap_used`
-  --> tests/ui/rename.rs:83:9
+  --> tests/ui/rename.rs:86:9
    |
 LL | #![warn(clippy::result_unwrap_used)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_used`
 
 error: lint `clippy::single_char_push_str` has been renamed to `clippy::single_char_add_str`
-  --> tests/ui/rename.rs:84:9
+  --> tests/ui/rename.rs:87:9
    |
 LL | #![warn(clippy::single_char_push_str)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::single_char_add_str`
 
 error: lint `clippy::stutter` has been renamed to `clippy::module_name_repetitions`
-  --> tests/ui/rename.rs:85:9
+  --> tests/ui/rename.rs:88:9
    |
 LL | #![warn(clippy::stutter)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `clippy::module_name_repetitions`
 
+error: lint `clippy::thread_local_initializer_can_be_made_const` has been renamed to `clippy::missing_const_for_thread_local`
+  --> tests/ui/rename.rs:89:9
+   |
+LL | #![warn(clippy::thread_local_initializer_can_be_made_const)]
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::missing_const_for_thread_local`
+
 error: lint `clippy::to_string_in_display` has been renamed to `clippy::recursive_format_impl`
-  --> tests/ui/rename.rs:86:9
+  --> tests/ui/rename.rs:90:9
    |
 LL | #![warn(clippy::to_string_in_display)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::recursive_format_impl`
 
 error: lint `clippy::unwrap_or_else_default` has been renamed to `clippy::unwrap_or_default`
-  --> tests/ui/rename.rs:87:9
+  --> tests/ui/rename.rs:91:9
    |
 LL | #![warn(clippy::unwrap_or_else_default)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::unwrap_or_default`
 
 error: lint `clippy::zero_width_space` has been renamed to `clippy::invisible_characters`
-  --> tests/ui/rename.rs:88:9
+  --> tests/ui/rename.rs:92:9
    |
 LL | #![warn(clippy::zero_width_space)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::invisible_characters`
 
 error: lint `clippy::cast_ref_to_mut` has been renamed to `invalid_reference_casting`
-  --> tests/ui/rename.rs:89:9
+  --> tests/ui/rename.rs:93:9
    |
 LL | #![warn(clippy::cast_ref_to_mut)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_reference_casting`
 
 error: lint `clippy::clone_double_ref` has been renamed to `suspicious_double_ref_op`
-  --> tests/ui/rename.rs:90:9
+  --> tests/ui/rename.rs:94:9
    |
 LL | #![warn(clippy::clone_double_ref)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `suspicious_double_ref_op`
 
 error: lint `clippy::cmp_nan` has been renamed to `invalid_nan_comparisons`
-  --> tests/ui/rename.rs:91:9
+  --> tests/ui/rename.rs:95:9
    |
 LL | #![warn(clippy::cmp_nan)]
    |         ^^^^^^^^^^^^^^^ help: use the new name: `invalid_nan_comparisons`
 
 error: lint `clippy::drop_bounds` has been renamed to `drop_bounds`
-  --> tests/ui/rename.rs:92:9
+  --> tests/ui/rename.rs:96:9
    |
 LL | #![warn(clippy::drop_bounds)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `drop_bounds`
 
 error: lint `clippy::drop_copy` has been renamed to `dropping_copy_types`
-  --> tests/ui/rename.rs:93:9
+  --> tests/ui/rename.rs:97:9
    |
 LL | #![warn(clippy::drop_copy)]
    |         ^^^^^^^^^^^^^^^^^ help: use the new name: `dropping_copy_types`
 
 error: lint `clippy::drop_ref` has been renamed to `dropping_references`
-  --> tests/ui/rename.rs:94:9
+  --> tests/ui/rename.rs:98:9
    |
 LL | #![warn(clippy::drop_ref)]
    |         ^^^^^^^^^^^^^^^^ help: use the new name: `dropping_references`
 
 error: lint `clippy::fn_null_check` has been renamed to `useless_ptr_null_checks`
-  --> tests/ui/rename.rs:95:9
+  --> tests/ui/rename.rs:99:9
    |
 LL | #![warn(clippy::fn_null_check)]
    |         ^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `useless_ptr_null_checks`
 
 error: lint `clippy::for_loop_over_option` has been renamed to `for_loops_over_fallibles`
-  --> tests/ui/rename.rs:96:9
+  --> tests/ui/rename.rs:100:9
    |
 LL | #![warn(clippy::for_loop_over_option)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
 
 error: lint `clippy::for_loop_over_result` has been renamed to `for_loops_over_fallibles`
-  --> tests/ui/rename.rs:97:9
+  --> tests/ui/rename.rs:101:9
    |
 LL | #![warn(clippy::for_loop_over_result)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
 
 error: lint `clippy::for_loops_over_fallibles` has been renamed to `for_loops_over_fallibles`
-  --> tests/ui/rename.rs:98:9
+  --> tests/ui/rename.rs:102:9
    |
 LL | #![warn(clippy::for_loops_over_fallibles)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `for_loops_over_fallibles`
 
 error: lint `clippy::forget_copy` has been renamed to `forgetting_copy_types`
-  --> tests/ui/rename.rs:99:9
+  --> tests/ui/rename.rs:103:9
    |
 LL | #![warn(clippy::forget_copy)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_copy_types`
 
 error: lint `clippy::forget_ref` has been renamed to `forgetting_references`
-  --> tests/ui/rename.rs:100:9
+  --> tests/ui/rename.rs:104:9
    |
 LL | #![warn(clippy::forget_ref)]
    |         ^^^^^^^^^^^^^^^^^^ help: use the new name: `forgetting_references`
 
 error: lint `clippy::into_iter_on_array` has been renamed to `array_into_iter`
-  --> tests/ui/rename.rs:101:9
+  --> tests/ui/rename.rs:105:9
    |
 LL | #![warn(clippy::into_iter_on_array)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `array_into_iter`
 
 error: lint `clippy::invalid_atomic_ordering` has been renamed to `invalid_atomic_ordering`
-  --> tests/ui/rename.rs:102:9
+  --> tests/ui/rename.rs:106:9
    |
 LL | #![warn(clippy::invalid_atomic_ordering)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_atomic_ordering`
 
 error: lint `clippy::invalid_ref` has been renamed to `invalid_value`
-  --> tests/ui/rename.rs:103:9
+  --> tests/ui/rename.rs:107:9
    |
 LL | #![warn(clippy::invalid_ref)]
    |         ^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_value`
 
 error: lint `clippy::invalid_utf8_in_unchecked` has been renamed to `invalid_from_utf8_unchecked`
-  --> tests/ui/rename.rs:104:9
+  --> tests/ui/rename.rs:108:9
    |
 LL | #![warn(clippy::invalid_utf8_in_unchecked)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `invalid_from_utf8_unchecked`
 
 error: lint `clippy::let_underscore_drop` has been renamed to `let_underscore_drop`
-  --> tests/ui/rename.rs:105:9
+  --> tests/ui/rename.rs:109:9
    |
 LL | #![warn(clippy::let_underscore_drop)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `let_underscore_drop`
 
 error: lint `clippy::mem_discriminant_non_enum` has been renamed to `enum_intrinsics_non_enums`
-  --> tests/ui/rename.rs:106:9
+  --> tests/ui/rename.rs:110:9
    |
 LL | #![warn(clippy::mem_discriminant_non_enum)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `enum_intrinsics_non_enums`
 
 error: lint `clippy::panic_params` has been renamed to `non_fmt_panics`
-  --> tests/ui/rename.rs:107:9
+  --> tests/ui/rename.rs:111:9
    |
 LL | #![warn(clippy::panic_params)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `non_fmt_panics`
 
 error: lint `clippy::positional_named_format_parameters` has been renamed to `named_arguments_used_positionally`
-  --> tests/ui/rename.rs:108:9
+  --> tests/ui/rename.rs:112:9
    |
 LL | #![warn(clippy::positional_named_format_parameters)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `named_arguments_used_positionally`
 
 error: lint `clippy::temporary_cstring_as_ptr` has been renamed to `temporary_cstring_as_ptr`
-  --> tests/ui/rename.rs:109:9
+  --> tests/ui/rename.rs:113:9
    |
 LL | #![warn(clippy::temporary_cstring_as_ptr)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `temporary_cstring_as_ptr`
 
 error: lint `clippy::undropped_manually_drops` has been renamed to `undropped_manually_drops`
-  --> tests/ui/rename.rs:110:9
+  --> tests/ui/rename.rs:114:9
    |
 LL | #![warn(clippy::undropped_manually_drops)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `undropped_manually_drops`
 
 error: lint `clippy::unknown_clippy_lints` has been renamed to `unknown_lints`
-  --> tests/ui/rename.rs:111:9
+  --> tests/ui/rename.rs:115:9
    |
 LL | #![warn(clippy::unknown_clippy_lints)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unknown_lints`
 
 error: lint `clippy::unused_label` has been renamed to `unused_labels`
-  --> tests/ui/rename.rs:112:9
+  --> tests/ui/rename.rs:116:9
    |
 LL | #![warn(clippy::unused_label)]
    |         ^^^^^^^^^^^^^^^^^^^^ help: use the new name: `unused_labels`
 
 error: lint `clippy::vtable_address_comparisons` has been renamed to `ambiguous_wide_pointer_comparisons`
-  --> tests/ui/rename.rs:113:9
+  --> tests/ui/rename.rs:117:9
    |
 LL | #![warn(clippy::vtable_address_comparisons)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `ambiguous_wide_pointer_comparisons`
 
-error: aborting due to 58 previous errors
+error: aborting due to 60 previous errors
 
diff --git a/src/tools/clippy/tests/ui/set_contains_or_insert.rs b/src/tools/clippy/tests/ui/set_contains_or_insert.rs
new file mode 100644
index 00000000000..8465007402a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/set_contains_or_insert.rs
@@ -0,0 +1,83 @@
+#![allow(unused)]
+#![allow(clippy::nonminimal_bool)]
+#![allow(clippy::needless_borrow)]
+#![warn(clippy::set_contains_or_insert)]
+
+use std::collections::HashSet;
+
+fn main() {
+    should_warn_cases();
+
+    should_not_warn_cases();
+}
+
+fn should_warn_cases() {
+    let mut set = HashSet::new();
+    let value = 5;
+
+    if !set.contains(&value) {
+        set.insert(value);
+        println!("Just a comment");
+    }
+
+    if set.contains(&value) {
+        set.insert(value);
+        println!("Just a comment");
+    }
+
+    if !set.contains(&value) {
+        set.insert(value);
+    }
+
+    if !!set.contains(&value) {
+        set.insert(value);
+        println!("Just a comment");
+    }
+
+    if (&set).contains(&value) {
+        set.insert(value);
+    }
+
+    let borrow_value = &6;
+    if !set.contains(borrow_value) {
+        set.insert(*borrow_value);
+    }
+
+    let borrow_set = &mut set;
+    if !borrow_set.contains(&value) {
+        borrow_set.insert(value);
+    }
+}
+
+fn should_not_warn_cases() {
+    let mut set = HashSet::new();
+    let value = 5;
+    let another_value = 6;
+
+    if !set.contains(&value) {
+        set.insert(another_value);
+    }
+
+    if !set.contains(&value) {
+        println!("Just a comment");
+    }
+
+    if simply_true() {
+        set.insert(value);
+    }
+
+    if !set.contains(&value) {
+        set.replace(value); //it is not insert
+        println!("Just a comment");
+    }
+
+    if set.contains(&value) {
+        println!("value is already in set");
+    } else {
+        set.insert(value);
+    }
+}
+
+fn simply_true() -> bool {
+    true
+}
diff --git a/src/tools/clippy/tests/ui/set_contains_or_insert.stderr b/src/tools/clippy/tests/ui/set_contains_or_insert.stderr
new file mode 100644
index 00000000000..507e20964fc
--- /dev/null
+++ b/src/tools/clippy/tests/ui/set_contains_or_insert.stderr
@@ -0,0 +1,61 @@
+error: usage of `HashSet::insert` after `HashSet::contains`
+  --> tests/ui/set_contains_or_insert.rs:18:13
+   |
+LL |     if !set.contains(&value) {
+   |             ^^^^^^^^^^^^^^^^
+LL |         set.insert(value);
+   |             ^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::set-contains-or-insert` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::set_contains_or_insert)]`
+
+error: usage of `HashSet::insert` after `HashSet::contains`
+  --> tests/ui/set_contains_or_insert.rs:23:12
+   |
+LL |     if set.contains(&value) {
+   |            ^^^^^^^^^^^^^^^^
+LL |         set.insert(value);
+   |             ^^^^^^^^^^^^^
+
+error: usage of `HashSet::insert` after `HashSet::contains`
+  --> tests/ui/set_contains_or_insert.rs:28:13
+   |
+LL |     if !set.contains(&value) {
+   |             ^^^^^^^^^^^^^^^^
+LL |         set.insert(value);
+   |             ^^^^^^^^^^^^^
+
+error: usage of `HashSet::insert` after `HashSet::contains`
+  --> tests/ui/set_contains_or_insert.rs:32:14
+   |
+LL |     if !!set.contains(&value) {
+   |              ^^^^^^^^^^^^^^^^
+LL |         set.insert(value);
+   |             ^^^^^^^^^^^^^
+
+error: usage of `HashSet::insert` after `HashSet::contains`
+  --> tests/ui/set_contains_or_insert.rs:37:15
+   |
+LL |     if (&set).contains(&value) {
+   |               ^^^^^^^^^^^^^^^^
+LL |         set.insert(value);
+   |             ^^^^^^^^^^^^^
+
+error: usage of `HashSet::insert` after `HashSet::contains`
+  --> tests/ui/set_contains_or_insert.rs:42:13
+   |
+LL |     if !set.contains(borrow_value) {
+   |             ^^^^^^^^^^^^^^^^^^^^^^
+LL |         set.insert(*borrow_value);
+   |             ^^^^^^^^^^^^^^^^^^^^^
+
+error: usage of `HashSet::insert` after `HashSet::contains`
+  --> tests/ui/set_contains_or_insert.rs:47:20
+   |
+LL |     if !borrow_set.contains(&value) {
+   |                    ^^^^^^^^^^^^^^^^
+LL |         borrow_set.insert(value);
+   |                    ^^^^^^^^^^^^^
+
+error: aborting due to 7 previous errors
+
diff --git a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs
index 8ee15440ccf..0db6fbfb7be 100644
--- a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs
+++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.rs
@@ -801,4 +801,30 @@ fn should_not_trigger_lint_with_explicit_drop() {
     }
 }
 
+fn should_trigger_lint_in_if_let() {
+    let mutex = Mutex::new(vec![1]);
+
+    if let Some(val) = mutex.lock().unwrap().first().copied() {
+        //~^ ERROR: temporary with significant `Drop` in `if let` scrutinee will live until the
+        //~| NOTE: this might lead to deadlocks or other unexpected behavior
+        println!("{}", val);
+    }
+
+    // Should not trigger lint without the final `copied()`, because we actually hold a reference
+    // (i.e., the `val`) to the locked data.
+    if let Some(val) = mutex.lock().unwrap().first() {
+        println!("{}", val);
+    };
+}
+
+fn should_trigger_lint_in_while_let() {
+    let mutex = Mutex::new(vec![1]);
+
+    while let Some(val) = mutex.lock().unwrap().pop() {
+        //~^ ERROR: temporary with significant `Drop` in `while let` scrutinee will live until the
+        //~| NOTE: this might lead to deadlocks or other unexpected behavior
+        println!("{}", val);
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr
index 4a483e79d8a..c0c93cd10c0 100644
--- a/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr
+++ b/src/tools/clippy/tests/ui/significant_drop_in_scrutinee.stderr
@@ -541,5 +541,32 @@ LL ~     let value = mutex.lock().unwrap()[0];
 LL ~     for val in [value, 2] {
    |
 
-error: aborting due to 27 previous errors
+error: temporary with significant `Drop` in `if let` scrutinee will live until the end of the `if let` expression
+  --> tests/ui/significant_drop_in_scrutinee.rs:807:24
+   |
+LL |     if let Some(val) = mutex.lock().unwrap().first().copied() {
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL |     }
+   |      - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
+help: try moving the temporary above the match
+   |
+LL ~     let value = mutex.lock().unwrap().first().copied();
+LL ~     if let Some(val) = value {
+   |
+
+error: temporary with significant `Drop` in `while let` scrutinee will live until the end of the `while let` expression
+  --> tests/ui/significant_drop_in_scrutinee.rs:823:27
+   |
+LL |     while let Some(val) = mutex.lock().unwrap().pop() {
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL |     }
+   |      - temporary lives until here
+   |
+   = note: this might lead to deadlocks or other unexpected behavior
+
+error: aborting due to 29 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.fixed b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
index 11761c6c90e..006f123cbcd 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.fixed
@@ -43,7 +43,7 @@ fn get_number() -> i32 {
     0
 }
 
-fn get_usize() -> usize {
+const fn get_usize() -> usize {
     0
 }
 fn get_struct() -> Struct {
@@ -113,4 +113,16 @@ fn main() {
     'label: {
         break 'label
     };
+    let () = const {
+        [42, 55][get_usize()];
+    };
+}
+
+const _: () = {
+    [42, 55][get_usize()];
+};
+
+const fn foo() {
+    assert!([42, 55].len() > get_usize());
+    //~^ ERROR: unnecessary operation
 }
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.rs b/src/tools/clippy/tests/ui/unnecessary_operation.rs
index de0081289ac..b4067c74074 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.rs
@@ -43,7 +43,7 @@ fn get_number() -> i32 {
     0
 }
 
-fn get_usize() -> usize {
+const fn get_usize() -> usize {
     0
 }
 fn get_struct() -> Struct {
@@ -117,4 +117,16 @@ fn main() {
     'label: {
         break 'label
     };
+    let () = const {
+        [42, 55][get_usize()];
+    };
+}
+
+const _: () = {
+    [42, 55][get_usize()];
+};
+
+const fn foo() {
+    [42, 55][get_usize()];
+    //~^ ERROR: unnecessary operation
 }
diff --git a/src/tools/clippy/tests/ui/unnecessary_operation.stderr b/src/tools/clippy/tests/ui/unnecessary_operation.stderr
index 27be5e6f4b9..036a9a44bba 100644
--- a/src/tools/clippy/tests/ui/unnecessary_operation.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_operation.stderr
@@ -119,5 +119,11 @@ LL | |         s: String::from("blah"),
 LL | |     };
    | |______^ help: statement can be reduced to: `String::from("blah");`
 
-error: aborting due to 19 previous errors
+error: unnecessary operation
+  --> tests/ui/unnecessary_operation.rs:130:5
+   |
+LL |     [42, 55][get_usize()];
+   |     ^^^^^^^^^^^^^^^^^^^^^^ help: statement can be written as: `assert!([42, 55].len() > get_usize());`
+
+error: aborting due to 20 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 1afa5ab54c4..fdcac8fb08d 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.fixed
@@ -157,6 +157,25 @@ fn main() {
     require_path(&std::path::PathBuf::from("x"));
     require_str(&String::from("x"));
     require_slice(&[String::from("x")]);
+
+    let slice = [0u8; 1024];
+    let _ref_str: &str = core::str::from_utf8(&slice).expect("not UTF-8");
+    let _ref_str: &str = core::str::from_utf8(b"foo").unwrap();
+    let _ref_str: &str = core::str::from_utf8(b"foo".as_slice()).unwrap();
+    // Expression is of type `&String`, can't suggest `str::from_utf8` here
+    let _ref_string = &String::from_utf8(b"foo".to_vec()).unwrap();
+    macro_rules! arg_from_macro {
+        () => {
+            b"foo".to_vec()
+        };
+    }
+    macro_rules! string_from_utf8_from_macro {
+        () => {
+            &String::from_utf8(b"foo".to_vec()).unwrap()
+        };
+    }
+    let _ref_str: &str = &String::from_utf8(arg_from_macro!()).unwrap();
+    let _ref_str: &str = string_from_utf8_from_macro!();
 }
 
 fn require_c_str(_: &CStr) {}
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
index aa88dde43bf..10a9727a9a7 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.rs
@@ -157,6 +157,25 @@ fn main() {
     require_path(&std::path::PathBuf::from("x").to_path_buf());
     require_str(&String::from("x").to_string());
     require_slice(&[String::from("x")].to_owned());
+
+    let slice = [0u8; 1024];
+    let _ref_str: &str = &String::from_utf8(slice.to_vec()).expect("not UTF-8");
+    let _ref_str: &str = &String::from_utf8(b"foo".to_vec()).unwrap();
+    let _ref_str: &str = &String::from_utf8(b"foo".as_slice().to_owned()).unwrap();
+    // Expression is of type `&String`, can't suggest `str::from_utf8` here
+    let _ref_string = &String::from_utf8(b"foo".to_vec()).unwrap();
+    macro_rules! arg_from_macro {
+        () => {
+            b"foo".to_vec()
+        };
+    }
+    macro_rules! string_from_utf8_from_macro {
+        () => {
+            &String::from_utf8(b"foo".to_vec()).unwrap()
+        };
+    }
+    let _ref_str: &str = &String::from_utf8(arg_from_macro!()).unwrap();
+    let _ref_str: &str = string_from_utf8_from_macro!();
 }
 
 fn require_c_str(_: &CStr) {}
diff --git a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
index 2829f3cd6e9..511b4ae119f 100644
--- a/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_to_owned.stderr
@@ -477,8 +477,44 @@ error: unnecessary use of `to_owned`
 LL |     let _ = IntoIterator::into_iter([std::path::PathBuf::new()][..].to_owned());
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `[std::path::PathBuf::new()][..].iter().cloned()`
 
+error: allocating a new `String` only to create a temporary `&str` from it
+  --> tests/ui/unnecessary_to_owned.rs:162:26
+   |
+LL |     let _ref_str: &str = &String::from_utf8(slice.to_vec()).expect("not UTF-8");
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: convert from `&[u8]` to `&str` directly
+   |
+LL -     let _ref_str: &str = &String::from_utf8(slice.to_vec()).expect("not UTF-8");
+LL +     let _ref_str: &str = core::str::from_utf8(&slice).expect("not UTF-8");
+   |
+
+error: allocating a new `String` only to create a temporary `&str` from it
+  --> tests/ui/unnecessary_to_owned.rs:163:26
+   |
+LL |     let _ref_str: &str = &String::from_utf8(b"foo".to_vec()).unwrap();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: convert from `&[u8]` to `&str` directly
+   |
+LL -     let _ref_str: &str = &String::from_utf8(b"foo".to_vec()).unwrap();
+LL +     let _ref_str: &str = core::str::from_utf8(b"foo").unwrap();
+   |
+
+error: allocating a new `String` only to create a temporary `&str` from it
+  --> tests/ui/unnecessary_to_owned.rs:164:26
+   |
+LL |     let _ref_str: &str = &String::from_utf8(b"foo".as_slice().to_owned()).unwrap();
+   |                          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: convert from `&[u8]` to `&str` directly
+   |
+LL -     let _ref_str: &str = &String::from_utf8(b"foo".as_slice().to_owned()).unwrap();
+LL +     let _ref_str: &str = core::str::from_utf8(b"foo".as_slice()).unwrap();
+   |
+
 error: unnecessary use of `to_vec`
-  --> tests/ui/unnecessary_to_owned.rs:202:14
+  --> tests/ui/unnecessary_to_owned.rs:221:14
    |
 LL |     for t in file_types.to_vec() {
    |              ^^^^^^^^^^^^^^^^^^^
@@ -494,64 +530,64 @@ LL +         let path = match get_file_path(t) {
    |
 
 error: unnecessary use of `to_vec`
-  --> tests/ui/unnecessary_to_owned.rs:225:14
+  --> tests/ui/unnecessary_to_owned.rs:244:14
    |
 LL |     let _ = &["x"][..].to_vec().into_iter();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().cloned()`
 
 error: unnecessary use of `to_vec`
-  --> tests/ui/unnecessary_to_owned.rs:230:14
+  --> tests/ui/unnecessary_to_owned.rs:249:14
    |
 LL |     let _ = &["x"][..].to_vec().into_iter();
    |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use: `["x"][..].iter().copied()`
 
 error: unnecessary use of `to_string`
-  --> tests/ui/unnecessary_to_owned.rs:278:24
+  --> tests/ui/unnecessary_to_owned.rs:297:24
    |
 LL |         Box::new(build(y.to_string()))
    |                        ^^^^^^^^^^^^^ help: use: `y`
 
 error: unnecessary use of `to_string`
-  --> tests/ui/unnecessary_to_owned.rs:387:12
+  --> tests/ui/unnecessary_to_owned.rs:406:12
    |
 LL |         id("abc".to_string())
    |            ^^^^^^^^^^^^^^^^^ help: use: `"abc"`
 
 error: unnecessary use of `to_vec`
-  --> tests/ui/unnecessary_to_owned.rs:530:37
+  --> tests/ui/unnecessary_to_owned.rs:549:37
    |
 LL |         IntoFuture::into_future(foo([].to_vec(), &0));
    |                                     ^^^^^^^^^^^ help: use: `[]`
 
 error: unnecessary use of `to_vec`
-  --> tests/ui/unnecessary_to_owned.rs:540:18
+  --> tests/ui/unnecessary_to_owned.rs:559:18
    |
 LL |         s.remove(&a.to_vec());
    |                  ^^^^^^^^^^^ help: replace it with: `a`
 
 error: unnecessary use of `to_owned`
-  --> tests/ui/unnecessary_to_owned.rs:544:14
+  --> tests/ui/unnecessary_to_owned.rs:563:14
    |
 LL |     s.remove(&"b".to_owned());
    |              ^^^^^^^^^^^^^^^ help: replace it with: `"b"`
 
 error: unnecessary use of `to_string`
-  --> tests/ui/unnecessary_to_owned.rs:545:14
+  --> tests/ui/unnecessary_to_owned.rs:564:14
    |
 LL |     s.remove(&"b".to_string());
    |              ^^^^^^^^^^^^^^^^ help: replace it with: `"b"`
 
 error: unnecessary use of `to_vec`
-  --> tests/ui/unnecessary_to_owned.rs:550:14
+  --> tests/ui/unnecessary_to_owned.rs:569:14
    |
 LL |     s.remove(&["b"].to_vec());
    |              ^^^^^^^^^^^^^^^ help: replace it with: `["b"].as_slice()`
 
 error: unnecessary use of `to_vec`
-  --> tests/ui/unnecessary_to_owned.rs:551:14
+  --> tests/ui/unnecessary_to_owned.rs:570:14
    |
 LL |     s.remove(&(&["b"]).to_vec());
    |              ^^^^^^^^^^^^^^^^^^ help: replace it with: `(&["b"]).as_slice()`
 
-error: aborting due to 85 previous errors
+error: aborting due to 88 previous errors
 
diff --git a/src/tools/clippy/tests/ui/wildcard_imports.fixed b/src/tools/clippy/tests/ui/wildcard_imports.fixed
index 6fdd728b9b7..46890ee9213 100644
--- a/src/tools/clippy/tests/ui/wildcard_imports.fixed
+++ b/src/tools/clippy/tests/ui/wildcard_imports.fixed
@@ -204,6 +204,7 @@ mod super_imports {
         }
     }
 
+    #[cfg(test)]
     mod test_should_pass {
         use super::*;
 
@@ -212,6 +213,7 @@ mod super_imports {
         }
     }
 
+    #[cfg(test)]
     mod test_should_pass_inside_function {
         fn with_super_inside_function() {
             use super::*;
@@ -219,6 +221,7 @@ mod super_imports {
         }
     }
 
+    #[cfg(test)]
     mod test_should_pass_further_inside {
         fn insidefoo() {}
         mod inner {
diff --git a/src/tools/clippy/tests/ui/wildcard_imports.rs b/src/tools/clippy/tests/ui/wildcard_imports.rs
index 20e06d4b366..1a5586cbb88 100644
--- a/src/tools/clippy/tests/ui/wildcard_imports.rs
+++ b/src/tools/clippy/tests/ui/wildcard_imports.rs
@@ -205,6 +205,7 @@ mod super_imports {
         }
     }
 
+    #[cfg(test)]
     mod test_should_pass {
         use super::*;
 
@@ -213,6 +214,7 @@ mod super_imports {
         }
     }
 
+    #[cfg(test)]
     mod test_should_pass_inside_function {
         fn with_super_inside_function() {
             use super::*;
@@ -220,6 +222,7 @@ mod super_imports {
         }
     }
 
+    #[cfg(test)]
     mod test_should_pass_further_inside {
         fn insidefoo() {}
         mod inner {
diff --git a/src/tools/clippy/tests/ui/wildcard_imports.stderr b/src/tools/clippy/tests/ui/wildcard_imports.stderr
index 0c69d5262c2..8e88f216394 100644
--- a/src/tools/clippy/tests/ui/wildcard_imports.stderr
+++ b/src/tools/clippy/tests/ui/wildcard_imports.stderr
@@ -106,31 +106,31 @@ LL |         use super::*;
    |             ^^^^^^^^ help: try: `super::foofoo`
 
 error: usage of wildcard import
-  --> tests/ui/wildcard_imports.rs:236:17
+  --> tests/ui/wildcard_imports.rs:239:17
    |
 LL |             use super::*;
    |                 ^^^^^^^^ help: try: `super::insidefoo`
 
 error: usage of wildcard import
-  --> tests/ui/wildcard_imports.rs:244:13
+  --> tests/ui/wildcard_imports.rs:247:13
    |
 LL |         use crate::super_imports::*;
    |             ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo`
 
 error: usage of wildcard import
-  --> tests/ui/wildcard_imports.rs:253:17
+  --> tests/ui/wildcard_imports.rs:256:17
    |
 LL |             use super::super::*;
    |                 ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo`
 
 error: usage of wildcard import
-  --> tests/ui/wildcard_imports.rs:262:13
+  --> tests/ui/wildcard_imports.rs:265:13
    |
 LL |         use super::super::super_imports::*;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo`
 
 error: usage of wildcard import
-  --> tests/ui/wildcard_imports.rs:270:13
+  --> tests/ui/wildcard_imports.rs:273:13
    |
 LL |         use super::*;
    |             ^^^^^^^^ help: try: `super::foofoo`
diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed
index 6a9fe007d65..197dd3b94df 100644
--- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed
+++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.fixed
@@ -198,6 +198,7 @@ mod super_imports {
         }
     }
 
+    #[cfg(test)]
     mod test_should_pass {
         use super::*;
 
@@ -206,6 +207,7 @@ mod super_imports {
         }
     }
 
+    #[cfg(test)]
     mod test_should_pass_inside_function {
         fn with_super_inside_function() {
             use super::*;
@@ -213,6 +215,7 @@ mod super_imports {
         }
     }
 
+    #[cfg(test)]
     mod test_should_pass_further_inside {
         fn insidefoo() {}
         mod inner {
diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr
index 11e0bd37769..66adacd95dc 100644
--- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr
+++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2018.stderr
@@ -106,31 +106,31 @@ LL |         use super::*;
    |             ^^^^^^^^ help: try: `super::foofoo`
 
 error: usage of wildcard import
-  --> tests/ui/wildcard_imports_2021.rs:230:17
+  --> tests/ui/wildcard_imports_2021.rs:233:17
    |
 LL |             use super::*;
    |                 ^^^^^^^^ help: try: `super::insidefoo`
 
 error: usage of wildcard import
-  --> tests/ui/wildcard_imports_2021.rs:238:13
+  --> tests/ui/wildcard_imports_2021.rs:241:13
    |
 LL |         use crate::super_imports::*;
    |             ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo`
 
 error: usage of wildcard import
-  --> tests/ui/wildcard_imports_2021.rs:247:17
+  --> tests/ui/wildcard_imports_2021.rs:250:17
    |
 LL |             use super::super::*;
    |                 ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo`
 
 error: usage of wildcard import
-  --> tests/ui/wildcard_imports_2021.rs:256:13
+  --> tests/ui/wildcard_imports_2021.rs:259:13
    |
 LL |         use super::super::super_imports::*;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo`
 
 error: usage of wildcard import
-  --> tests/ui/wildcard_imports_2021.rs:264:13
+  --> tests/ui/wildcard_imports_2021.rs:267:13
    |
 LL |         use super::*;
    |             ^^^^^^^^ help: try: `super::foofoo`
diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed
index 6a9fe007d65..197dd3b94df 100644
--- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed
+++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.fixed
@@ -198,6 +198,7 @@ mod super_imports {
         }
     }
 
+    #[cfg(test)]
     mod test_should_pass {
         use super::*;
 
@@ -206,6 +207,7 @@ mod super_imports {
         }
     }
 
+    #[cfg(test)]
     mod test_should_pass_inside_function {
         fn with_super_inside_function() {
             use super::*;
@@ -213,6 +215,7 @@ mod super_imports {
         }
     }
 
+    #[cfg(test)]
     mod test_should_pass_further_inside {
         fn insidefoo() {}
         mod inner {
diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr
index 11e0bd37769..66adacd95dc 100644
--- a/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr
+++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.edition2021.stderr
@@ -106,31 +106,31 @@ LL |         use super::*;
    |             ^^^^^^^^ help: try: `super::foofoo`
 
 error: usage of wildcard import
-  --> tests/ui/wildcard_imports_2021.rs:230:17
+  --> tests/ui/wildcard_imports_2021.rs:233:17
    |
 LL |             use super::*;
    |                 ^^^^^^^^ help: try: `super::insidefoo`
 
 error: usage of wildcard import
-  --> tests/ui/wildcard_imports_2021.rs:238:13
+  --> tests/ui/wildcard_imports_2021.rs:241:13
    |
 LL |         use crate::super_imports::*;
    |             ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `crate::super_imports::foofoo`
 
 error: usage of wildcard import
-  --> tests/ui/wildcard_imports_2021.rs:247:17
+  --> tests/ui/wildcard_imports_2021.rs:250:17
    |
 LL |             use super::super::*;
    |                 ^^^^^^^^^^^^^^^ help: try: `super::super::foofoo`
 
 error: usage of wildcard import
-  --> tests/ui/wildcard_imports_2021.rs:256:13
+  --> tests/ui/wildcard_imports_2021.rs:259:13
    |
 LL |         use super::super::super_imports::*;
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `super::super::super_imports::foofoo`
 
 error: usage of wildcard import
-  --> tests/ui/wildcard_imports_2021.rs:264:13
+  --> tests/ui/wildcard_imports_2021.rs:267:13
    |
 LL |         use super::*;
    |             ^^^^^^^^ help: try: `super::foofoo`
diff --git a/src/tools/clippy/tests/ui/wildcard_imports_2021.rs b/src/tools/clippy/tests/ui/wildcard_imports_2021.rs
index 18ebc0f5127..606ff080e77 100644
--- a/src/tools/clippy/tests/ui/wildcard_imports_2021.rs
+++ b/src/tools/clippy/tests/ui/wildcard_imports_2021.rs
@@ -199,6 +199,7 @@ mod super_imports {
         }
     }
 
+    #[cfg(test)]
     mod test_should_pass {
         use super::*;
 
@@ -207,6 +208,7 @@ mod super_imports {
         }
     }
 
+    #[cfg(test)]
     mod test_should_pass_inside_function {
         fn with_super_inside_function() {
             use super::*;
@@ -214,6 +216,7 @@ mod super_imports {
         }
     }
 
+    #[cfg(test)]
     mod test_should_pass_further_inside {
         fn insidefoo() {}
         mod inner {
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 7479be90797..7f5d4f4b416 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -991,13 +991,19 @@ impl Config {
     }
 
     fn parse_custom_normalization(&self, line: &str, prefix: &str) -> Option<(String, String)> {
-        if parse_cfg_name_directive(self, line, prefix).outcome == MatchOutcome::Match {
-            let (regex, replacement) = parse_normalize_rule(line)
-                .unwrap_or_else(|| panic!("couldn't parse custom normalization rule: `{line}`"));
-            Some((regex, replacement))
-        } else {
-            None
+        let parsed = parse_cfg_name_directive(self, line, prefix);
+        if parsed.outcome != MatchOutcome::Match {
+            return None;
         }
+        let name = parsed.name.expect("successful match always has a name");
+
+        let Some((regex, replacement)) = parse_normalize_rule(line) else {
+            panic!(
+                "couldn't parse custom normalization rule: `{line}`\n\
+                help: expected syntax is: `{prefix}-{name}: \"REGEX\" -> \"REPLACEMENT\"`"
+            );
+        };
+        Some((regex, replacement))
     }
 
     fn parse_name_directive(&self, line: &str, directive: &str) -> bool {
diff --git a/src/tools/jsondoclint/src/validator.rs b/src/tools/jsondoclint/src/validator.rs
index cd011dce784..ea1e573384b 100644
--- a/src/tools/jsondoclint/src/validator.rs
+++ b/src/tools/jsondoclint/src/validator.rs
@@ -298,6 +298,7 @@ impl<'a> Validator<'a> {
                 generic_params.iter().for_each(|gpd| self.check_generic_param_def(gpd));
             }
             GenericBound::Outlives(_) => {}
+            GenericBound::Use(_) => {}
         }
     }
 
diff --git a/src/tools/lint-docs/src/groups.rs b/src/tools/lint-docs/src/groups.rs
index f246d71d499..9eaa234bfaf 100644
--- a/src/tools/lint-docs/src/groups.rs
+++ b/src/tools/lint-docs/src/groups.rs
@@ -37,7 +37,7 @@ impl<'a> LintExtractor<'a> {
             .map_err(|e| format!("could not read {}: {}", groups_path.display(), e))?;
         let new_contents =
             contents.replace("{{groups-table}}", &self.make_groups_table(lints, &groups)?);
-        // Delete the output because rustbuild uses hard links in its copies.
+        // Delete the output because bootstrap uses hard links in its copies.
         let _ = fs::remove_file(&groups_path);
         fs::write(&groups_path, new_contents)
             .map_err(|e| format!("could not write to {}: {}", groups_path.display(), e))?;
diff --git a/src/tools/lint-docs/src/lib.rs b/src/tools/lint-docs/src/lib.rs
index 72d6a495e7e..48e2dd09876 100644
--- a/src/tools/lint-docs/src/lib.rs
+++ b/src/tools/lint-docs/src/lib.rs
@@ -532,7 +532,7 @@ impl<'a> LintExtractor<'a> {
         }
         add_rename_redirect(level, &mut result);
         let out_path = self.out_path.join("listing").join(level.doc_filename());
-        // Delete the output because rustbuild uses hard links in its copies.
+        // Delete the output because bootstrap uses hard links in its copies.
         let _ = fs::remove_file(&out_path);
         fs::write(&out_path, result)
             .map_err(|e| format!("could not write to {}: {}", out_path.display(), e))?;
diff --git a/src/tools/miri/cargo-miri/src/phases.rs b/src/tools/miri/cargo-miri/src/phases.rs
index 8d48b9c8ad1..3743446e276 100644
--- a/src/tools/miri/cargo-miri/src/phases.rs
+++ b/src/tools/miri/cargo-miri/src/phases.rs
@@ -104,9 +104,17 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
             miri_for_host()
         )
     });
-    let host = &rustc_version.host;
-    let target = get_arg_flag_value("--target");
-    let target = target.as_ref().unwrap_or(host);
+    let mut targets = get_arg_flag_values("--target").collect::<Vec<_>>();
+    // If `targets` is empty, we need to add a `--target $HOST` flag ourselves, and also ensure
+    // that the host target is indeed setup.
+    let target_flag = if targets.is_empty() {
+        let host = &rustc_version.host;
+        targets.push(host.clone());
+        Some(host)
+    } else {
+        // We don't need to add a `--target` flag, we just forward the user's flags.
+        None
+    };
 
     // If cleaning the target directory & sysroot cache,
     // delete them then exit. There is no reason to setup a new
@@ -118,8 +126,11 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
         return;
     }
 
-    // We always setup.
-    let miri_sysroot = setup(&subcommand, target, &rustc_version, verbose, quiet);
+    for target in &targets {
+        // We always setup.
+        setup(&subcommand, target.as_str(), &rustc_version, verbose, quiet);
+    }
+    let miri_sysroot = get_sysroot_dir();
 
     // Invoke actual cargo for the job, but with different flags.
     // We re-use `cargo test` and `cargo run`, which makes target and binary handling very easy but
@@ -155,10 +166,9 @@ pub fn phase_cargo_miri(mut args: impl Iterator<Item = String>) {
     // This is needed to make the `target.runner` settings do something,
     // and it later helps us detect which crates are proc-macro/build-script
     // (host crates) and which crates are needed for the program itself.
-    if get_arg_flag_value("--target").is_none() {
-        // No target given. Explicitly pick the host.
+    if let Some(target_flag) = target_flag {
         cmd.arg("--target");
-        cmd.arg(host);
+        cmd.arg(target_flag);
     }
 
     // Set ourselves as runner for al binaries invoked by cargo.
diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh
index 67985f9b7d6..5e75638f467 100755
--- a/src/tools/miri/ci/ci.sh
+++ b/src/tools/miri/ci/ci.sh
@@ -41,9 +41,11 @@ function run_tests {
   if [ -n "${TEST_TARGET-}" ]; then
     begingroup "Testing foreign architecture $TEST_TARGET"
     TARGET_FLAG="--target $TEST_TARGET"
+    MULTI_TARGET_FLAG=""
   else
     begingroup "Testing host architecture"
     TARGET_FLAG=""
+    MULTI_TARGET_FLAG="--multi-target"
   fi
 
   ## ui test suite
@@ -93,7 +95,7 @@ function run_tests {
     echo 'build.rustc-wrapper = "thisdoesnotexist"' > .cargo/config.toml
   fi
   # Run the actual test
-  time ${PYTHON} test-cargo-miri/run-test.py $TARGET_FLAG
+  time ${PYTHON} test-cargo-miri/run-test.py $TARGET_FLAG $MULTI_TARGET_FLAG
   # Clean up
   unset RUSTC MIRI
   rm -rf .cargo
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index 5a35166769e..e90d3732ca5 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-66b4f0021bfb11a8c20d084c99a40f4a78ce1d38
+99b7134389e9766462601a2fc4013840b9d31745
diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs
index 9d8e44ce409..9f3fa075f38 100644
--- a/src/tools/miri/src/bin/miri.rs
+++ b/src/tools/miri/src/bin/miri.rs
@@ -592,6 +592,9 @@ fn main() {
             let num_cpus = param
                 .parse::<u32>()
                 .unwrap_or_else(|err| show_error!("-Zmiri-num-cpus requires a `u32`: {}", err));
+            if !(1..=miri::MAX_CPUS).contains(&usize::try_from(num_cpus).unwrap()) {
+                show_error!("-Zmiri-num-cpus must be in the range 1..={}", miri::MAX_CPUS);
+            }
             miri_config.num_cpus = num_cpus;
         } else if let Some(param) = arg.strip_prefix("-Zmiri-force-page-size=") {
             let page_size = param.parse::<u64>().unwrap_or_else(|err| {
diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs
index f65f49a75dd..774b36919fe 100644
--- a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs
+++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs
@@ -136,8 +136,16 @@ impl StackCache {
 
 impl PartialEq for Stack {
     fn eq(&self, other: &Self) -> bool {
-        // All the semantics of Stack are in self.borrows, everything else is caching
-        self.borrows == other.borrows
+        let Stack {
+            borrows,
+            unknown_bottom,
+            // The cache is ignored for comparison.
+            #[cfg(feature = "stack-cache")]
+                cache: _,
+            #[cfg(feature = "stack-cache")]
+                unique_range: _,
+        } = self;
+        *borrows == other.borrows && *unknown_bottom == other.unknown_bottom
     }
 }
 
diff --git a/src/tools/miri/src/concurrency/cpu_affinity.rs b/src/tools/miri/src/concurrency/cpu_affinity.rs
new file mode 100644
index 00000000000..8df26d718bf
--- /dev/null
+++ b/src/tools/miri/src/concurrency/cpu_affinity.rs
@@ -0,0 +1,90 @@
+use rustc_middle::ty::layout::LayoutOf;
+use rustc_target::abi::Endian;
+
+use crate::*;
+
+/// The maximum number of CPUs supported by miri.
+///
+/// This value is compatible with the libc `CPU_SETSIZE` constant and corresponds to the number
+/// of CPUs that a `cpu_set_t` can contain.
+///
+/// Real machines can have more CPUs than this number, and there exist APIs to set their affinity,
+/// but this is not currently supported by miri.
+pub const MAX_CPUS: usize = 1024;
+
+/// A thread's CPU affinity mask determines the set of CPUs on which it is eligible to run.
+// the actual representation depends on the target's endianness and pointer width.
+// See CpuAffinityMask::set for details
+#[derive(Clone)]
+pub(crate) struct CpuAffinityMask([u8; Self::CPU_MASK_BYTES]);
+
+impl CpuAffinityMask {
+    pub(crate) const CPU_MASK_BYTES: usize = MAX_CPUS / 8;
+
+    pub fn new<'tcx>(cx: &impl LayoutOf<'tcx>, cpu_count: u32) -> Self {
+        let mut this = Self([0; Self::CPU_MASK_BYTES]);
+
+        // the default affinity mask includes only the available CPUs
+        for i in 0..cpu_count as usize {
+            this.set(cx, i);
+        }
+
+        this
+    }
+
+    pub fn chunk_size<'tcx>(cx: &impl LayoutOf<'tcx>) -> u64 {
+        // The actual representation of the CpuAffinityMask is [c_ulong; _].
+        let ulong = helpers::path_ty_layout(cx, &["core", "ffi", "c_ulong"]);
+        ulong.size.bytes()
+    }
+
+    fn set<'tcx>(&mut self, cx: &impl LayoutOf<'tcx>, cpu: usize) {
+        // we silently ignore CPUs that are out of bounds. This matches the behavior of
+        // `sched_setaffinity` with a mask that specifies more than `CPU_SETSIZE` CPUs.
+        if cpu >= MAX_CPUS {
+            return;
+        }
+
+        // The actual representation of the CpuAffinityMask is [c_ulong; _].
+        // Within the array elements, we need to use the endianness of the target.
+        let target = &cx.tcx().sess.target;
+        match Self::chunk_size(cx) {
+            4 => {
+                let start = cpu / 32 * 4; // first byte of the correct u32
+                let chunk = self.0[start..].first_chunk_mut::<4>().unwrap();
+                let offset = cpu % 32;
+                *chunk = match target.options.endian {
+                    Endian::Little => (u32::from_le_bytes(*chunk) | 1 << offset).to_le_bytes(),
+                    Endian::Big => (u32::from_be_bytes(*chunk) | 1 << offset).to_be_bytes(),
+                };
+            }
+            8 => {
+                let start = cpu / 64 * 8; // first byte of the correct u64
+                let chunk = self.0[start..].first_chunk_mut::<8>().unwrap();
+                let offset = cpu % 64;
+                *chunk = match target.options.endian {
+                    Endian::Little => (u64::from_le_bytes(*chunk) | 1 << offset).to_le_bytes(),
+                    Endian::Big => (u64::from_be_bytes(*chunk) | 1 << offset).to_be_bytes(),
+                };
+            }
+            other => bug!("chunk size not supported: {other}"),
+        };
+    }
+
+    pub fn as_slice(&self) -> &[u8] {
+        self.0.as_slice()
+    }
+
+    pub fn from_array<'tcx>(
+        cx: &impl LayoutOf<'tcx>,
+        cpu_count: u32,
+        bytes: [u8; Self::CPU_MASK_BYTES],
+    ) -> Option<Self> {
+        // mask by what CPUs are actually available
+        let default = Self::new(cx, cpu_count);
+        let masked = std::array::from_fn(|i| bytes[i] & default.0[i]);
+
+        // at least one thread must be set for the input to be valid
+        masked.iter().any(|b| *b != 0).then_some(Self(masked))
+    }
+}
diff --git a/src/tools/miri/src/concurrency/mod.rs b/src/tools/miri/src/concurrency/mod.rs
index 822d173ac06..17789fe9f87 100644
--- a/src/tools/miri/src/concurrency/mod.rs
+++ b/src/tools/miri/src/concurrency/mod.rs
@@ -1,3 +1,4 @@
+pub mod cpu_affinity;
 pub mod data_race;
 pub mod init_once;
 mod range_object_map;
diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs
index 91865a2192c..d0c9a4600e8 100644
--- a/src/tools/miri/src/concurrency/sync.rs
+++ b/src/tools/miri/src/concurrency/sync.rs
@@ -269,7 +269,7 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let this = self.eval_context_mut();
         if this.mutex_is_locked(mutex) {
             assert_ne!(this.mutex_get_owner(mutex), this.active_thread());
-            this.mutex_enqueue_and_block(mutex, retval, dest);
+            this.mutex_enqueue_and_block(mutex, Some((retval, dest)));
         } else {
             // We can have it right now!
             this.mutex_lock(mutex);
@@ -390,9 +390,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     }
 
     /// Put the thread into the queue waiting for the mutex.
-    /// Once the Mutex becomes available, `retval` will be written to `dest`.
+    ///
+    /// Once the Mutex becomes available and if it exists, `retval_dest.0` will
+    /// be written to `retval_dest.1`.
     #[inline]
-    fn mutex_enqueue_and_block(&mut self, id: MutexId, retval: Scalar, dest: MPlaceTy<'tcx>) {
+    fn mutex_enqueue_and_block(
+        &mut self,
+        id: MutexId,
+        retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>,
+    ) {
         let this = self.eval_context_mut();
         assert!(this.mutex_is_locked(id), "queing on unlocked mutex");
         let thread = this.active_thread();
@@ -403,13 +409,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             callback!(
                 @capture<'tcx> {
                     id: MutexId,
-                    retval: Scalar,
-                    dest: MPlaceTy<'tcx>,
+                    retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>,
                 }
                 @unblock = |this| {
                     assert!(!this.mutex_is_locked(id));
                     this.mutex_lock(id);
-                    this.write_scalar(retval, &dest)?;
+
+                    if let Some((retval, dest)) = retval_dest {
+                        this.write_scalar(retval, &dest)?;
+                    }
+
                     Ok(())
                 }
             ),
diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs
index 718daf93ea0..a53dd7eac1e 100644
--- a/src/tools/miri/src/concurrency/thread.rs
+++ b/src/tools/miri/src/concurrency/thread.rs
@@ -936,6 +936,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // After this all accesses will be treated as occurring in the new thread.
         let old_thread_id = this.machine.threads.set_active_thread_id(new_thread_id);
 
+        // The child inherits its parent's cpu affinity.
+        if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&old_thread_id).cloned() {
+            this.machine.thread_cpu_affinity.insert(new_thread_id, cpuset);
+        }
+
         // Perform the function pointer load in the new thread frame.
         let instance = this.get_ptr_fn(start_routine)?.as_instance()?;
 
diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs
index 9142b8b5fdb..2184a4426c8 100644
--- a/src/tools/miri/src/eval.rs
+++ b/src/tools/miri/src/eval.rs
@@ -282,7 +282,8 @@ pub fn create_ecx<'tcx>(
     })?;
 
     // Make sure we have MIR. We check MIR for some stable monomorphic function in libcore.
-    let sentinel = ecx.try_resolve_path(&["core", "ascii", "escape_default"], Namespace::ValueNS);
+    let sentinel =
+        helpers::try_resolve_path(tcx, &["core", "ascii", "escape_default"], Namespace::ValueNS);
     if !matches!(sentinel, Some(s) if tcx.is_mir_available(s.def.def_id())) {
         tcx.dcx().fatal(
             "the current sysroot was built without `-Zalways-encode-mir`, or libcore seems missing. \
diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs
index 590e8984e99..ba094c988e5 100644
--- a/src/tools/miri/src/helpers.rs
+++ b/src/tools/miri/src/helpers.rs
@@ -18,6 +18,7 @@ use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::middle::dependency_format::Linkage;
 use rustc_middle::middle::exported_symbols::ExportedSymbol;
 use rustc_middle::mir;
+use rustc_middle::ty::layout::MaybeResult;
 use rustc_middle::ty::{
     self,
     layout::{LayoutOf, TyAndLayout},
@@ -159,6 +160,35 @@ fn try_resolve_did(tcx: TyCtxt<'_>, path: &[&str], namespace: Option<Namespace>)
     None
 }
 
+/// Gets an instance for a path; fails gracefully if the path does not exist.
+pub fn try_resolve_path<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    path: &[&str],
+    namespace: Namespace,
+) -> Option<ty::Instance<'tcx>> {
+    let did = try_resolve_did(tcx, path, Some(namespace))?;
+    Some(ty::Instance::mono(tcx, did))
+}
+
+/// Gets an instance for a path.
+#[track_caller]
+pub fn resolve_path<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    path: &[&str],
+    namespace: Namespace,
+) -> ty::Instance<'tcx> {
+    try_resolve_path(tcx, path, namespace)
+        .unwrap_or_else(|| panic!("failed to find required Rust item: {path:?}"))
+}
+
+/// Gets the layout of a type at a path.
+#[track_caller]
+pub fn path_ty_layout<'tcx>(cx: &impl LayoutOf<'tcx>, path: &[&str]) -> TyAndLayout<'tcx> {
+    let ty =
+        resolve_path(cx.tcx(), path, Namespace::TypeNS).ty(cx.tcx(), ty::ParamEnv::reveal_all());
+    cx.layout_of(ty).to_result().ok().unwrap()
+}
+
 /// Call `f` for each exported symbol.
 pub fn iter_exported_symbols<'tcx>(
     tcx: TyCtxt<'tcx>,
@@ -259,23 +289,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         try_resolve_did(*self.eval_context_ref().tcx, path, None).is_some()
     }
 
-    /// Gets an instance for a path; fails gracefully if the path does not exist.
-    fn try_resolve_path(&self, path: &[&str], namespace: Namespace) -> Option<ty::Instance<'tcx>> {
-        let tcx = self.eval_context_ref().tcx.tcx;
-        let did = try_resolve_did(tcx, path, Some(namespace))?;
-        Some(ty::Instance::mono(tcx, did))
-    }
-
-    /// Gets an instance for a path.
-    fn resolve_path(&self, path: &[&str], namespace: Namespace) -> ty::Instance<'tcx> {
-        self.try_resolve_path(path, namespace)
-            .unwrap_or_else(|| panic!("failed to find required Rust item: {path:?}"))
-    }
-
     /// Evaluates the scalar at the specified path.
     fn eval_path(&self, path: &[&str]) -> OpTy<'tcx> {
         let this = self.eval_context_ref();
-        let instance = this.resolve_path(path, Namespace::ValueNS);
+        let instance = resolve_path(*this.tcx, path, Namespace::ValueNS);
         // We don't give a span -- this isn't actually used directly by the program anyway.
         let const_val = this.eval_global(instance).unwrap_or_else(|err| {
             panic!("failed to evaluate required Rust item: {path:?}\n{err:?}")
@@ -344,19 +361,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 "`libc` crate is not reliably available on Windows targets; Miri should not use it there"
             );
         }
-        let ty = this
-            .resolve_path(&["libc", name], Namespace::TypeNS)
-            .ty(*this.tcx, ty::ParamEnv::reveal_all());
-        this.layout_of(ty).unwrap()
+        path_ty_layout(this, &["libc", name])
     }
 
     /// Helper function to get the `TyAndLayout` of a `windows` type
     fn windows_ty_layout(&self, name: &str) -> TyAndLayout<'tcx> {
         let this = self.eval_context_ref();
-        let ty = this
-            .resolve_path(&["std", "sys", "pal", "windows", "c", name], Namespace::TypeNS)
-            .ty(*this.tcx, ty::ParamEnv::reveal_all());
-        this.layout_of(ty).unwrap()
+        path_ty_layout(this, &["std", "sys", "pal", "windows", "c", name])
     }
 
     /// Project to the given *named* field (which must be a struct or union type).
diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs
index 313eac36337..9cd776c9371 100644
--- a/src/tools/miri/src/intrinsics/mod.rs
+++ b/src/tools/miri/src/intrinsics/mod.rs
@@ -392,10 +392,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                         bug!("float_finite: non-float input type {}", x.layout.ty)
                     };
                     Ok(match fty {
-                        FloatTy::F16 => unimplemented!("f16_f128"),
+                        FloatTy::F16 => x.to_scalar().to_f16()?.is_finite(),
                         FloatTy::F32 => x.to_scalar().to_f32()?.is_finite(),
                         FloatTy::F64 => x.to_scalar().to_f64()?.is_finite(),
-                        FloatTy::F128 => unimplemented!("f16_f128"),
+                        FloatTy::F128 => x.to_scalar().to_f128()?.is_finite(),
                     })
                 };
                 match (float_finite(&a)?, float_finite(&b)?) {
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index 8da00861f90..7fb68d782f1 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -129,6 +129,7 @@ pub use crate::borrow_tracker::{
 };
 pub use crate::clock::{Clock, Instant};
 pub use crate::concurrency::{
+    cpu_affinity::MAX_CPUS,
     data_race::{AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd, EvalContextExt as _},
     init_once::{EvalContextExt as _, InitOnceId},
     sync::{CondvarId, EvalContextExt as _, MutexId, RwLockId, SynchronizationObjects},
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 714d97823f4..adb84593561 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -30,6 +30,7 @@ use rustc_target::spec::abi::Abi;
 
 use crate::{
     concurrency::{
+        cpu_affinity::{self, CpuAffinityMask},
         data_race::{self, NaReadType, NaWriteType},
         weak_memory,
     },
@@ -471,6 +472,12 @@ pub struct MiriMachine<'tcx> {
 
     /// The set of threads.
     pub(crate) threads: ThreadManager<'tcx>,
+
+    /// Stores which thread is eligible to run on which CPUs.
+    /// This has no effect at all, it is just tracked to produce the correct result
+    /// in `sched_getaffinity`
+    pub(crate) thread_cpu_affinity: FxHashMap<ThreadId, CpuAffinityMask>,
+
     /// The state of the primitive synchronization objects.
     pub(crate) sync: SynchronizationObjects,
 
@@ -627,6 +634,18 @@ impl<'tcx> MiriMachine<'tcx> {
         let stack_addr = if tcx.pointer_size().bits() < 32 { page_size } else { page_size * 32 };
         let stack_size =
             if tcx.pointer_size().bits() < 32 { page_size * 4 } else { page_size * 16 };
+        assert!(
+            usize::try_from(config.num_cpus).unwrap() <= cpu_affinity::MAX_CPUS,
+            "miri only supports up to {} CPUs, but {} were configured",
+            cpu_affinity::MAX_CPUS,
+            config.num_cpus
+        );
+        let threads = ThreadManager::default();
+        let mut thread_cpu_affinity = FxHashMap::default();
+        if matches!(&*tcx.sess.target.os, "linux" | "freebsd" | "android") {
+            thread_cpu_affinity
+                .insert(threads.active_thread(), CpuAffinityMask::new(&layout_cx, config.num_cpus));
+        }
         MiriMachine {
             tcx,
             borrow_tracker,
@@ -644,7 +663,8 @@ impl<'tcx> MiriMachine<'tcx> {
             fds: shims::FdTable::new(config.mute_stdout_stderr),
             dirs: Default::default(),
             layouts,
-            threads: ThreadManager::default(),
+            threads,
+            thread_cpu_affinity,
             sync: SynchronizationObjects::default(),
             static_roots: Vec::new(),
             profiler,
@@ -765,6 +785,7 @@ impl VisitProvenance for MiriMachine<'_> {
         #[rustfmt::skip]
         let MiriMachine {
             threads,
+            thread_cpu_affinity: _,
             sync: _,
             tls,
             env_vars,
diff --git a/src/tools/miri/src/provenance_gc.rs b/src/tools/miri/src/provenance_gc.rs
index af980ca4819..8edd80744dd 100644
--- a/src/tools/miri/src/provenance_gc.rs
+++ b/src/tools/miri/src/provenance_gc.rs
@@ -30,6 +30,17 @@ impl<T: VisitProvenance> VisitProvenance for Option<T> {
     }
 }
 
+impl<A, B> VisitProvenance for (A, B)
+where
+    A: VisitProvenance,
+    B: VisitProvenance,
+{
+    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
+        self.0.visit_provenance(visit);
+        self.1.visit_provenance(visit);
+    }
+}
+
 impl<T: VisitProvenance> VisitProvenance for std::cell::RefCell<T> {
     fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
         self.borrow().visit_provenance(visit)
diff --git a/src/tools/miri/src/shims/tls.rs b/src/tools/miri/src/shims/tls.rs
index c91386fa877..87074468789 100644
--- a/src/tools/miri/src/shims/tls.rs
+++ b/src/tools/miri/src/shims/tls.rs
@@ -36,9 +36,9 @@ pub struct TlsData<'tcx> {
     /// pthreads-style thread-local storage.
     keys: BTreeMap<TlsKey, TlsEntry<'tcx>>,
 
-    /// A single per thread destructor of the thread local storage (that's how
-    /// things work on macOS) with a data argument.
-    macos_thread_dtors: BTreeMap<ThreadId, (ty::Instance<'tcx>, Scalar)>,
+    /// On macOS, each thread holds a list of destructor functions with their
+    /// respective data arguments.
+    macos_thread_dtors: BTreeMap<ThreadId, Vec<(ty::Instance<'tcx>, Scalar)>>,
 }
 
 impl<'tcx> Default for TlsData<'tcx> {
@@ -119,26 +119,15 @@ impl<'tcx> TlsData<'tcx> {
         }
     }
 
-    /// Set the thread wide destructor of the thread local storage for the given
-    /// thread. This function is used to implement `_tlv_atexit` shim on MacOS.
-    ///
-    /// Thread wide dtors are available only on MacOS. There is one destructor
-    /// per thread as can be guessed from the following comment in the
-    /// [`_tlv_atexit`
-    /// implementation](https://github.com/opensource-apple/dyld/blob/195030646877261f0c8c7ad8b001f52d6a26f514/src/threadLocalVariables.c#L389):
-    ///
-    /// NOTE: this does not need locks because it only operates on current thread data
-    pub fn set_macos_thread_dtor(
+    /// Add a thread local storage destructor for the given thread. This function
+    /// is used to implement the `_tlv_atexit` shim on MacOS.
+    pub fn add_macos_thread_dtor(
         &mut self,
         thread: ThreadId,
         dtor: ty::Instance<'tcx>,
         data: Scalar,
     ) -> InterpResult<'tcx> {
-        if self.macos_thread_dtors.insert(thread, (dtor, data)).is_some() {
-            throw_unsup_format!(
-                "setting more than one thread local storage destructor for the same thread is not supported"
-            );
-        }
+        self.macos_thread_dtors.entry(thread).or_default().push((dtor, data));
         Ok(())
     }
 
@@ -202,6 +191,10 @@ impl<'tcx> TlsData<'tcx> {
         for TlsEntry { data, .. } in self.keys.values_mut() {
             data.remove(&thread_id);
         }
+
+        if let Some(dtors) = self.macos_thread_dtors.remove(&thread_id) {
+            assert!(dtors.is_empty(), "the destructors should have already been run");
+        }
     }
 }
 
@@ -212,7 +205,7 @@ impl VisitProvenance for TlsData<'_> {
         for scalar in keys.values().flat_map(|v| v.data.values()) {
             scalar.visit_provenance(visit);
         }
-        for (_, scalar) in macos_thread_dtors.values() {
+        for (_, scalar) in macos_thread_dtors.values().flatten() {
             scalar.visit_provenance(visit);
         }
     }
@@ -225,6 +218,7 @@ pub struct TlsDtorsState<'tcx>(TlsDtorsStatePriv<'tcx>);
 enum TlsDtorsStatePriv<'tcx> {
     #[default]
     Init,
+    MacOsDtors,
     PthreadDtors(RunningDtorState),
     /// For Windows Dtors, we store the list of functions that we still have to call.
     /// These are functions from the magic `.CRT$XLB` linker section.
@@ -243,11 +237,10 @@ impl<'tcx> TlsDtorsState<'tcx> {
                 Init => {
                     match this.tcx.sess.target.os.as_ref() {
                         "macos" => {
-                            // The macOS thread wide destructor runs "before any TLS slots get
-                            // freed", so do that first.
-                            this.schedule_macos_tls_dtor()?;
-                            // When that destructor is done, go on with the pthread dtors.
-                            break 'new_state PthreadDtors(Default::default());
+                            // macOS has a _tlv_atexit function that allows
+                            // registering destructors without associated keys.
+                            // These are run first.
+                            break 'new_state MacOsDtors;
                         }
                         _ if this.target_os_is_unix() => {
                             // All other Unixes directly jump to running the pthread dtors.
@@ -266,6 +259,14 @@ impl<'tcx> TlsDtorsState<'tcx> {
                         }
                     }
                 }
+                MacOsDtors => {
+                    match this.schedule_macos_tls_dtor()? {
+                        Poll::Pending => return Ok(Poll::Pending),
+                        // After all macOS destructors are run, the system switches
+                        // to destroying the pthread destructors.
+                        Poll::Ready(()) => break 'new_state PthreadDtors(Default::default()),
+                    }
+                }
                 PthreadDtors(state) => {
                     match this.schedule_next_pthread_tls_dtor(state)? {
                         Poll::Pending => return Ok(Poll::Pending), // just keep going
@@ -328,12 +329,15 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         Ok(())
     }
 
-    /// Schedule the MacOS thread destructor of the thread local storage to be
-    /// executed.
-    fn schedule_macos_tls_dtor(&mut self) -> InterpResult<'tcx> {
+    /// Schedule the macOS thread local storage destructors to be executed.
+    fn schedule_macos_tls_dtor(&mut self) -> InterpResult<'tcx, Poll<()>> {
         let this = self.eval_context_mut();
         let thread_id = this.active_thread();
-        if let Some((instance, data)) = this.machine.tls.macos_thread_dtors.remove(&thread_id) {
+        // macOS keeps track of TLS destructors in a stack. If a destructor
+        // registers another destructor, it will be run next.
+        // See https://github.com/apple-oss-distributions/dyld/blob/d552c40cd1de105f0ec95008e0e0c0972de43456/dyld/DyldRuntimeState.cpp#L2277
+        let dtor = this.machine.tls.macos_thread_dtors.get_mut(&thread_id).and_then(Vec::pop);
+        if let Some((instance, data)) = dtor {
             trace!("Running macos dtor {:?} on {:?} at {:?}", instance, data, thread_id);
 
             this.call_function(
@@ -343,8 +347,11 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 None,
                 StackPopCleanup::Root { cleanup: true },
             )?;
+
+            return Ok(Poll::Pending);
         }
-        Ok(())
+
+        Ok(Poll::Ready(()))
     }
 
     /// Schedule a pthread TLS destructor. Returns `true` if found
diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs
index 7f6a0978103..8fb046b5e64 100644
--- a/src/tools/miri/src/shims/unix/fd.rs
+++ b/src/tools/miri/src/shims/unix/fd.rs
@@ -419,7 +419,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         match result {
             Ok(read_bytes) => {
                 // If reading to `bytes` did not fail, we write those bytes to the buffer.
-                this.write_bytes_ptr(buf, bytes)?;
+                // Crucially, if fewer than `bytes.len()` bytes were read, only write
+                // that much into the output buffer!
+                this.write_bytes_ptr(
+                    buf,
+                    bytes[..usize::try_from(read_bytes).unwrap()].iter().copied(),
+                )?;
                 Ok(read_bytes)
             }
             Err(e) => {
diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs
index 2421f9244f3..3a18d622033 100644
--- a/src/tools/miri/src/shims/unix/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/foreign_items.rs
@@ -3,8 +3,10 @@ use std::str;
 
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_span::Symbol;
+use rustc_target::abi::Size;
 use rustc_target::spec::abi::Abi;
 
+use crate::concurrency::cpu_affinity::CpuAffinityMask;
 use crate::shims::alloc::EvalContextExt as _;
 use crate::shims::unix::*;
 use crate::*;
@@ -571,6 +573,99 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let result = this.nanosleep(req, rem)?;
                 this.write_scalar(Scalar::from_i32(result), dest)?;
             }
+            "sched_getaffinity" => {
+                // Currently this function does not exist on all Unixes, e.g. on macOS.
+                if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "android") {
+                    throw_unsup_format!(
+                        "`sched_getaffinity` is not supported on {}",
+                        this.tcx.sess.target.os
+                    );
+                }
+
+                let [pid, cpusetsize, mask] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let pid = this.read_scalar(pid)?.to_u32()?;
+                let cpusetsize = this.read_target_usize(cpusetsize)?;
+                let mask = this.read_pointer(mask)?;
+
+                // TODO: when https://github.com/rust-lang/miri/issues/3730 is fixed this should use its notion of tid/pid
+                let thread_id = match pid {
+                    0 => this.active_thread(),
+                    _ => throw_unsup_format!("`sched_getaffinity` is only supported with a pid of 0 (indicating the current thread)"),
+                };
+
+                // The mask is stored in chunks, and the size must be a whole number of chunks.
+                let chunk_size = CpuAffinityMask::chunk_size(this);
+
+                if this.ptr_is_null(mask)? {
+                    let einval = this.eval_libc("EFAULT");
+                    this.set_last_error(einval)?;
+                    this.write_scalar(Scalar::from_i32(-1), dest)?;
+                } else if cpusetsize == 0 || cpusetsize.checked_rem(chunk_size).unwrap() != 0 {
+                    // we only copy whole chunks of size_of::<c_ulong>()
+                    let einval = this.eval_libc("EINVAL");
+                    this.set_last_error(einval)?;
+                    this.write_scalar(Scalar::from_i32(-1), dest)?;
+                } else if let Some(cpuset) = this.machine.thread_cpu_affinity.get(&thread_id) {
+                    let cpuset = cpuset.clone();
+                    // we only copy whole chunks of size_of::<c_ulong>()
+                    let byte_count = Ord::min(cpuset.as_slice().len(), cpusetsize.try_into().unwrap());
+                    this.write_bytes_ptr(mask, cpuset.as_slice()[..byte_count].iter().copied())?;
+                    this.write_scalar(Scalar::from_i32(0), dest)?;
+                } else {
+                    // The thread whose ID is pid could not be found
+                    let einval = this.eval_libc("ESRCH");
+                    this.set_last_error(einval)?;
+                    this.write_scalar(Scalar::from_i32(-1), dest)?;
+                }
+            }
+            "sched_setaffinity" => {
+                // Currently this function does not exist on all Unixes, e.g. on macOS.
+                if !matches!(&*this.tcx.sess.target.os, "linux" | "freebsd" | "android") {
+                    throw_unsup_format!(
+                        "`sched_setaffinity` is not supported on {}",
+                        this.tcx.sess.target.os
+                    );
+                }
+
+                let [pid, cpusetsize, mask] =
+                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let pid = this.read_scalar(pid)?.to_u32()?;
+                let cpusetsize = this.read_target_usize(cpusetsize)?;
+                let mask = this.read_pointer(mask)?;
+
+                // TODO: when https://github.com/rust-lang/miri/issues/3730 is fixed this should use its notion of tid/pid
+                let thread_id = match pid {
+                    0 => this.active_thread(),
+                    _ => throw_unsup_format!("`sched_setaffinity` is only supported with a pid of 0 (indicating the current thread)"),
+                };
+
+                if this.ptr_is_null(mask)? {
+                    let einval = this.eval_libc("EFAULT");
+                    this.set_last_error(einval)?;
+                    this.write_scalar(Scalar::from_i32(-1), dest)?;
+                } else {
+                    // NOTE: cpusetsize might be smaller than `CpuAffinityMask::CPU_MASK_BYTES`.
+                    // Any unspecified bytes are treated as zero here (none of the CPUs are configured).
+                    // This is not exactly documented, so we assume that this is the behavior in practice.
+                    let bits_slice = this.read_bytes_ptr_strip_provenance(mask, Size::from_bytes(cpusetsize))?;
+                    // This ignores the bytes beyond `CpuAffinityMask::CPU_MASK_BYTES`
+                    let bits_array: [u8; CpuAffinityMask::CPU_MASK_BYTES] =
+                        std::array::from_fn(|i| bits_slice.get(i).copied().unwrap_or(0));
+                    match CpuAffinityMask::from_array(this, this.machine.num_cpus, bits_array) {
+                        Some(cpuset) => {
+                            this.machine.thread_cpu_affinity.insert(thread_id, cpuset);
+                            this.write_scalar(Scalar::from_i32(0), dest)?;
+                        }
+                        None => {
+                            // The intersection between the mask and the available CPUs was empty.
+                            let einval = this.eval_libc("EINVAL");
+                            this.set_last_error(einval)?;
+                            this.write_scalar(Scalar::from_i32(-1), dest)?;
+                        }
+                    }
+                }
+            }
 
             // Miscellaneous
             "isatty" => {
diff --git a/src/tools/miri/src/shims/unix/linux/foreign_items.rs b/src/tools/miri/src/shims/unix/linux/foreign_items.rs
index e31d43d9190..95bee38cd78 100644
--- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs
@@ -178,19 +178,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
                 this.write_scalar(Scalar::from_i32(SIGRTMAX), dest)?;
             }
-            "sched_getaffinity" => {
-                // This shim isn't useful, aside from the fact that it makes `num_cpus`
-                // fall back to `sysconf` where it will successfully determine the number of CPUs.
-                let [pid, cpusetsize, mask] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
-                this.read_scalar(pid)?.to_i32()?;
-                this.read_target_usize(cpusetsize)?;
-                this.deref_pointer_as(mask, this.libc_ty_layout("cpu_set_t"))?;
-                // FIXME: we just return an error.
-                let einval = this.eval_libc("EINVAL");
-                this.set_last_error(einval)?;
-                this.write_scalar(Scalar::from_i32(-1), dest)?;
-            }
 
             // Incomplete shims that we "stub out" just to get pre-main initialization code to work.
             // These shims are enabled only when the caller is in the standard library.
diff --git a/src/tools/miri/src/shims/unix/macos/foreign_items.rs b/src/tools/miri/src/shims/unix/macos/foreign_items.rs
index 25002f0a611..47b887dec94 100644
--- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs
@@ -1,6 +1,7 @@
 use rustc_span::Symbol;
 use rustc_target::spec::abi::Abi;
 
+use super::sync::EvalContextExt as _;
 use crate::shims::unix::*;
 use crate::*;
 
@@ -132,7 +133,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let dtor = this.get_ptr_fn(dtor)?.as_instance()?;
                 let data = this.read_scalar(data)?;
                 let active_thread = this.active_thread();
-                this.machine.tls.set_macos_thread_dtor(active_thread, dtor, data)?;
+                this.machine.tls.add_macos_thread_dtor(active_thread, dtor, data)?;
             }
 
             // Querying system information
@@ -174,6 +175,27 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_scalar(res, dest)?;
             }
 
+            "os_unfair_lock_lock" => {
+                let [lock_op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                this.os_unfair_lock_lock(lock_op)?;
+            }
+            "os_unfair_lock_trylock" => {
+                let [lock_op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                this.os_unfair_lock_trylock(lock_op, dest)?;
+            }
+            "os_unfair_lock_unlock" => {
+                let [lock_op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                this.os_unfair_lock_unlock(lock_op)?;
+            }
+            "os_unfair_lock_assert_owner" => {
+                let [lock_op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                this.os_unfair_lock_assert_owner(lock_op)?;
+            }
+            "os_unfair_lock_assert_not_owner" => {
+                let [lock_op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                this.os_unfair_lock_assert_not_owner(lock_op)?;
+            }
+
             _ => return Ok(EmulateItemResult::NotSupported),
         };
 
diff --git a/src/tools/miri/src/shims/unix/macos/mod.rs b/src/tools/miri/src/shims/unix/macos/mod.rs
index 09c6507b24f..50fb2b9d328 100644
--- a/src/tools/miri/src/shims/unix/macos/mod.rs
+++ b/src/tools/miri/src/shims/unix/macos/mod.rs
@@ -1 +1,2 @@
 pub mod foreign_items;
+pub mod sync;
diff --git a/src/tools/miri/src/shims/unix/macos/sync.rs b/src/tools/miri/src/shims/unix/macos/sync.rs
new file mode 100644
index 00000000000..5e5fccb587b
--- /dev/null
+++ b/src/tools/miri/src/shims/unix/macos/sync.rs
@@ -0,0 +1,107 @@
+//! Contains macOS-specific synchronization functions.
+//!
+//! For `os_unfair_lock`, see the documentation
+//! <https://developer.apple.com/documentation/os/synchronization?language=objc>
+//! and in case of underspecification its implementation
+//! <https://github.com/apple-oss-distributions/libplatform/blob/a00a4cc36da2110578bcf3b8eeeeb93dcc7f4e11/src/os/lock.c#L645>.
+//!
+//! Note that we don't emulate every edge-case behaviour of the locks. Notably,
+//! we don't abort when locking a lock owned by a thread that has already exited
+//! and we do not detect copying of the lock, but macOS doesn't guarantee anything
+//! in that case either.
+
+use crate::*;
+
+impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
+trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
+    fn os_unfair_lock_getid(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx, MutexId> {
+        let this = self.eval_context_mut();
+        // os_unfair_lock holds a 32-bit value, is initialized with zero and
+        // must be assumed to be opaque. Therefore, we can just store our
+        // internal mutex ID in the structure without anyone noticing.
+        this.mutex_get_or_create_id(lock_op, this.libc_ty_layout("os_unfair_lock"), 0)
+    }
+}
+
+impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
+pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
+    fn os_unfair_lock_lock(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
+        let this = self.eval_context_mut();
+
+        let id = this.os_unfair_lock_getid(lock_op)?;
+        if this.mutex_is_locked(id) {
+            if this.mutex_get_owner(id) == this.active_thread() {
+                // Matching the current macOS implementation: abort on reentrant locking.
+                throw_machine_stop!(TerminationInfo::Abort(
+                    "attempted to lock an os_unfair_lock that is already locked by the current thread".to_owned()
+                ));
+            }
+
+            this.mutex_enqueue_and_block(id, None);
+        } else {
+            this.mutex_lock(id);
+        }
+
+        Ok(())
+    }
+
+    fn os_unfair_lock_trylock(
+        &mut self,
+        lock_op: &OpTy<'tcx>,
+        dest: &MPlaceTy<'tcx>,
+    ) -> InterpResult<'tcx> {
+        let this = self.eval_context_mut();
+
+        let id = this.os_unfair_lock_getid(lock_op)?;
+        if this.mutex_is_locked(id) {
+            // Contrary to the blocking lock function, this does not check for
+            // reentrancy.
+            this.write_scalar(Scalar::from_bool(false), dest)?;
+        } else {
+            this.mutex_lock(id);
+            this.write_scalar(Scalar::from_bool(true), dest)?;
+        }
+
+        Ok(())
+    }
+
+    fn os_unfair_lock_unlock(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
+        let this = self.eval_context_mut();
+
+        let id = this.os_unfair_lock_getid(lock_op)?;
+        if this.mutex_unlock(id)?.is_none() {
+            // Matching the current macOS implementation: abort.
+            throw_machine_stop!(TerminationInfo::Abort(
+                "attempted to unlock an os_unfair_lock not owned by the current thread".to_owned()
+            ));
+        }
+
+        Ok(())
+    }
+
+    fn os_unfair_lock_assert_owner(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
+        let this = self.eval_context_mut();
+
+        let id = this.os_unfair_lock_getid(lock_op)?;
+        if !this.mutex_is_locked(id) || this.mutex_get_owner(id) != this.active_thread() {
+            throw_machine_stop!(TerminationInfo::Abort(
+                "called os_unfair_lock_assert_owner on an os_unfair_lock not owned by the current thread".to_owned()
+            ));
+        }
+
+        Ok(())
+    }
+
+    fn os_unfair_lock_assert_not_owner(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
+        let this = self.eval_context_mut();
+
+        let id = this.os_unfair_lock_getid(lock_op)?;
+        if this.mutex_is_locked(id) && this.mutex_get_owner(id) == this.active_thread() {
+            throw_machine_stop!(TerminationInfo::Abort(
+                "called os_unfair_lock_assert_not_owner on an os_unfair_lock owned by the current thread".to_owned()
+            ));
+        }
+
+        Ok(())
+    }
+}
diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs
index be6732b1b67..e8653117ae9 100644
--- a/src/tools/miri/src/shims/unix/sync.rs
+++ b/src/tools/miri/src/shims/unix/sync.rs
@@ -473,7 +473,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let ret = if this.mutex_is_locked(id) {
             let owner_thread = this.mutex_get_owner(id);
             if owner_thread != this.active_thread() {
-                this.mutex_enqueue_and_block(id, Scalar::from_i32(0), dest.clone());
+                this.mutex_enqueue_and_block(id, Some((Scalar::from_i32(0), dest.clone())));
                 return Ok(());
             } else {
                 // Trying to acquire the same mutex again.
diff --git a/src/tools/miri/test-cargo-miri/run-test.py b/src/tools/miri/test-cargo-miri/run-test.py
index d855c333a75..5b77092979d 100755
--- a/src/tools/miri/test-cargo-miri/run-test.py
+++ b/src/tools/miri/test-cargo-miri/run-test.py
@@ -22,12 +22,17 @@ def fail(msg):
     print("\nTEST FAIL: {}".format(msg))
     sys.exit(1)
 
-def cargo_miri(cmd, quiet = True):
+def cargo_miri(cmd, quiet = True, targets = None):
     args = ["cargo", "miri", cmd] + CARGO_EXTRA_FLAGS
     if quiet:
         args += ["-q"]
-    if ARGS.target:
+
+    if targets is not None:
+        for target in targets:
+            args.extend(("--target", target))
+    elif ARGS.target is not None:
         args += ["--target", ARGS.target]
+
     return args
 
 def normalize_stdout(str):
@@ -186,10 +191,21 @@ def test_cargo_miri_test():
         default_ref, "test.stderr-empty.ref",
         env={'MIRIFLAGS': "-Zmiri-permissive-provenance"},
     )
+    if ARGS.multi_target:
+        test_cargo_miri_multi_target()
+
+
+def test_cargo_miri_multi_target():
+    test("`cargo miri test` (multiple targets)",
+        cargo_miri("test", targets = ["aarch64-unknown-linux-gnu", "s390x-unknown-linux-gnu"]),
+        "test.multiple_targets.stdout.ref", "test.stderr-empty.ref",
+        env={'MIRIFLAGS': "-Zmiri-permissive-provenance"},
+    )
 
 args_parser = argparse.ArgumentParser(description='`cargo miri` testing')
 args_parser.add_argument('--target', help='the target to test')
 args_parser.add_argument('--bless', help='bless the reference files', action='store_true')
+args_parser.add_argument('--multi-target', help='run tests related to multiple targets', action='store_true')
 ARGS = args_parser.parse_args()
 
 os.chdir(os.path.dirname(os.path.realpath(__file__)))
diff --git a/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref b/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref
new file mode 100644
index 00000000000..567c5db07d0
--- /dev/null
+++ b/src/tools/miri/test-cargo-miri/test.multiple_targets.stdout.ref
@@ -0,0 +1,22 @@
+
+running 2 tests
+..
+test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
+
+
+running 2 tests
+..
+test result: ok. 2 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in $TIME
+
+imported main
+imported main
+
+running 6 tests
+...i..
+test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME
+
+
+running 6 tests
+...i..
+test result: ok. 5 passed; 0 failed; 1 ignored; 0 measured; 0 filtered out; finished in $TIME
+
diff --git a/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_assert_not_owner.rs b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_assert_not_owner.rs
new file mode 100644
index 00000000000..d6604f37139
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_assert_not_owner.rs
@@ -0,0 +1,13 @@
+//@ only-target-darwin
+
+use std::cell::UnsafeCell;
+
+fn main() {
+    let lock = UnsafeCell::new(libc::OS_UNFAIR_LOCK_INIT);
+
+    unsafe {
+        libc::os_unfair_lock_lock(lock.get());
+        libc::os_unfair_lock_assert_not_owner(lock.get());
+        //~^ error: abnormal termination: called os_unfair_lock_assert_not_owner on an os_unfair_lock owned by the current thread
+    }
+}
diff --git a/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_assert_not_owner.stderr b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_assert_not_owner.stderr
new file mode 100644
index 00000000000..7e890681c43
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_assert_not_owner.stderr
@@ -0,0 +1,13 @@
+error: abnormal termination: called os_unfair_lock_assert_not_owner on an os_unfair_lock owned by the current thread
+  --> $DIR/apple_os_unfair_lock_assert_not_owner.rs:LL:CC
+   |
+LL |         libc::os_unfair_lock_assert_not_owner(lock.get());
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ called os_unfair_lock_assert_not_owner on an os_unfair_lock owned by the current thread
+   |
+   = note: BACKTRACE:
+   = note: inside `main` at $DIR/apple_os_unfair_lock_assert_not_owner.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_assert_owner.rs b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_assert_owner.rs
new file mode 100644
index 00000000000..ddd8b572eaf
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_assert_owner.rs
@@ -0,0 +1,12 @@
+//@ only-target-darwin
+
+use std::cell::UnsafeCell;
+
+fn main() {
+    let lock = UnsafeCell::new(libc::OS_UNFAIR_LOCK_INIT);
+
+    unsafe {
+        libc::os_unfair_lock_assert_owner(lock.get());
+        //~^ error: abnormal termination: called os_unfair_lock_assert_owner on an os_unfair_lock not owned by the current thread
+    }
+}
diff --git a/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_assert_owner.stderr b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_assert_owner.stderr
new file mode 100644
index 00000000000..3724f7996fb
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_assert_owner.stderr
@@ -0,0 +1,13 @@
+error: abnormal termination: called os_unfair_lock_assert_owner on an os_unfair_lock not owned by the current thread
+  --> $DIR/apple_os_unfair_lock_assert_owner.rs:LL:CC
+   |
+LL |         libc::os_unfair_lock_assert_owner(lock.get());
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ called os_unfair_lock_assert_owner on an os_unfair_lock not owned by the current thread
+   |
+   = note: BACKTRACE:
+   = note: inside `main` at $DIR/apple_os_unfair_lock_assert_owner.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_reentrant.rs b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_reentrant.rs
new file mode 100644
index 00000000000..eb98adeba07
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_reentrant.rs
@@ -0,0 +1,13 @@
+//@ only-target-darwin
+
+use std::cell::UnsafeCell;
+
+fn main() {
+    let lock = UnsafeCell::new(libc::OS_UNFAIR_LOCK_INIT);
+
+    unsafe {
+        libc::os_unfair_lock_lock(lock.get());
+        libc::os_unfair_lock_lock(lock.get());
+        //~^ error: abnormal termination: attempted to lock an os_unfair_lock that is already locked by the current thread
+    }
+}
diff --git a/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_reentrant.stderr b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_reentrant.stderr
new file mode 100644
index 00000000000..644462a1b05
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_reentrant.stderr
@@ -0,0 +1,13 @@
+error: abnormal termination: attempted to lock an os_unfair_lock that is already locked by the current thread
+  --> $DIR/apple_os_unfair_lock_reentrant.rs:LL:CC
+   |
+LL |         libc::os_unfair_lock_lock(lock.get());
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempted to lock an os_unfair_lock that is already locked by the current thread
+   |
+   = note: BACKTRACE:
+   = note: inside `main` at $DIR/apple_os_unfair_lock_reentrant.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_unowned.rs b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_unowned.rs
new file mode 100644
index 00000000000..aed467552ab
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_unowned.rs
@@ -0,0 +1,12 @@
+//@ only-target-darwin
+
+use std::cell::UnsafeCell;
+
+fn main() {
+    let lock = UnsafeCell::new(libc::OS_UNFAIR_LOCK_INIT);
+
+    unsafe {
+        libc::os_unfair_lock_unlock(lock.get());
+        //~^ error: abnormal termination: attempted to unlock an os_unfair_lock not owned by the current thread
+    }
+}
diff --git a/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_unowned.stderr b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_unowned.stderr
new file mode 100644
index 00000000000..6a8d12fa807
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/concurrency/apple_os_unfair_lock_unowned.stderr
@@ -0,0 +1,13 @@
+error: abnormal termination: attempted to unlock an os_unfair_lock not owned by the current thread
+  --> $DIR/apple_os_unfair_lock_unowned.rs:LL:CC
+   |
+LL |         libc::os_unfair_lock_unlock(lock.get());
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ attempted to unlock an os_unfair_lock not owned by the current thread
+   |
+   = note: BACKTRACE:
+   = note: inside `main` at $DIR/apple_os_unfair_lock_unowned.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail-dep/libc/affinity.rs b/src/tools/miri/tests/fail-dep/libc/affinity.rs
new file mode 100644
index 00000000000..c41d1d18018
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/affinity.rs
@@ -0,0 +1,17 @@
+//@ignore-target-windows: only very limited libc on Windows
+//@ignore-target-apple: `sched_setaffinity` is not supported on macOS
+//@compile-flags: -Zmiri-disable-isolation -Zmiri-num-cpus=4
+
+fn main() {
+    use libc::{cpu_set_t, sched_setaffinity};
+
+    use std::mem::size_of;
+
+    // If pid is zero, then the calling thread is used.
+    const PID: i32 = 0;
+
+    let cpuset: cpu_set_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
+
+    let err = unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>() + 1, &cpuset) }; //~ ERROR: memory access failed
+    assert_eq!(err, 0);
+}
diff --git a/src/tools/miri/tests/fail-dep/libc/affinity.stderr b/src/tools/miri/tests/fail-dep/libc/affinity.stderr
new file mode 100644
index 00000000000..c01f15800fa
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/affinity.stderr
@@ -0,0 +1,20 @@
+error: Undefined Behavior: memory access failed: ALLOC has size 128, so pointer to 129 bytes starting at offset 0 is out-of-bounds
+  --> $DIR/affinity.rs:LL:CC
+   |
+LL |     let err = unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>() + 1, &cpuset) };
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ memory access failed: ALLOC has size 128, so pointer to 129 bytes starting at offset 0 is out-of-bounds
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+help: ALLOC was allocated here:
+  --> $DIR/affinity.rs:LL:CC
+   |
+LL |     let cpuset: cpu_set_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
+   |         ^^^^^^
+   = note: BACKTRACE (of the first span):
+   = note: inside `main` at $DIR/affinity.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs
new file mode 100644
index 00000000000..98ef454c88b
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.rs
@@ -0,0 +1,27 @@
+//! We test that if we requested to read 4 bytes, but actually read 3 bytes,
+//! then 3 bytes (not 4) will be initialized.
+//@ignore-target-windows: no file system support on Windows
+//@compile-flags: -Zmiri-disable-isolation
+
+use std::ffi::CString;
+use std::fs::remove_file;
+use std::mem::MaybeUninit;
+
+#[path = "../../utils/mod.rs"]
+mod utils;
+
+fn main() {
+    let path =
+        utils::prepare_with_content("fail-libc-read-and-uninit-premature-eof.txt", &[1u8, 2, 3]);
+    let cpath = CString::new(path.clone().into_os_string().into_encoded_bytes()).unwrap();
+    unsafe {
+        let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY);
+        assert_ne!(fd, -1);
+        let mut buf: MaybeUninit<[u8; 4]> = std::mem::MaybeUninit::uninit();
+        // Read 4 bytes from a 3-byte file.
+        assert_eq!(libc::read(fd, buf.as_mut_ptr().cast::<std::ffi::c_void>(), 4), 3);
+        buf.assume_init(); //~ERROR: Undefined Behavior: constructing invalid value at .value[3]: encountered uninitialized memory, but expected an integer
+        assert_eq!(libc::close(fd), 0);
+    }
+    remove_file(&path).unwrap();
+}
diff --git a/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.stderr b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.stderr
new file mode 100644
index 00000000000..e4c7aba07e3
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/libc-read-and-uninit-premature-eof.stderr
@@ -0,0 +1,15 @@
+error: Undefined Behavior: constructing invalid value at .value[3]: encountered uninitialized memory, but expected an integer
+  --> $DIR/libc-read-and-uninit-premature-eof.rs:LL:CC
+   |
+LL | ...   buf.assume_init();
+   |       ^^^^^^^^^^^^^^^^^ constructing invalid value at .value[3]: encountered uninitialized memory, but expected an integer
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `main` at $DIR/libc-read-and-uninit-premature-eof.rs:LL:CC
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/fail/alloc/global_system_mixup.rs b/src/tools/miri/tests/fail/alloc/global_system_mixup.rs
index 2e88e5644e4..19c62913b4c 100644
--- a/src/tools/miri/tests/fail/alloc/global_system_mixup.rs
+++ b/src/tools/miri/tests/fail/alloc/global_system_mixup.rs
@@ -4,7 +4,7 @@
 
 //@normalize-stderr-test: "using [A-Za-z]+ heap deallocation operation" -> "using PLATFORM heap deallocation operation"
 //@normalize-stderr-test: "\| +\^+" -> "| ^"
-//@normalize-stderr-test: "libc::free\([^()]*\)|unsafe \{ HeapFree\([^()]*\) \};" -> "FREE();"
+//@normalize-stderr-test: "libc::free\([^()]*\)|unsafe \{ HeapFree\([^}]*\};" -> "FREE();"
 
 #![feature(allocator_api, slice_ptr_get)]
 
diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.rs b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.rs
new file mode 100644
index 00000000000..023bce1616b
--- /dev/null
+++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.rs
@@ -0,0 +1,20 @@
+#![feature(raw_ref_op)]
+#![feature(strict_provenance)]
+use std::ptr;
+
+fn direct_raw(x: *const (i32, i32)) -> *const i32 {
+    unsafe { &raw const (*x).0 }
+}
+
+// Ensure that if a raw pointer is created via an intermediate
+// reference, we catch that. (Just in case someone decides to
+// desugar this differenly or so.)
+fn via_ref(x: *const (i32, i32)) -> *const i32 {
+    unsafe { &(*x).0 as *const i32 } //~ERROR: dangling pointer
+}
+
+fn main() {
+    let ptr = ptr::without_provenance(0x10);
+    direct_raw(ptr); // this is fine
+    via_ref(ptr); // this is not
+}
diff --git a/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.stderr b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.stderr
new file mode 100644
index 00000000000..37f2bb39557
--- /dev/null
+++ b/src/tools/miri/tests/fail/dangling_pointers/dangling_pointer_to_raw_pointer.stderr
@@ -0,0 +1,20 @@
+error: Undefined Behavior: out-of-bounds pointer use: 0x10[noalloc] is a dangling pointer (it has no provenance)
+  --> $DIR/dangling_pointer_to_raw_pointer.rs:LL:CC
+   |
+LL |     unsafe { &(*x).0 as *const i32 }
+   |              ^^^^^^^ out-of-bounds pointer use: 0x10[noalloc] is a dangling pointer (it has no provenance)
+   |
+   = help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
+   = help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
+   = note: BACKTRACE:
+   = note: inside `via_ref` at $DIR/dangling_pointer_to_raw_pointer.rs:LL:CC
+note: inside `main`
+  --> $DIR/dangling_pointer_to_raw_pointer.rs:LL:CC
+   |
+LL |     via_ref(ptr); // this is not
+   |     ^^^^^^^^^^^^
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/miri/tests/pass-dep/concurrency/apple-os-unfair-lock.rs b/src/tools/miri/tests/pass-dep/concurrency/apple-os-unfair-lock.rs
new file mode 100644
index 00000000000..c2b9c37bbfb
--- /dev/null
+++ b/src/tools/miri/tests/pass-dep/concurrency/apple-os-unfair-lock.rs
@@ -0,0 +1,25 @@
+//@ only-target-darwin
+
+use std::cell::UnsafeCell;
+
+fn main() {
+    let lock = UnsafeCell::new(libc::OS_UNFAIR_LOCK_INIT);
+
+    unsafe {
+        libc::os_unfair_lock_lock(lock.get());
+        libc::os_unfair_lock_assert_owner(lock.get());
+        assert!(!libc::os_unfair_lock_trylock(lock.get()));
+        libc::os_unfair_lock_unlock(lock.get());
+
+        libc::os_unfair_lock_assert_not_owner(lock.get());
+    }
+
+    // `os_unfair_lock`s can be moved and leaked.
+    // In the real implementation, even moving it while locked is possible
+    // (and "forks" the lock, i.e. old and new location have independent wait queues);
+    // Miri behavior differs here and anyway none of this is documented.
+    let lock = lock;
+    let locked = unsafe { libc::os_unfair_lock_trylock(lock.get()) };
+    assert!(locked);
+    let _lock = lock;
+}
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs b/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs
new file mode 100644
index 00000000000..0e482ab2601
--- /dev/null
+++ b/src/tools/miri/tests/pass-dep/libc/libc-affinity.rs
@@ -0,0 +1,218 @@
+//@ignore-target-windows: only very limited libc on Windows
+//@ignore-target-apple: `sched_{g, s}etaffinity` are not supported on macOS
+//@compile-flags: -Zmiri-disable-isolation -Zmiri-num-cpus=4
+#![feature(io_error_more)]
+#![feature(pointer_is_aligned_to)]
+#![feature(strict_provenance)]
+
+use libc::{cpu_set_t, sched_getaffinity, sched_setaffinity};
+use std::mem::{size_of, size_of_val};
+
+// If pid is zero, then the calling thread is used.
+const PID: i32 = 0;
+
+fn null_pointers() {
+    let err = unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), std::ptr::null_mut()) };
+    assert_eq!(err, -1);
+
+    let err = unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>(), std::ptr::null()) };
+    assert_eq!(err, -1);
+}
+
+fn configure_no_cpus() {
+    let cpu_count = std::thread::available_parallelism().unwrap().get();
+
+    let mut cpuset: cpu_set_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
+
+    // configuring no CPUs will fail
+    let err = unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>(), &cpuset) };
+    assert_eq!(err, -1);
+    assert_eq!(std::io::Error::last_os_error().kind(), std::io::ErrorKind::InvalidInput);
+
+    // configuring no (physically available) CPUs will fail
+    unsafe { libc::CPU_SET(cpu_count, &mut cpuset) };
+    let err = unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>(), &cpuset) };
+    assert_eq!(err, -1);
+    assert_eq!(std::io::Error::last_os_error().kind(), std::io::ErrorKind::InvalidInput);
+}
+
+fn configure_unavailable_cpu() {
+    let cpu_count = std::thread::available_parallelism().unwrap().get();
+
+    // Safety: valid value for this type
+    let mut cpuset: cpu_set_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
+
+    let err = unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut cpuset) };
+    assert_eq!(err, 0);
+
+    // by default, only available CPUs are configured
+    for i in 0..cpu_count {
+        assert!(unsafe { libc::CPU_ISSET(i, &cpuset) });
+    }
+    assert!(unsafe { !libc::CPU_ISSET(cpu_count, &cpuset) });
+
+    // configure CPU that we don't have
+    unsafe { libc::CPU_SET(cpu_count, &mut cpuset) };
+
+    let err = unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>(), &cpuset) };
+    assert_eq!(err, 0);
+
+    let err = unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut cpuset) };
+    assert_eq!(err, 0);
+
+    // the CPU is not set because it is not available
+    assert!(!unsafe { libc::CPU_ISSET(cpu_count, &cpuset) });
+}
+
+fn large_set() {
+    // rust's libc does not currently implement dynamic cpu set allocation
+    // and related functions like `CPU_ZERO_S`. So we have to be creative
+
+    // i.e. this has 2048 bits, twice the standard number
+    let mut cpuset = [u64::MAX; 32];
+
+    let err = unsafe { sched_setaffinity(PID, size_of_val(&cpuset), cpuset.as_ptr().cast()) };
+    assert_eq!(err, 0);
+
+    let err = unsafe { sched_getaffinity(PID, size_of_val(&cpuset), cpuset.as_mut_ptr().cast()) };
+    assert_eq!(err, 0);
+}
+
+fn get_small_cpu_mask() {
+    let mut cpuset: cpu_set_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
+
+    // should be 4 on 32-bit systems and 8 otherwise for systems that implement sched_getaffinity
+    let step = size_of::<std::ffi::c_ulong>();
+
+    for i in (0..=2).map(|x| x * step) {
+        if i == 0 {
+            // 0 always fails
+            let err = unsafe { sched_getaffinity(PID, i, &mut cpuset) };
+            assert_eq!(err, -1, "fail for {}", i);
+            assert_eq!(std::io::Error::last_os_error().kind(), std::io::ErrorKind::InvalidInput);
+        } else {
+            // other whole multiples of the size of c_ulong works
+            let err = unsafe { sched_getaffinity(PID, i, &mut cpuset) };
+            assert_eq!(err, 0, "fail for {i}");
+        }
+
+        // anything else returns an error
+        for j in 1..step {
+            let err = unsafe { sched_getaffinity(PID, i + j, &mut cpuset) };
+            assert_eq!(err, -1, "success for {}", i + j);
+            assert_eq!(std::io::Error::last_os_error().kind(), std::io::ErrorKind::InvalidInput);
+        }
+    }
+}
+
+fn set_small_cpu_mask() {
+    let mut cpuset: cpu_set_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
+
+    let err = unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut cpuset) };
+    assert_eq!(err, 0);
+
+    // setting a mask of size 0 is invalid
+    let err = unsafe { sched_setaffinity(PID, 0, &cpuset) };
+    assert_eq!(err, -1);
+    assert_eq!(std::io::Error::last_os_error().kind(), std::io::ErrorKind::InvalidInput);
+
+    // on LE systems, any other number of bytes (at least up to `size_of<cpu_set_t>()`) will work.
+    // on BE systems the CPUs 0..8 are stored in the right-most byte of the first chunk. If that
+    // byte is not included, no valid CPUs are configured. We skip those cases.
+    let cpu_zero_included_length =
+        if cfg!(target_endian = "little") { 1 } else { core::mem::size_of::<std::ffi::c_ulong>() };
+
+    for i in cpu_zero_included_length..24 {
+        let err = unsafe { sched_setaffinity(PID, i, &cpuset) };
+        assert_eq!(err, 0, "fail for {i}");
+    }
+}
+
+fn set_custom_cpu_mask() {
+    let cpu_count = std::thread::available_parallelism().unwrap().get();
+
+    assert!(cpu_count > 1, "this test cannot do anything interesting with just one thread");
+
+    let mut cpuset: cpu_set_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
+
+    // at the start, thread 1 should be set
+    let err = unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut cpuset) };
+    assert_eq!(err, 0);
+    assert!(unsafe { libc::CPU_ISSET(1, &cpuset) });
+
+    // make a valid mask
+    unsafe { libc::CPU_ZERO(&mut cpuset) };
+    unsafe { libc::CPU_SET(0, &mut cpuset) };
+
+    // giving a smaller mask is fine
+    let err = unsafe { sched_setaffinity(PID, 8, &cpuset) };
+    assert_eq!(err, 0);
+
+    // and actually disables other threads
+    let err = unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut cpuset) };
+    assert_eq!(err, 0);
+    assert!(unsafe { !libc::CPU_ISSET(1, &cpuset) });
+
+    // it is important that we reset the cpu mask now for future tests
+    for i in 0..cpu_count {
+        unsafe { libc::CPU_SET(i, &mut cpuset) };
+    }
+
+    let err = unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>(), &cpuset) };
+    assert_eq!(err, 0);
+}
+
+fn parent_child() {
+    let cpu_count = std::thread::available_parallelism().unwrap().get();
+
+    assert!(cpu_count > 1, "this test cannot do anything interesting with just one thread");
+
+    // configure the parent thread to only run only on CPU 0
+    let mut parent_cpuset: cpu_set_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
+    unsafe { libc::CPU_SET(0, &mut parent_cpuset) };
+
+    let err = unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>(), &parent_cpuset) };
+    assert_eq!(err, 0);
+
+    std::thread::scope(|spawner| {
+        spawner.spawn(|| {
+            let mut cpuset: cpu_set_t = unsafe { core::mem::MaybeUninit::zeroed().assume_init() };
+
+            let err = unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut cpuset) };
+            assert_eq!(err, 0);
+
+            // the child inherits its parent's set
+            assert!(unsafe { libc::CPU_ISSET(0, &cpuset) });
+            assert!(unsafe { !libc::CPU_ISSET(1, &cpuset) });
+
+            // configure cpu 1 for the child
+            unsafe { libc::CPU_SET(1, &mut cpuset) };
+        });
+    });
+
+    let err = unsafe { sched_getaffinity(PID, size_of::<cpu_set_t>(), &mut parent_cpuset) };
+    assert_eq!(err, 0);
+
+    // the parent's set should be unaffected
+    assert!(unsafe { !libc::CPU_ISSET(1, &parent_cpuset) });
+
+    // it is important that we reset the cpu mask now for future tests
+    let mut cpuset = parent_cpuset;
+    for i in 0..cpu_count {
+        unsafe { libc::CPU_SET(i, &mut cpuset) };
+    }
+
+    let err = unsafe { sched_setaffinity(PID, size_of::<cpu_set_t>(), &cpuset) };
+    assert_eq!(err, 0);
+}
+
+fn main() {
+    null_pointers();
+    configure_no_cpus();
+    configure_unavailable_cpu();
+    large_set();
+    get_small_cpu_mask();
+    set_small_cpu_mask();
+    set_custom_cpu_mask();
+    parent_child();
+}
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs
index da685e5c6b7..eddea92353e 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-fs.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-fs.rs
@@ -36,6 +36,7 @@ fn main() {
     #[cfg(target_os = "linux")]
     test_sync_file_range();
     test_isatty();
+    test_read_and_uninit();
 }
 
 fn test_file_open_unix_allow_two_args() {
@@ -388,3 +389,37 @@ fn test_isatty() {
         remove_file(&path).unwrap();
     }
 }
+
+fn test_read_and_uninit() {
+    use std::mem::MaybeUninit;
+    {
+        // We test that libc::read initializes its buffer.
+        let path = utils::prepare_with_content("pass-libc-read-and-uninit.txt", &[1u8, 2, 3]);
+        let cpath = CString::new(path.clone().into_os_string().into_encoded_bytes()).unwrap();
+        unsafe {
+            let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY);
+            assert_ne!(fd, -1);
+            let mut buf: MaybeUninit<[u8; 2]> = std::mem::MaybeUninit::uninit();
+            assert_eq!(libc::read(fd, buf.as_mut_ptr().cast::<std::ffi::c_void>(), 2), 2);
+            let buf = buf.assume_init();
+            assert_eq!(buf, [1, 2]);
+            assert_eq!(libc::close(fd), 0);
+        }
+        remove_file(&path).unwrap();
+    }
+    {
+        // We test that if we requested to read 4 bytes, but actually read 3 bytes, then
+        // 3 bytes (not 4) will be overwritten, and remaining byte will be left as-is.
+        let path = utils::prepare_with_content("pass-libc-read-and-uninit-2.txt", &[1u8, 2, 3]);
+        let cpath = CString::new(path.clone().into_os_string().into_encoded_bytes()).unwrap();
+        unsafe {
+            let fd = libc::open(cpath.as_ptr(), libc::O_RDONLY);
+            assert_ne!(fd, -1);
+            let mut buf = [42u8; 5];
+            assert_eq!(libc::read(fd, buf.as_mut_ptr().cast::<std::ffi::c_void>(), 4), 3);
+            assert_eq!(buf, [1, 2, 3, 42, 42]);
+            assert_eq!(libc::close(fd), 0);
+        }
+        remove_file(&path).unwrap();
+    }
+}
diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs
index 5464627fa14..6ab18a5345e 100644
--- a/src/tools/miri/tests/pass/float.rs
+++ b/src/tools/miri/tests/pass/float.rs
@@ -4,8 +4,11 @@
 #![feature(f128)]
 #![feature(f16)]
 #![allow(arithmetic_overflow)]
+#![allow(internal_features)]
 
-use std::fmt::Debug;
+use std::any::type_name;
+use std::cmp::min;
+use std::fmt::{Debug, Display, LowerHex};
 use std::hint::black_box;
 use std::{f32, f64};
 
@@ -29,15 +32,41 @@ fn main() {
     test_algebraic();
 }
 
-// Helper function to avoid promotion so that this tests "run-time" casts, not CTFE.
-// Doesn't make a big difference when running this in Miri, but it means we can compare this
-// with the LLVM backend by running `rustc -Zmir-opt-level=0 -Zsaturating-float-casts`.
-#[track_caller]
-#[inline(never)]
-fn assert_eq<T: PartialEq + Debug>(x: T, y: T) {
-    assert_eq!(x, y);
+trait Float: Copy + PartialEq + Debug {
+    /// The unsigned integer with the same bit width as this float
+    type Int: Copy + PartialEq + LowerHex + Debug;
+    const BITS: u32 = size_of::<Self>() as u32 * 8;
+    const EXPONENT_BITS: u32 = Self::BITS - Self::SIGNIFICAND_BITS - 1;
+    const SIGNIFICAND_BITS: u32;
+
+    /// The saturated (all ones) value of the exponent (infinity representation)
+    const EXPONENT_SAT: u32 = (1 << Self::EXPONENT_BITS) - 1;
+
+    /// The exponent bias value (max representable positive exponent)
+    const EXPONENT_BIAS: u32 = Self::EXPONENT_SAT >> 1;
+
+    fn to_bits(self) -> Self::Int;
+}
+
+macro_rules! impl_float {
+    ($ty:ty, $ity:ty) => {
+        impl Float for $ty {
+            type Int = $ity;
+            // Just get this from std's value, which includes the implicit digit
+            const SIGNIFICAND_BITS: u32 = <$ty>::MANTISSA_DIGITS - 1;
+
+            fn to_bits(self) -> Self::Int {
+                self.to_bits()
+            }
+        }
+    };
 }
 
+impl_float!(f16, u16);
+impl_float!(f32, u32);
+impl_float!(f64, u64);
+impl_float!(f128, u128);
+
 trait FloatToInt<Int>: Copy {
     fn cast(self) -> Int;
     unsafe fn cast_unchecked(self) -> Int;
@@ -58,19 +87,61 @@ macro_rules! float_to_int {
     };
 }
 
+float_to_int!(f16 => i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
 float_to_int!(f32 => i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
 float_to_int!(f64 => i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
+float_to_int!(f128 => i8, u8, i16, u16, i32, u32, i64, u64, i128, u128);
 
 /// Test this cast both via `as` and via `approx_unchecked` (i.e., it must not saturate).
 #[track_caller]
 #[inline(never)]
-fn test_both_cast<F, I>(x: F, y: I)
+fn test_both_cast<F, I>(x: F, y: I, msg: impl Display)
 where
     F: FloatToInt<I>,
     I: PartialEq + Debug,
 {
-    assert_eq!(x.cast(), y);
-    assert_eq!(unsafe { x.cast_unchecked() }, y);
+    let f_tname = type_name::<F>();
+    let i_tname = type_name::<I>();
+    assert_eq!(x.cast(), y, "{f_tname} -> {i_tname}: {msg}");
+    assert_eq!(unsafe { x.cast_unchecked() }, y, "{f_tname} -> {i_tname}: {msg}",);
+}
+
+/// Helper function to avoid promotion so that this tests "run-time" casts, not CTFE.
+/// Doesn't make a big difference when running this in Miri, but it means we can compare this
+/// with the LLVM backend by running `rustc -Zmir-opt-level=0 -Zsaturating-float-casts`.
+#[track_caller]
+#[inline(never)]
+fn assert_eq<T: PartialEq + Debug>(x: T, y: T) {
+    assert_eq!(x, y);
+}
+
+/// The same as `assert_eq` except prints a specific message on failure
+#[track_caller]
+#[inline(never)]
+fn assert_eq_msg<T: PartialEq + Debug>(x: T, y: T, msg: impl Display) {
+    assert_eq!(x, y, "{msg}");
+}
+
+/// Check that floats have bitwise equality
+fn assert_biteq<F: Float>(a: F, b: F, msg: impl Display) {
+    let ab = a.to_bits();
+    let bb = b.to_bits();
+    let tname = type_name::<F>();
+    let width = (2 + F::BITS / 4) as usize;
+    assert_eq_msg::<F::Int>(
+        ab,
+        bb,
+        format_args!("({ab:#0width$x} != {bb:#0width$x}) {tname}: {msg}"),
+    );
+}
+
+/// Check that two floats have equality
+fn assert_feq<F: Float>(a: F, b: F, msg: impl Display) {
+    let ab = a.to_bits();
+    let bb = b.to_bits();
+    let tname = type_name::<F>();
+    let width = (2 + F::BITS / 4) as usize;
+    assert_eq_msg::<F>(a, b, format_args!("({ab:#0width$x} != {bb:#0width$x}) {tname}: {msg}"));
 }
 
 fn basic() {
@@ -148,155 +219,368 @@ fn basic() {
     assert_eq!(34.2f64.abs(), 34.2f64);
 }
 
-/// Many of these test values are taken from
+/// Test casts from floats to ints and back
+macro_rules! test_ftoi_itof {
+    (
+        f: $fty:ty,
+        i: $ity:ty,
+        // Int min and max as float literals
+        imin_f: $imin_f:literal,
+        imax_f: $imax_f:literal $(,)?
+    ) => {{
+        /// By default we test float to int `as` casting as well as to_int_unchecked
+        fn assert_ftoi(f: $fty, i: $ity, msg: &str) {
+            #[allow(unused_comparisons)]
+            if <$ity>::MIN >= 0 && f < 0.0 {
+                // If `ity` is signed and `f` is negative, it is unrepresentable so skip
+                // unchecked casts.
+                assert_ftoi_unrep(f, i, msg);
+            } else {
+                test_both_cast::<$fty, $ity>(f, i, msg);
+            }
+        }
+
+        /// Unrepresentable values only get tested with `as` casting, not unchecked
+        fn assert_ftoi_unrep(f: $fty, i: $ity, msg: &str) {
+            assert_eq_msg::<$ity>(
+                f as $ity,
+                i,
+                format_args!("{} -> {}: {msg}", stringify!($fty), stringify!($ity)),
+            );
+        }
+
+        /// Int to float checks
+        fn assert_itof(i: $ity, f: $fty, msg: &str) {
+            assert_eq_msg::<$fty>(
+                i as $fty,
+                f,
+                format_args!("{} -> {}: {msg}", stringify!($ity), stringify!($fty)),
+            );
+        }
+
+        /// Check both float to int and int to float
+        fn assert_bidir(f: $fty, i: $ity, msg: &str) {
+            assert_ftoi(f, i, msg);
+            assert_itof(i, f, msg);
+        }
+
+        /// Check both float to int and int to float for unrepresentable numbers
+        fn assert_bidir_unrep(f: $fty, i: $ity, msg: &str) {
+            assert_ftoi_unrep(f, i, msg);
+            assert_itof(i, f, msg);
+        }
+
+        let fbits = <$fty>::BITS;
+        let fsig_bits = <$fty>::SIGNIFICAND_BITS;
+        let ibits = <$ity>::BITS;
+        let imax: $ity = <$ity>::MAX;
+        let imin: $ity = <$ity>::MIN;
+        let izero: $ity = 0;
+        #[allow(unused_comparisons)]
+        let isigned = <$ity>::MIN < 0;
+
+        #[allow(overflowing_literals)]
+        let imin_f: $fty = $imin_f;
+        #[allow(overflowing_literals)]
+        let imax_f: $fty = $imax_f;
+
+        // If an integer can fit entirely in the mantissa (counting the hidden bit), every value
+        // can be represented exactly.
+        let all_ints_exact_rep = ibits <= fsig_bits + 1;
+
+        // We can represent the full range of the integer (but possibly not every value) without
+        // saturating to infinity if `1 << (I::BITS - 1)` (single one in the MSB position) is
+        // within the float's dynamic range.
+        let int_range_rep = ibits - 1 < <$fty>::EXPONENT_BIAS;
+
+        // Skip unchecked cast when int min/max would be unrepresentable
+        let assert_ftoi_big = if all_ints_exact_rep { assert_ftoi } else { assert_ftoi_unrep };
+        let assert_bidir_big = if all_ints_exact_rep { assert_bidir } else { assert_bidir_unrep };
+
+        // Near zero representations
+        assert_bidir(0.0, 0, "zero");
+        assert_ftoi(-0.0, 0, "negative zero");
+        assert_ftoi(1.0, 1, "one");
+        assert_ftoi(-1.0, izero.saturating_sub(1), "negative one");
+        assert_ftoi(1.0 - <$fty>::EPSILON, 0, "1.0 - ε");
+        assert_ftoi(1.0 + <$fty>::EPSILON, 1, "1.0 + ε");
+        assert_ftoi(-1.0 + <$fty>::EPSILON, 0, "-1.0 + ε");
+        assert_ftoi(-1.0 - <$fty>::EPSILON, izero.saturating_sub(1), "-1.0 - ε");
+        assert_ftoi(<$fty>::from_bits(0x1), 0, "min subnormal");
+        assert_ftoi(<$fty>::from_bits(0x1 | 1 << (fbits - 1)), 0, "min neg subnormal");
+
+        // Spot checks. Use `saturating_sub` to create negative integers so that unsigned
+        // integers stay at zero.
+        assert_ftoi(0.9, 0, "0.9");
+        assert_ftoi(-0.9, 0, "-0.9");
+        assert_ftoi(1.1, 1, "1.1");
+        assert_ftoi(-1.1, izero.saturating_sub(1), "-1.1");
+        assert_ftoi(1.9, 1, "1.9");
+        assert_ftoi(-1.9, izero.saturating_sub(1), "-1.9");
+        assert_ftoi(5.0, 5, "5.0");
+        assert_ftoi(-5.0, izero.saturating_sub(5), "-5.0");
+        assert_ftoi(5.9, 5, "5.0");
+        assert_ftoi(-5.9, izero.saturating_sub(5), "-5.0");
+
+        // Exercise the middle of the integer's bit range. A power of two fits as long as the
+        // exponent can fit its log2, so cap at the maximum representable power of two (which
+        // is the exponent's bias).
+        let half_i_max: $ity = 1 << min(ibits / 2, <$fty>::EXPONENT_BIAS);
+        let half_i_min = izero.saturating_sub(half_i_max);
+        assert_bidir(half_i_max as $fty, half_i_max, "half int max");
+        assert_bidir(half_i_min as $fty, half_i_min, "half int min");
+
+        // Integer limits
+        assert_bidir_big(imax_f, imax, "i max");
+        assert_bidir_big(imin_f, imin, "i min");
+
+        // We need a small perturbation to test against that does not round up to the next
+        // integer. `f16` needs a smaller perturbation since it only has resolution for ~1 decimal
+        // place near 10^3.
+        let perturb = if fbits < 32 { 0.9 } else { 0.99 };
+        assert_ftoi_big(imax_f + perturb, <$ity>::MAX, "slightly above i max");
+        assert_ftoi_big(imin_f - perturb, <$ity>::MIN, "slightly below i min");
+
+        // Tests for when we can represent the integer's magnitude
+        if int_range_rep {
+            // If the float can represent values larger than the integer, float extremes
+            // will saturate.
+            assert_ftoi_unrep(<$fty>::MAX, imax, "f max");
+            assert_ftoi_unrep(<$fty>::MIN, imin, "f min");
+
+            // Max representable power of 10
+            let pow10_max = (10 as $ity).pow(imax.ilog10());
+
+            // If the power of 10 should be representable (fits in a mantissa), check it
+            if ibits - pow10_max.leading_zeros() - pow10_max.trailing_zeros() <= fsig_bits + 1 {
+                assert_bidir(pow10_max as $fty, pow10_max, "pow10 max");
+            }
+        }
+
+        // Test rounding the largest and smallest integers, but skip this when
+        // all integers have an exact representation (it's less interesting then and the arithmetic gets more complicated).
+        if int_range_rep && !all_ints_exact_rep {
+            // The maximum representable integer is a saturated mantissa (including the implicit
+            // bit), shifted into the int's leftmost position.
+            //
+            // Positive signed integers never use their top bit, so shift by one bit fewer.
+            let sat_mantissa: $ity = (1 << (fsig_bits + 1)) - 1;
+            let adj = if isigned { 1 } else { 0 };
+            let max_rep = sat_mantissa << (sat_mantissa.leading_zeros() - adj);
+
+            // This value should roundtrip exactly
+            assert_bidir(max_rep as $fty, max_rep, "max representable int");
+
+            // The cutoff for where to round to `imax` is halfway between the maximum exactly
+            // representable integer and `imax`. This should round down (to `max_rep`),
+            // i.e., `max_rep as $fty == max_non_sat as $fty`.
+            let max_non_sat = max_rep + ((imax - max_rep) / 2);
+            assert_bidir(max_non_sat as $fty, max_rep, "max non saturating int");
+
+            // So the next value up should round up to the maximum value of the integer
+            assert_bidir_unrep((max_non_sat + 1) as $fty, imax, "min infinite int");
+
+            if isigned {
+                // Floats can always represent the minimum signed number if they can fit the
+                // exponent, because it is just a `1` in the MSB. So, no negative int -> float
+                // conversion will round to negative infinity (if the exponent fits).
+                //
+                // Since `imin` is thus the minimum representable value, we test rounding near
+                // the next value. This happens to be the opposite of the maximum representable
+                // value, and it should roundtrip exactly.
+                let next_min_rep = max_rep.wrapping_neg();
+                assert_bidir(next_min_rep as $fty, next_min_rep, "min representable above imin");
+
+                // Following a similar pattern as for positive numbers, halfway between this value
+                // and `imin` should round back to `next_min_rep`.
+                let min_non_sat = imin - ((imin - next_min_rep) / 2) + 1;
+                assert_bidir(
+                    min_non_sat as $fty,
+                    next_min_rep,
+                    "min int that does not round to imin",
+                );
+
+                // And then anything else saturates to the minimum value.
+                assert_bidir_unrep(
+                    (min_non_sat - 1) as $fty,
+                    imin,
+                    "max negative int that rounds to imin",
+                );
+            }
+        }
+
+        // Check potentially saturating int ranges. (`imax_f` here will be `$fty::INFINITY` if
+        // it cannot be represented as a finite value.)
+        assert_itof(imax, imax_f, "imax");
+        assert_itof(imin, imin_f, "imin");
+
+        // Float limits
+        assert_ftoi_unrep(<$fty>::INFINITY, imax, "f inf");
+        assert_ftoi_unrep(<$fty>::NEG_INFINITY, imin, "f neg inf");
+        assert_ftoi_unrep(<$fty>::NAN, 0, "f nan");
+        assert_ftoi_unrep(-<$fty>::NAN, 0, "f neg nan");
+    }};
+}
+
+/// Test casts from one float to another
+macro_rules! test_ftof {
+    (
+        f1: $f1:ty,
+        f2: $f2:ty $(,)?
+    ) => {{
+        type F2Int = <$f2 as Float>::Int;
+
+        let f1zero: $f1 = 0.0;
+        let f2zero: $f2 = 0.0;
+        let f1five: $f1 = 5.0;
+        let f2five: $f2 = 5.0;
+
+        assert_biteq((f1zero as $f2), f2zero, "0.0");
+        assert_biteq(((-f1zero) as $f2), (-f2zero), "-0.0");
+        assert_biteq((f1five as $f2), f2five, "5.0");
+        assert_biteq(((-f1five) as $f2), (-f2five), "-5.0");
+
+        assert_feq(<$f1>::INFINITY as $f2, <$f2>::INFINITY, "max -> inf");
+        assert_feq(<$f1>::NEG_INFINITY as $f2, <$f2>::NEG_INFINITY, "max -> inf");
+        assert!((<$f1>::NAN as $f2).is_nan(), "{} -> {} nan", stringify!($f1), stringify!($f2));
+
+        let min_sub_casted = <$f1>::from_bits(0x1) as $f2;
+        let min_neg_sub_casted = <$f1>::from_bits(0x1 | 1 << (<$f1>::BITS - 1)) as $f2;
+
+        if <$f1>::BITS > <$f2>::BITS {
+            assert_feq(<$f1>::MAX as $f2, <$f2>::INFINITY, "max -> inf");
+            assert_feq(<$f1>::MIN as $f2, <$f2>::NEG_INFINITY, "max -> inf");
+            assert_biteq(min_sub_casted, f2zero, "min subnormal -> 0.0");
+            assert_biteq(min_neg_sub_casted, -f2zero, "min neg subnormal -> -0.0");
+        } else {
+            // When increasing precision, the minimum subnormal will just roll to the next
+            // exponent. This exponent will be the current exponent (with bias), plus
+            // `sig_bits - 1` to account for the implicit change in exponent (since the
+            // mantissa starts with 0).
+            let sub_casted = <$f2>::from_bits(
+                ((<$f2>::EXPONENT_BIAS - (<$f1>::EXPONENT_BIAS + <$f1>::SIGNIFICAND_BITS - 1))
+                    as F2Int)
+                    << <$f2>::SIGNIFICAND_BITS,
+            );
+            assert_biteq(min_sub_casted, sub_casted, "min subnormal");
+            assert_biteq(min_neg_sub_casted, -sub_casted, "min neg subnormal");
+        }
+    }};
+}
+
+/// Many of these test patterns were adapted from the values in
 /// https://github.com/WebAssembly/testsuite/blob/master/conversions.wast.
 fn casts() {
-    // f32 -> i8
-    test_both_cast::<f32, i8>(127.99, 127);
-    test_both_cast::<f32, i8>(-128.99, -128);
-
-    // f32 -> i32
-    test_both_cast::<f32, i32>(0.0, 0);
-    test_both_cast::<f32, i32>(-0.0, 0);
-    test_both_cast::<f32, i32>(/*0x1p-149*/ f32::from_bits(0x00000001), 0);
-    test_both_cast::<f32, i32>(/*-0x1p-149*/ f32::from_bits(0x80000001), 0);
-    test_both_cast::<f32, i32>(/*0x1.19999ap+0*/ f32::from_bits(0x3f8ccccd), 1);
-    test_both_cast::<f32, i32>(/*-0x1.19999ap+0*/ f32::from_bits(0xbf8ccccd), -1);
-    test_both_cast::<f32, i32>(1.9, 1);
-    test_both_cast::<f32, i32>(-1.9, -1);
-    test_both_cast::<f32, i32>(5.0, 5);
-    test_both_cast::<f32, i32>(-5.0, -5);
-    test_both_cast::<f32, i32>(2147483520.0, 2147483520);
-    test_both_cast::<f32, i32>(-2147483648.0, -2147483648);
-    // unrepresentable casts
-    assert_eq::<i32>(2147483648.0f32 as i32, i32::MAX);
-    assert_eq::<i32>(-2147483904.0f32 as i32, i32::MIN);
-    assert_eq::<i32>(f32::MAX as i32, i32::MAX);
-    assert_eq::<i32>(f32::MIN as i32, i32::MIN);
-    assert_eq::<i32>(f32::INFINITY as i32, i32::MAX);
-    assert_eq::<i32>(f32::NEG_INFINITY as i32, i32::MIN);
-    assert_eq::<i32>(f32::NAN as i32, 0);
-    assert_eq::<i32>((-f32::NAN) as i32, 0);
-
-    // f32 -> u32
-    test_both_cast::<f32, u32>(0.0, 0);
-    test_both_cast::<f32, u32>(-0.0, 0);
-    test_both_cast::<f32, u32>(-0.9999999, 0);
-    test_both_cast::<f32, u32>(/*0x1p-149*/ f32::from_bits(0x1), 0);
-    test_both_cast::<f32, u32>(/*-0x1p-149*/ f32::from_bits(0x80000001), 0);
-    test_both_cast::<f32, u32>(/*0x1.19999ap+0*/ f32::from_bits(0x3f8ccccd), 1);
-    test_both_cast::<f32, u32>(1.9, 1);
-    test_both_cast::<f32, u32>(5.0, 5);
-    test_both_cast::<f32, u32>(2147483648.0, 0x8000_0000);
-    test_both_cast::<f32, u32>(4294967040.0, 0u32.wrapping_sub(256));
-    test_both_cast::<f32, u32>(/*-0x1.ccccccp-1*/ f32::from_bits(0xbf666666), 0);
-    test_both_cast::<f32, u32>(/*-0x1.fffffep-1*/ f32::from_bits(0xbf7fffff), 0);
-    test_both_cast::<f32, u32>((u32::MAX - 128) as f32, u32::MAX - 255); // rounding loss
-    // unrepresentable casts
-    assert_eq::<u32>((u32::MAX - 127) as f32 as u32, u32::MAX); // rounds up and then becomes unrepresentable
-    assert_eq::<u32>(4294967296.0f32 as u32, u32::MAX);
-    assert_eq::<u32>(-5.0f32 as u32, 0);
-    assert_eq::<u32>(f32::MAX as u32, u32::MAX);
-    assert_eq::<u32>(f32::MIN as u32, 0);
-    assert_eq::<u32>(f32::INFINITY as u32, u32::MAX);
-    assert_eq::<u32>(f32::NEG_INFINITY as u32, 0);
-    assert_eq::<u32>(f32::NAN as u32, 0);
-    assert_eq::<u32>((-f32::NAN) as u32, 0);
-
-    // f32 -> i64
-    test_both_cast::<f32, i64>(4294967296.0, 4294967296);
-    test_both_cast::<f32, i64>(-4294967296.0, -4294967296);
-    test_both_cast::<f32, i64>(9223371487098961920.0, 9223371487098961920);
-    test_both_cast::<f32, i64>(-9223372036854775808.0, -9223372036854775808);
-
-    // f64 -> i8
-    test_both_cast::<f64, i8>(127.99, 127);
-    test_both_cast::<f64, i8>(-128.99, -128);
-
-    // f64 -> i32
-    test_both_cast::<f64, i32>(0.0, 0);
-    test_both_cast::<f64, i32>(-0.0, 0);
-    test_both_cast::<f64, i32>(/*0x1.199999999999ap+0*/ f64::from_bits(0x3ff199999999999a), 1);
-    test_both_cast::<f64, i32>(
-        /*-0x1.199999999999ap+0*/ f64::from_bits(0xbff199999999999a),
-        -1,
-    );
-    test_both_cast::<f64, i32>(1.9, 1);
-    test_both_cast::<f64, i32>(-1.9, -1);
-    test_both_cast::<f64, i32>(1e8, 100_000_000);
-    test_both_cast::<f64, i32>(2147483647.0, 2147483647);
-    test_both_cast::<f64, i32>(-2147483648.0, -2147483648);
-    // unrepresentable casts
-    assert_eq::<i32>(2147483648.0f64 as i32, i32::MAX);
-    assert_eq::<i32>(-2147483649.0f64 as i32, i32::MIN);
-
-    // f64 -> i64
-    test_both_cast::<f64, i64>(0.0, 0);
-    test_both_cast::<f64, i64>(-0.0, 0);
-    test_both_cast::<f64, i64>(/*0x0.0000000000001p-1022*/ f64::from_bits(0x1), 0);
-    test_both_cast::<f64, i64>(
-        /*-0x0.0000000000001p-1022*/ f64::from_bits(0x8000000000000001),
-        0,
-    );
-    test_both_cast::<f64, i64>(/*0x1.199999999999ap+0*/ f64::from_bits(0x3ff199999999999a), 1);
-    test_both_cast::<f64, i64>(
-        /*-0x1.199999999999ap+0*/ f64::from_bits(0xbff199999999999a),
-        -1,
-    );
-    test_both_cast::<f64, i64>(5.0, 5);
-    test_both_cast::<f64, i64>(5.9, 5);
-    test_both_cast::<f64, i64>(-5.0, -5);
-    test_both_cast::<f64, i64>(-5.9, -5);
-    test_both_cast::<f64, i64>(4294967296.0, 4294967296);
-    test_both_cast::<f64, i64>(-4294967296.0, -4294967296);
-    test_both_cast::<f64, i64>(9223372036854774784.0, 9223372036854774784);
-    test_both_cast::<f64, i64>(-9223372036854775808.0, -9223372036854775808);
-    // unrepresentable casts
-    assert_eq::<i64>(9223372036854775808.0f64 as i64, i64::MAX);
-    assert_eq::<i64>(-9223372036854777856.0f64 as i64, i64::MIN);
-    assert_eq::<i64>(f64::MAX as i64, i64::MAX);
-    assert_eq::<i64>(f64::MIN as i64, i64::MIN);
-    assert_eq::<i64>(f64::INFINITY as i64, i64::MAX);
-    assert_eq::<i64>(f64::NEG_INFINITY as i64, i64::MIN);
-    assert_eq::<i64>(f64::NAN as i64, 0);
-    assert_eq::<i64>((-f64::NAN) as i64, 0);
-
-    // f64 -> u64
-    test_both_cast::<f64, u64>(0.0, 0);
-    test_both_cast::<f64, u64>(-0.0, 0);
-    test_both_cast::<f64, u64>(-0.99999999999, 0);
-    test_both_cast::<f64, u64>(5.0, 5);
-    test_both_cast::<f64, u64>(1e16, 10000000000000000);
-    test_both_cast::<f64, u64>((u64::MAX - 1024) as f64, u64::MAX - 2047); // rounding loss
-    test_both_cast::<f64, u64>(9223372036854775808.0, 9223372036854775808);
-    // unrepresentable casts
-    assert_eq::<u64>(-5.0f64 as u64, 0);
-    assert_eq::<u64>((u64::MAX - 1023) as f64 as u64, u64::MAX); // rounds up and then becomes unrepresentable
-    assert_eq::<u64>(18446744073709551616.0f64 as u64, u64::MAX);
-    assert_eq::<u64>(f64::MAX as u64, u64::MAX);
-    assert_eq::<u64>(f64::MIN as u64, 0);
-    assert_eq::<u64>(f64::INFINITY as u64, u64::MAX);
-    assert_eq::<u64>(f64::NEG_INFINITY as u64, 0);
-    assert_eq::<u64>(f64::NAN as u64, 0);
-    assert_eq::<u64>((-f64::NAN) as u64, 0);
-
-    // f64 -> i128
-    assert_eq::<i128>(f64::MAX as i128, i128::MAX);
-    assert_eq::<i128>(f64::MIN as i128, i128::MIN);
-
-    // f64 -> u128
-    assert_eq::<u128>(f64::MAX as u128, u128::MAX);
-    assert_eq::<u128>(f64::MIN as u128, 0);
+    /* int <-> float generic tests */
+
+    test_ftoi_itof! { f: f16, i: i8, imin_f: -128.0, imax_f: 127.0 };
+    test_ftoi_itof! { f: f16, i: u8, imin_f: 0.0, imax_f: 255.0 };
+    test_ftoi_itof! { f: f16, i: i16, imin_f: -32_768.0, imax_f: 32_767.0 };
+    test_ftoi_itof! { f: f16, i: u16, imin_f: 0.0, imax_f: 65_535.0 };
+    test_ftoi_itof! { f: f16, i: i32, imin_f: -2_147_483_648.0, imax_f: 2_147_483_647.0 };
+    test_ftoi_itof! { f: f16, i: u32, imin_f: 0.0, imax_f: 4_294_967_295.0 };
+    test_ftoi_itof! {
+        f: f16,
+        i: i64,
+        imin_f: -9_223_372_036_854_775_808.0,
+        imax_f: 9_223_372_036_854_775_807.0
+    };
+    test_ftoi_itof! { f: f16, i: u64, imin_f: 0.0, imax_f: 18_446_744_073_709_551_615.0 };
+    test_ftoi_itof! {
+        f: f16,
+        i: i128,
+        imin_f: -170_141_183_460_469_231_731_687_303_715_884_105_728.0,
+        imax_f: 170_141_183_460_469_231_731_687_303_715_884_105_727.0,
+    };
+    test_ftoi_itof! {
+        f: f16,
+        i: u128,
+        imin_f: 0.0,
+        imax_f: 340_282_366_920_938_463_463_374_607_431_768_211_455.0
+    };
+
+    test_ftoi_itof! { f: f32, i: i8, imin_f: -128.0, imax_f: 127.0 };
+    test_ftoi_itof! { f: f32, i: u8, imin_f: 0.0, imax_f: 255.0 };
+    test_ftoi_itof! { f: f32, i: i16, imin_f: -32_768.0, imax_f: 32_767.0 };
+    test_ftoi_itof! { f: f32, i: u16, imin_f: 0.0, imax_f: 65_535.0 };
+    test_ftoi_itof! { f: f32, i: i32, imin_f: -2_147_483_648.0, imax_f: 2_147_483_647.0 };
+    test_ftoi_itof! { f: f32, i: u32, imin_f: 0.0, imax_f: 4_294_967_295.0 };
+    test_ftoi_itof! {
+        f: f32,
+        i: i64,
+        imin_f: -9_223_372_036_854_775_808.0,
+        imax_f: 9_223_372_036_854_775_807.0
+    };
+    test_ftoi_itof! { f: f32, i: u64, imin_f: 0.0, imax_f: 18_446_744_073_709_551_615.0 };
+    test_ftoi_itof! {
+        f: f32,
+        i: i128,
+        imin_f: -170_141_183_460_469_231_731_687_303_715_884_105_728.0,
+        imax_f: 170_141_183_460_469_231_731_687_303_715_884_105_727.0,
+    };
+    test_ftoi_itof! {
+        f: f32,
+        i: u128,
+        imin_f: 0.0,
+        imax_f: 340_282_366_920_938_463_463_374_607_431_768_211_455.0
+    };
+
+    test_ftoi_itof! { f: f64, i: i8, imin_f: -128.0, imax_f: 127.0 };
+    test_ftoi_itof! { f: f64, i: u8, imin_f: 0.0, imax_f: 255.0 };
+    test_ftoi_itof! { f: f64, i: i16, imin_f: -32_768.0, imax_f: 32_767.0 };
+    test_ftoi_itof! { f: f64, i: u16, imin_f: 0.0, imax_f: 65_535.0 };
+    test_ftoi_itof! { f: f64, i: i32, imin_f: -2_147_483_648.0, imax_f: 2_147_483_647.0 };
+    test_ftoi_itof! { f: f64, i: u32, imin_f: 0.0, imax_f: 4_294_967_295.0 };
+    test_ftoi_itof! {
+        f: f64,
+        i: i64,
+        imin_f: -9_223_372_036_854_775_808.0,
+        imax_f: 9_223_372_036_854_775_807.0
+    };
+    test_ftoi_itof! { f: f64, i: u64, imin_f: 0.0, imax_f: 18_446_744_073_709_551_615.0 };
+    test_ftoi_itof! {
+        f: f64,
+        i: i128,
+        imin_f: -170_141_183_460_469_231_731_687_303_715_884_105_728.0,
+        imax_f: 170_141_183_460_469_231_731_687_303_715_884_105_727.0,
+    };
+    test_ftoi_itof! {
+        f: f64,
+        i: u128,
+        imin_f: 0.0,
+        imax_f: 340_282_366_920_938_463_463_374_607_431_768_211_455.0
+    };
+
+    test_ftoi_itof! { f: f128, i: i8, imin_f: -128.0, imax_f: 127.0 };
+    test_ftoi_itof! { f: f128, i: u8, imin_f: 0.0, imax_f: 255.0 };
+    test_ftoi_itof! { f: f128, i: i16, imin_f: -32_768.0, imax_f: 32_767.0 };
+    test_ftoi_itof! { f: f128, i: u16, imin_f: 0.0, imax_f: 65_535.0 };
+    test_ftoi_itof! { f: f128, i: i32, imin_f: -2_147_483_648.0, imax_f: 2_147_483_647.0 };
+    test_ftoi_itof! { f: f128, i: u32, imin_f: 0.0, imax_f: 4_294_967_295.0 };
+    test_ftoi_itof! {
+        f: f128,
+        i: i64,
+        imin_f: -9_223_372_036_854_775_808.0,
+        imax_f: 9_223_372_036_854_775_807.0
+    };
+    test_ftoi_itof! { f: f128, i: u64, imin_f: 0.0, imax_f: 18_446_744_073_709_551_615.0 };
+    test_ftoi_itof! {
+        f: f128,
+        i: i128,
+        imin_f: -170_141_183_460_469_231_731_687_303_715_884_105_728.0,
+        imax_f: 170_141_183_460_469_231_731_687_303_715_884_105_727.0,
+    };
+    test_ftoi_itof! {
+        f: f128,
+        i: u128,
+        imin_f: 0.0,
+        imax_f: 340_282_366_920_938_463_463_374_607_431_768_211_455.0
+    };
+
+    /* int <-> float spot checks */
 
     // int -> f32
-    assert_eq::<f32>(127i8 as f32, 127.0);
-    assert_eq::<f32>(2147483647i32 as f32, 2147483648.0);
-    assert_eq::<f32>((-2147483648i32) as f32, -2147483648.0);
     assert_eq::<f32>(1234567890i32 as f32, /*0x1.26580cp+30*/ f32::from_bits(0x4e932c06));
-    assert_eq::<f32>(16777217i32 as f32, 16777216.0);
-    assert_eq::<f32>((-16777217i32) as f32, -16777216.0);
-    assert_eq::<f32>(16777219i32 as f32, 16777220.0);
-    assert_eq::<f32>((-16777219i32) as f32, -16777220.0);
     assert_eq::<f32>(
         0x7fffff4000000001i64 as f32,
         /*0x1.fffffep+62*/ f32::from_bits(0x5effffff),
@@ -313,36 +597,33 @@ fn casts() {
         0xffdfffffdfffffffu64 as i64 as f32,
         /*-0x1.000002p+53*/ f32::from_bits(0xda000001),
     );
-    assert_eq::<f32>(i128::MIN as f32, -170141183460469231731687303715884105728.0f32);
-    assert_eq::<f32>(u128::MAX as f32, f32::INFINITY); // saturation
 
     // int -> f64
-    assert_eq::<f64>(127i8 as f64, 127.0);
-    assert_eq::<f64>(i16::MIN as f64, -32768.0f64);
-    assert_eq::<f64>(2147483647i32 as f64, 2147483647.0);
-    assert_eq::<f64>(-2147483648i32 as f64, -2147483648.0);
     assert_eq::<f64>(987654321i32 as f64, 987654321.0);
-    assert_eq::<f64>(9223372036854775807i64 as f64, 9223372036854775807.0);
-    assert_eq::<f64>(-9223372036854775808i64 as f64, -9223372036854775808.0);
     assert_eq::<f64>(4669201609102990i64 as f64, 4669201609102990.0); // Feigenbaum (?)
     assert_eq::<f64>(9007199254740993i64 as f64, 9007199254740992.0);
     assert_eq::<f64>(-9007199254740993i64 as f64, -9007199254740992.0);
     assert_eq::<f64>(9007199254740995i64 as f64, 9007199254740996.0);
     assert_eq::<f64>(-9007199254740995i64 as f64, -9007199254740996.0);
-    assert_eq::<f64>(u128::MAX as f64, 340282366920938463463374607431768211455.0f64); // even that fits...
+
+    /* float -> float generic tests */
+
+    test_ftof! { f1: f16, f2: f32 };
+    test_ftof! { f1: f16, f2: f64 };
+    test_ftof! { f1: f16, f2: f128 };
+    test_ftof! { f1: f32, f2: f16 };
+    test_ftof! { f1: f32, f2: f64 };
+    test_ftof! { f1: f32, f2: f128 };
+    test_ftof! { f1: f64, f2: f16 };
+    test_ftof! { f1: f64, f2: f32 };
+    test_ftof! { f1: f64, f2: f128 };
+    test_ftof! { f1: f128, f2: f16 };
+    test_ftof! { f1: f128, f2: f32 };
+    test_ftof! { f1: f128, f2: f64 };
+
+    /* float -> float spot checks */
 
     // f32 -> f64
-    assert_eq::<u64>((0.0f32 as f64).to_bits(), 0.0f64.to_bits());
-    assert_eq::<u64>(((-0.0f32) as f64).to_bits(), (-0.0f64).to_bits());
-    assert_eq::<f64>(5.0f32 as f64, 5.0f64);
-    assert_eq::<f64>(
-        /*0x1p-149*/ f32::from_bits(0x1) as f64,
-        /*0x1p-149*/ f64::from_bits(0x36a0000000000000),
-    );
-    assert_eq::<f64>(
-        /*-0x1p-149*/ f32::from_bits(0x80000001) as f64,
-        /*-0x1p-149*/ f64::from_bits(0xb6a0000000000000),
-    );
     assert_eq::<f64>(
         /*0x1.fffffep+127*/ f32::from_bits(0x7f7fffff) as f64,
         /*0x1.fffffep+127*/ f64::from_bits(0x47efffffe0000000),
@@ -359,15 +640,8 @@ fn casts() {
         /*0x1.8f867ep+125*/ f32::from_bits(0x7e47c33f) as f64,
         6.6382536710104395e+37,
     );
-    assert_eq::<f64>(f32::INFINITY as f64, f64::INFINITY);
-    assert_eq::<f64>(f32::NEG_INFINITY as f64, f64::NEG_INFINITY);
 
     // f64 -> f32
-    assert_eq::<u32>((0.0f64 as f32).to_bits(), 0.0f32.to_bits());
-    assert_eq::<u32>(((-0.0f64) as f32).to_bits(), (-0.0f32).to_bits());
-    assert_eq::<f32>(5.0f64 as f32, 5.0f32);
-    assert_eq::<f32>(/*0x0.0000000000001p-1022*/ f64::from_bits(0x1) as f32, 0.0);
-    assert_eq::<f32>(/*-0x0.0000000000001p-1022*/ (-f64::from_bits(0x1)) as f32, -0.0);
     assert_eq::<f32>(
         /*0x1.fffffe0000000p-127*/ f64::from_bits(0x380fffffe0000000) as f32,
         /*0x1p-149*/ f32::from_bits(0x800000),
@@ -376,10 +650,6 @@ fn casts() {
         /*0x1.4eae4f7024c7p+108*/ f64::from_bits(0x46b4eae4f7024c70) as f32,
         /*0x1.4eae5p+108*/ f32::from_bits(0x75a75728),
     );
-    assert_eq::<f32>(f64::MAX as f32, f32::INFINITY);
-    assert_eq::<f32>(f64::MIN as f32, f32::NEG_INFINITY);
-    assert_eq::<f32>(f64::INFINITY as f32, f32::INFINITY);
-    assert_eq::<f32>(f64::NEG_INFINITY as f32, f32::NEG_INFINITY);
 }
 
 fn ops() {
diff --git a/src/tools/miri/tests/pass/tls/macos_tlv_atexit.rs b/src/tools/miri/tests/pass/tls/macos_tlv_atexit.rs
new file mode 100644
index 00000000000..845f50c1eba
--- /dev/null
+++ b/src/tools/miri/tests/pass/tls/macos_tlv_atexit.rs
@@ -0,0 +1,43 @@
+//@only-target-darwin
+
+use std::thread;
+
+extern "C" {
+    fn _tlv_atexit(dtor: unsafe extern "C" fn(*mut u8), arg: *mut u8);
+}
+
+fn register<F>(f: F)
+where
+    F: FnOnce() + 'static,
+{
+    // This will receive the pointer passed into `_tlv_atexit`, which is the
+    // original `f` but boxed up.
+    unsafe extern "C" fn run<F>(ptr: *mut u8)
+    where
+        F: FnOnce() + 'static,
+    {
+        let f = unsafe { Box::from_raw(ptr as *mut F) };
+        f()
+    }
+
+    unsafe {
+        _tlv_atexit(run::<F>, Box::into_raw(Box::new(f)) as *mut u8);
+    }
+}
+
+fn main() {
+    thread::spawn(|| {
+        register(|| println!("dtor 2"));
+        register(|| println!("dtor 1"));
+        println!("exiting thread");
+    })
+    .join()
+    .unwrap();
+
+    println!("exiting main");
+    register(|| println!("dtor 5"));
+    register(|| {
+        println!("registering dtor in dtor 3");
+        register(|| println!("dtor 4"));
+    });
+}
diff --git a/src/tools/miri/tests/pass/tls/macos_tlv_atexit.stdout b/src/tools/miri/tests/pass/tls/macos_tlv_atexit.stdout
new file mode 100644
index 00000000000..89d6ca25935
--- /dev/null
+++ b/src/tools/miri/tests/pass/tls/macos_tlv_atexit.stdout
@@ -0,0 +1,7 @@
+exiting thread
+dtor 1
+dtor 2
+exiting main
+registering dtor in dtor 3
+dtor 4
+dtor 5
diff --git a/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs b/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs
index 2d142bef73c..a3356b682a6 100644
--- a/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs
+++ b/src/tools/miri/tests/pass/zero-sized-accesses-and-offsets.rs
@@ -39,8 +39,6 @@ fn test_ptr(ptr: *mut ()) {
         // Distance.
         let ptr = ptr.cast::<i32>();
         ptr.offset_from(ptr);
-        /*
-        FIXME: this is disabled for now as these cases are not yet allowed.
         // Distance from other "bad" pointers that have the same address, but different provenance. Some
         // of this is library UB, but we don't want it to be language UB since that would violate
         // provenance monotonicity: if we allow computing the distance between two ptrs with no
@@ -54,6 +52,5 @@ fn test_ptr(ptr: *mut ()) {
         // - Distance from use-after-free pointer
         drop(b);
         ptr.offset_from(other_ptr.with_addr(ptr.addr()));
-        */
     }
 }
diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml
index ec3b8a96ef3..969552dec84 100644
--- a/src/tools/run-make-support/Cargo.toml
+++ b/src/tools/run-make-support/Cargo.toml
@@ -11,3 +11,5 @@ wasmparser = "0.118.2"
 regex = "1.8" # 1.8 to avoid memchr 2.6.0, as 2.5.0 is pinned in the workspace
 gimli = "0.28.1"
 ar = "0.9.0"
+
+build_helper = { path = "../build_helper" }
diff --git a/src/tools/run-make-support/src/command.rs b/src/tools/run-make-support/src/command.rs
index c506c3d6b61..5017a4b88da 100644
--- a/src/tools/run-make-support/src/command.rs
+++ b/src/tools/run-make-support/src/command.rs
@@ -5,8 +5,8 @@ use std::panic;
 use std::path::Path;
 use std::process::{Command as StdCommand, ExitStatus, Output, Stdio};
 
-use crate::drop_bomb::DropBomb;
 use crate::{assert_contains, assert_equals, assert_not_contains, handle_failed_output};
+use build_helper::drop_bomb::DropBomb;
 
 /// This is a custom command wrapper that simplifies working with commands and makes it easier to
 /// ensure that we check the exit status of executed processes.
diff --git a/src/tools/run-make-support/src/diff/mod.rs b/src/tools/run-make-support/src/diff/mod.rs
index 24fa88af82e..ad989b74e4d 100644
--- a/src/tools/run-make-support/src/diff/mod.rs
+++ b/src/tools/run-make-support/src/diff/mod.rs
@@ -2,8 +2,8 @@ use regex::Regex;
 use similar::TextDiff;
 use std::path::{Path, PathBuf};
 
-use crate::drop_bomb::DropBomb;
 use crate::fs_wrapper;
+use build_helper::drop_bomb::DropBomb;
 
 #[cfg(test)]
 mod tests;
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index 5655318267a..e5f1ce1bf34 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -7,7 +7,6 @@ pub mod cc;
 pub mod clang;
 mod command;
 pub mod diff;
-mod drop_bomb;
 pub mod fs_wrapper;
 pub mod llvm;
 pub mod run;
@@ -303,6 +302,11 @@ pub fn filename_not_in_denylist<P: AsRef<Path>, V: AsRef<[String]>>(path: P, exp
         .is_some_and(|name| !expected.contains(&name.to_str().unwrap().to_owned()))
 }
 
+/// Returns true if the filename at `path` ends with `suffix`.
+pub fn has_suffix<P: AsRef<Path>>(path: P, suffix: &str) -> bool {
+    path.as_ref().file_name().is_some_and(|name| name.to_str().unwrap().ends_with(suffix))
+}
+
 /// Gathers all files in the current working directory that have the extension `ext`, and counts
 /// the number of lines within that contain a match with the regex pattern `re`.
 pub fn count_regex_matches_in_files_with_extension(re: &regex::Regex, ext: &str) -> usize {
diff --git a/src/tools/run-make-support/src/rustc.rs b/src/tools/run-make-support/src/rustc.rs
index a2a7c8064dc..ae200d51431 100644
--- a/src/tools/run-make-support/src/rustc.rs
+++ b/src/tools/run-make-support/src/rustc.rs
@@ -4,13 +4,15 @@ use std::path::Path;
 
 use crate::{command, cwd, env_var, set_host_rpath};
 
-/// Construct a new `rustc` invocation.
+/// Construct a new `rustc` invocation. This will automatically set the library
+/// search path as `-L cwd()`. Use [`bare_rustc`] to avoid this.
 #[track_caller]
 pub fn rustc() -> Rustc {
     Rustc::new()
 }
 
-/// Construct a plain `rustc` invocation with no flags set.
+/// Construct a plain `rustc` invocation with no flags set. Note that [`set_host_rpath`]
+/// still presets the environment variable `HOST_RPATH_DIR` by default.
 #[track_caller]
 pub fn bare_rustc() -> Rustc {
     Rustc::bare()
@@ -42,7 +44,8 @@ fn setup_common() -> Command {
 impl Rustc {
     // `rustc` invocation constructor methods
 
-    /// Construct a new `rustc` invocation.
+    /// Construct a new `rustc` invocation. This will automatically set the library
+    /// search path as `-L cwd()`. Use [`bare_rustc`] to avoid this.
     #[track_caller]
     pub fn new() -> Self {
         let mut cmd = setup_common();
diff --git a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
index ff623440123..7bf8af4caba 100644
--- a/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
+++ b/src/tools/rust-analyzer/crates/hir-ty/src/diagnostics/match_check/pat_analysis.rs
@@ -5,11 +5,10 @@ use std::fmt;
 use hir_def::{DefWithBodyId, EnumId, EnumVariantId, HasModule, LocalFieldId, ModuleId, VariantId};
 use intern::sym;
 use once_cell::unsync::Lazy;
-use rustc_index::IndexVec;
 use rustc_pattern_analysis::{
     constructor::{Constructor, ConstructorSet, VariantVisibility},
     usefulness::{compute_match_usefulness, PlaceValidity, UsefulnessReport},
-    Captures, PatCx, PrivateUninhabitedField,
+    Captures, IndexVec, PatCx, PrivateUninhabitedField,
 };
 use smallvec::{smallvec, SmallVec};
 use stdx::never;
@@ -54,7 +53,7 @@ impl EnumVariantContiguousIndex {
     }
 }
 
-impl rustc_index::Idx for EnumVariantContiguousIndex {
+impl rustc_pattern_analysis::Idx for EnumVariantContiguousIndex {
     fn new(idx: usize) -> Self {
         EnumVariantContiguousIndex(idx)
     }
diff --git a/src/tools/rust-analyzer/crates/rust-analyzer/src/version.rs b/src/tools/rust-analyzer/crates/rust-analyzer/src/version.rs
index 1e829299e6f..2ff967416c0 100644
--- a/src/tools/rust-analyzer/crates/rust-analyzer/src/version.rs
+++ b/src/tools/rust-analyzer/crates/rust-analyzer/src/version.rs
@@ -15,7 +15,7 @@ pub struct VersionInfo {
     pub version: &'static str,
     /// The release channel we were built for (stable/beta/nightly/dev).
     ///
-    /// `None` if not built via rustbuild.
+    /// `None` if not built via bootstrap.
     pub release_channel: Option<&'static str>,
     /// Information about the Git repository we may have been built from.
     ///
diff --git a/src/tools/rust-analyzer/rust-version b/src/tools/rust-analyzer/rust-version
index 424c93a7521..18e32e5c65b 100644
--- a/src/tools/rust-analyzer/rust-version
+++ b/src/tools/rust-analyzer/rust-version
@@ -1 +1 @@
-bcf1f6db4594ae6132378b179a30cdb3599a863d
+a91f7d72f12efcc00ecf71591f066c534d45ddf7
diff --git a/src/tools/rust-installer/src/combiner.rs b/src/tools/rust-installer/src/combiner.rs
index c211b34850a..e7020980dc3 100644
--- a/src/tools/rust-installer/src/combiner.rs
+++ b/src/tools/rust-installer/src/combiner.rs
@@ -109,7 +109,7 @@ impl Combiner {
                 .with_context(|| format!("failed to read components in '{}'", input_tarball))?;
             for component in pkg_components.split_whitespace() {
                 // All we need to do is copy the component directory. We could
-                // move it, but rustbuild wants to reuse the unpacked package
+                // move it, but bootstrap wants to reuse the unpacked package
                 // dir for OS-specific installers on macOS and Windows.
                 let component_dir = package_dir.join(component);
                 create_dir(&component_dir)?;
diff --git a/src/tools/rust-installer/test.sh b/src/tools/rust-installer/test.sh
index 16b05c66197..7b3e7456006 100755
--- a/src/tools/rust-installer/test.sh
+++ b/src/tools/rust-installer/test.sh
@@ -974,7 +974,7 @@ combined_remains() {
     --package-name=rust \
     --input-tarballs="$OUT_DIR/rustc.tar.gz,$OUT_DIR/cargo.tar.gz,$OUT_DIR/rust-docs.tar.gz"
     for component in rustc cargo rust-docs; do
-    # rustbuild wants the original extracted package intact too
+    # bootstrap wants the original extracted package intact too
     try test -d "$WORK_DIR/$component/$component"
     try test -d "$WORK_DIR/rust/$component"
     done
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index 33120cf93f9..f7ec7d0b3f6 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -23,7 +23,6 @@ run-make/dep-info/Makefile
 run-make/dump-ice-to-disk/Makefile
 run-make/dump-mono-stats/Makefile
 run-make/emit-to-stdout/Makefile
-run-make/env-dep-info/Makefile
 run-make/export-executable-symbols/Makefile
 run-make/extern-diff-internal-name/Makefile
 run-make/extern-flag-disambiguates/Makefile
@@ -37,7 +36,6 @@ run-make/extern-fn-with-packed-struct/Makefile
 run-make/extern-fn-with-union/Makefile
 run-make/extern-multiple-copies/Makefile
 run-make/extern-multiple-copies2/Makefile
-run-make/extra-filename-with-temp-outputs/Makefile
 run-make/fmt-write-bloat/Makefile
 run-make/foreign-double-unwind/Makefile
 run-make/foreign-exceptions/Makefile
@@ -58,9 +56,7 @@ run-make/issue-35164/Makefile
 run-make/issue-36710/Makefile
 run-make/issue-47551/Makefile
 run-make/issue-69368/Makefile
-run-make/issue-83045/Makefile
 run-make/issue-84395-lto-embed-bitcode/Makefile
-run-make/issue-85019-moved-src-dir/Makefile
 run-make/issue-85401-static-mir/Makefile
 run-make/issue-88756-default-output/Makefile
 run-make/issue-97463-abi-param-passing/Makefile
@@ -116,7 +112,6 @@ run-make/return-non-c-like-enum-from-c/Makefile
 run-make/rlib-format-packed-bundled-libs-2/Makefile
 run-make/rlib-format-packed-bundled-libs-3/Makefile
 run-make/rlib-format-packed-bundled-libs/Makefile
-run-make/rustc-macro-dep-files/Makefile
 run-make/sanitizer-cdylib-link/Makefile
 run-make/sanitizer-dylib-link/Makefile
 run-make/sanitizer-staticlib-link/Makefile
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index 82fa43f581f..3c72fae0881 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -353,6 +353,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "rustc-hash",
     "rustc-rayon",
     "rustc-rayon-core",
+    "rustc-stable-hash",
     "rustc_apfloat",
     "rustc_version",
     "rustix",