about summary refs log tree commit diff
path: root/src/tools
diff options
context:
space:
mode:
authorLaurențiu Nicola <lnicola@dend.ro>2024-11-28 08:37:36 +0200
committerLaurențiu Nicola <lnicola@dend.ro>2024-11-28 08:37:36 +0200
commitc91f2a328018916e64db56905cd0edc27c673d33 (patch)
tree334b4418e944ecfcb12eb158ebed6d144de7fc56 /src/tools
parent6e3cb4abfbbdb6fb7e34eed99840d1fe22c6b682 (diff)
parentf005c7437def424a1c43cbc380352a58d8ac920b (diff)
downloadrust-c91f2a328018916e64db56905cd0edc27c673d33.tar.gz
rust-c91f2a328018916e64db56905cd0edc27c673d33.zip
Merge from rust-lang/rust
Diffstat (limited to 'src/tools')
-rw-r--r--src/tools/build-manifest/src/main.rs1
-rw-r--r--src/tools/build_helper/Cargo.toml10
-rw-r--r--src/tools/build_helper/README.md1
-rw-r--r--src/tools/build_helper/src/ci.rs95
-rw-r--r--src/tools/build_helper/src/drop_bomb/mod.rs54
-rw-r--r--src/tools/build_helper/src/drop_bomb/tests.rs15
-rw-r--r--src/tools/build_helper/src/git.rs208
-rw-r--r--src/tools/build_helper/src/lib.rs31
-rw-r--r--src/tools/build_helper/src/metrics.rs92
-rw-r--r--src/tools/build_helper/src/stage0_parser.rs76
-rw-r--r--src/tools/build_helper/src/util.rs75
-rw-r--r--src/tools/bump-stage0/Cargo.toml2
m---------src/tools/cargo0
-rw-r--r--src/tools/clippy/.github/workflows/clippy_dev.yml50
-rw-r--r--src/tools/clippy/.github/workflows/clippy_mq.yml (renamed from src/tools/clippy/.github/workflows/clippy_bors.yml)51
-rw-r--r--src/tools/clippy/.github/workflows/clippy_pr.yml (renamed from src/tools/clippy/.github/workflows/clippy.yml)40
-rw-r--r--src/tools/clippy/.github/workflows/deploy.yml2
-rw-r--r--src/tools/clippy/.github/workflows/remark.yml46
-rw-r--r--src/tools/clippy/CHANGELOG.md8
-rw-r--r--src/tools/clippy/CONTRIBUTING.md16
-rw-r--r--src/tools/clippy/Cargo.toml2
-rw-r--r--src/tools/clippy/README.md3
-rw-r--r--src/tools/clippy/book/src/README.md3
-rw-r--r--src/tools/clippy/book/src/development/README.md1
-rw-r--r--src/tools/clippy/book/src/development/common_tools_writing_lints.md4
-rw-r--r--src/tools/clippy/book/src/development/method_checking.md15
-rw-r--r--src/tools/clippy/book/src/lint_configuration.md33
-rw-r--r--src/tools/clippy/clippy_config/src/conf.rs46
-rw-r--r--src/tools/clippy/clippy_config/src/lib.rs1
-rw-r--r--src/tools/clippy/clippy_config/src/msrvs.rs13
-rw-r--r--src/tools/clippy/clippy_config/src/types.rs301
-rw-r--r--src/tools/clippy/clippy_dev/src/serve.rs8
-rw-r--r--src/tools/clippy/clippy_dev/src/setup/vscode.rs2
-rw-r--r--src/tools/clippy/clippy_dev/src/update_lints.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs531
-rw-r--r--src/tools/clippy/clippy_lints/src/assigning_clones.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/mod.rs128
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs11
-rw-r--r--src/tools/clippy/clippy_lints/src/attrs/utils.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/await_holding_invalid.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/booleans.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/box_default.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/ptr_as_ptr.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/checked_conversions.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/cognitive_complexity.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/comparison_chain.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/copies.rs32
-rw-r--r--src/tools/clippy/clippy_lints/src/copy_iterator.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/ctfe.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs49
-rw-r--r--src/tools/clippy/clippy_lints/src/declared_lints.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/dereference.rs28
-rw-r--r--src/tools/clippy/clippy_lints/src/derivable_impls.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/derive.rs58
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_macros.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/missing_headers.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/doc/mod.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/drop_forget_ref.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/empty_drop.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/eta_reduction.rs20
-rw-r--r--src/tools/clippy/clippy_lints/src/explicit_write.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/from_over_into.rs29
-rw-r--r--src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/must_use.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/functions/ref_option.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/implicit_hasher.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/infinite_iter.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/item_name_repetitions.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/iter_not_returning_iterator.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/iter_without_into_iter.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/large_const_arrays.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/large_futures.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/large_include_file.rs65
-rw-r--r--src/tools/clippy/clippy_lints/src/large_stack_frames.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/len_zero.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/let_if_seq.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/lib.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/lifetimes.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs59
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/manual_find.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/same_item_push.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/utils.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs15
-rw-r--r--src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs60
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_async_fn.rs23
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_bits.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_clamp.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_hash_one.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/manual_strip.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/manual_utils.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs30
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs12
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs17
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/single_match.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/matches/try_err.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_map.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/filter_next.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/is_empty.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_next_back.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs43
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_clone.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs134
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/mod.rs166
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs28
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/needless_collect.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/str_splitn.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs33
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs131
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs7
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/unnecessary_to_owned.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/useless_asref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/methods/zst_offset.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/minmax.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/misc_early/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_doc.rs25
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/missing_inline.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/mut_mut.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs18
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_continue.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_late_init.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_ref_mut.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/neg_multiply.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/no_effect.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs13
-rw-r--r--src/tools/clippy/clippy_lints/src/non_copy_const.rs22
-rw-r--r--src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/erasing_op.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/float_cmp.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/identity_op.rs190
-rw-r--r--src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr.rs5
-rw-r--r--src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/question_mark.rs180
-rw-r--r--src/tools/clippy/clippy_lints/src/redundant_slicing.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/ref_option_ref.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/returns.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/semicolon_block.rs27
-rw-r--r--src/tools/clippy/clippy_lints/src/serde_api.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/single_component_path_imports.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/strings.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs28
-rw-r--r--src/tools/clippy/clippy_lints/src/swap.rs6
-rw-r--r--src/tools/clippy/clippy_lints/src/trailing_empty_array.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/trait_bounds.rs26
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs8
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs10
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/transmute/utils.rs9
-rw-r--r--src/tools/clippy/clippy_lints/src/types/borrowed_box.rs3
-rw-r--r--src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/types/vec_box.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/uninhabited_references.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs4
-rw-r--r--src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/unused_io_amount.rs28
-rw-r--r--src/tools/clippy/clippy_lints/src/unwrap.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/attr_collector.rs40
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/author.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/collapsible_calls.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs14
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs69
-rw-r--r--src/tools/clippy/clippy_lints/src/utils/mod.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/vec_init_then_push.rs2
-rw-r--r--src/tools/clippy/clippy_lints/src/write.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/ast_utils.rs4
-rw-r--r--src/tools/clippy/clippy_utils/src/check_proc_macro.rs37
-rw-r--r--src/tools/clippy/clippy_utils/src/consts.rs18
-rw-r--r--src/tools/clippy/clippy_utils/src/diagnostics.rs62
-rw-r--r--src/tools/clippy/clippy_utils/src/eager_or_lazy.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/higher.rs8
-rw-r--r--src/tools/clippy/clippy_utils/src/hir_utils.rs29
-rw-r--r--src/tools/clippy/clippy_utils/src/lib.rs125
-rw-r--r--src/tools/clippy/clippy_utils/src/numeric_literal.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs28
-rw-r--r--src/tools/clippy/clippy_utils/src/sugg.rs2
-rw-r--r--src/tools/clippy/clippy_utils/src/ty.rs73
-rw-r--r--src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs6
-rw-r--r--src/tools/clippy/clippy_utils/src/usage.rs20
-rw-r--r--src/tools/clippy/clippy_utils/src/visitors.rs93
-rw-r--r--src/tools/clippy/rust-toolchain2
-rw-r--r--src/tools/clippy/tests/compile-test.rs3
-rw-r--r--src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs5
-rw-r--r--src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.fixed24
-rw-r--r--src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.rs24
-rw-r--r--src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.stderr23
-rw-r--r--src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed1
-rw-r--r--src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs1
-rw-r--r--src/tools/clippy/tests/ui-internal/unnecessary_def_path.stderr30
-rw-r--r--src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed1
-rw-r--r--src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs1
-rw-r--r--src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr10
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/default/clippy.toml0
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml12
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_enum/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_impl/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_trait/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_1.stderr8
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_2.stderr8
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_3.stderr8
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs186
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good_var_1.rs174
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr226
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs185
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs174
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.var_1.stderr100
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.only_enum.stderr16
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.rs43
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.only_impl.stderr40
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs67
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.only_trait.stderr40
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs48
-rw-r--r--src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/var_1/clippy.toml1
-rw-r--r--src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr62
-rw-r--r--src/tools/clippy/tests/ui-toml/large_include_file/empty.txt0
-rw-r--r--src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs11
-rw-r--r--src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr12
-rw-r--r--src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.rs6
-rw-r--r--src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr9
-rw-r--r--src/tools/clippy/tests/ui/allow_attributes.fixed7
-rw-r--r--src/tools/clippy/tests/ui/allow_attributes.rs7
-rw-r--r--src/tools/clippy/tests/ui/allow_attributes.stderr16
-rw-r--r--src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr11
-rw-r--r--src/tools/clippy/tests/ui/attrs.stderr18
-rw-r--r--src/tools/clippy/tests/ui/author.rs (renamed from src/tools/clippy/tests/ui-internal/author.rs)2
-rw-r--r--src/tools/clippy/tests/ui/author.stdout (renamed from src/tools/clippy/tests/ui-internal/author.stdout)0
-rw-r--r--src/tools/clippy/tests/ui/author/blocks.rs (renamed from src/tools/clippy/tests/ui-internal/author/blocks.rs)0
-rw-r--r--src/tools/clippy/tests/ui/author/blocks.stdout (renamed from src/tools/clippy/tests/ui-internal/author/blocks.stdout)0
-rw-r--r--src/tools/clippy/tests/ui/author/call.rs (renamed from src/tools/clippy/tests/ui-internal/author/call.rs)0
-rw-r--r--src/tools/clippy/tests/ui/author/call.stdout (renamed from src/tools/clippy/tests/ui-internal/author/call.stdout)0
-rw-r--r--src/tools/clippy/tests/ui/author/if.rs (renamed from src/tools/clippy/tests/ui-internal/author/if.rs)0
-rw-r--r--src/tools/clippy/tests/ui/author/if.stdout (renamed from src/tools/clippy/tests/ui-internal/author/if.stdout)0
-rw-r--r--src/tools/clippy/tests/ui/author/issue_3849.rs (renamed from src/tools/clippy/tests/ui-internal/author/issue_3849.rs)0
-rw-r--r--src/tools/clippy/tests/ui/author/issue_3849.stdout (renamed from src/tools/clippy/tests/ui-internal/author/issue_3849.stdout)0
-rw-r--r--src/tools/clippy/tests/ui/author/loop.rs (renamed from src/tools/clippy/tests/ui-internal/author/loop.rs)0
-rw-r--r--src/tools/clippy/tests/ui/author/loop.stdout (renamed from src/tools/clippy/tests/ui-internal/author/loop.stdout)0
-rw-r--r--src/tools/clippy/tests/ui/author/macro_in_closure.rs (renamed from src/tools/clippy/tests/ui-internal/author/macro_in_closure.rs)0
-rw-r--r--src/tools/clippy/tests/ui/author/macro_in_closure.stdout (renamed from src/tools/clippy/tests/ui-internal/author/macro_in_closure.stdout)0
-rw-r--r--src/tools/clippy/tests/ui/author/macro_in_loop.rs (renamed from src/tools/clippy/tests/ui-internal/author/macro_in_loop.rs)0
-rw-r--r--src/tools/clippy/tests/ui/author/macro_in_loop.stdout (renamed from src/tools/clippy/tests/ui-internal/author/macro_in_loop.stdout)0
-rw-r--r--src/tools/clippy/tests/ui/author/matches.rs (renamed from src/tools/clippy/tests/ui-internal/author/matches.rs)0
-rw-r--r--src/tools/clippy/tests/ui/author/matches.stdout (renamed from src/tools/clippy/tests/ui-internal/author/matches.stdout)0
-rw-r--r--src/tools/clippy/tests/ui/author/repeat.rs (renamed from src/tools/clippy/tests/ui-internal/author/repeat.rs)0
-rw-r--r--src/tools/clippy/tests/ui/author/repeat.stdout (renamed from src/tools/clippy/tests/ui-internal/author/repeat.stdout)0
-rw-r--r--src/tools/clippy/tests/ui/author/struct.rs (renamed from src/tools/clippy/tests/ui-internal/author/struct.rs)0
-rw-r--r--src/tools/clippy/tests/ui/author/struct.stdout (renamed from src/tools/clippy/tests/ui-internal/author/struct.stdout)0
-rw-r--r--src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs50
-rw-r--r--src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr14
-rw-r--r--src/tools/clippy/tests/ui/borrow_deref_ref.fixed6
-rw-r--r--src/tools/clippy/tests/ui/borrow_deref_ref.rs6
-rw-r--r--src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.fixed1
-rw-r--r--src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs1
-rw-r--r--src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr12
-rw-r--r--src/tools/clippy/tests/ui/const_is_empty.rs14
-rw-r--r--src/tools/clippy/tests/ui/const_is_empty.stderr8
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.fixed3
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.rs3
-rw-r--r--src/tools/clippy/tests/ui/doc/doc-fixable.stderr44
-rw-r--r--src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr20
-rw-r--r--src/tools/clippy/tests/ui/empty_line_after/outer_attribute.stderr18
-rw-r--r--src/tools/clippy/tests/ui/four_forward_slashes.stderr10
-rw-r--r--src/tools/clippy/tests/ui/four_forward_slashes_first_line.stderr2
-rw-r--r--src/tools/clippy/tests/ui/identity_op.fixed46
-rw-r--r--src/tools/clippy/tests/ui/identity_op.rs44
-rw-r--r--src/tools/clippy/tests/ui/identity_op.stderr70
-rw-r--r--src/tools/clippy/tests/ui/infinite_loops.rs20
-rw-r--r--src/tools/clippy/tests/ui/infinite_loops.stderr42
-rw-r--r--src/tools/clippy/tests/ui/iter_without_into_iter.stderr16
-rw-r--r--src/tools/clippy/tests/ui/large_const_arrays.fixed2
-rw-r--r--src/tools/clippy/tests/ui/large_const_arrays.rs2
-rw-r--r--src/tools/clippy/tests/ui/large_const_arrays.stderr22
-rw-r--r--src/tools/clippy/tests/ui/manual_bits.fixed11
-rw-r--r--src/tools/clippy/tests/ui/manual_bits.rs11
-rw-r--r--src/tools/clippy/tests/ui/map_all_any_identity.fixed21
-rw-r--r--src/tools/clippy/tests/ui/map_all_any_identity.rs21
-rw-r--r--src/tools/clippy/tests/ui/map_all_any_identity.stderr26
-rw-r--r--src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.fixed73
-rw-r--r--src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.rs73
-rw-r--r--src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.stderr223
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.fixed5
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.rs5
-rw-r--r--src/tools/clippy/tests/ui/missing_const_for_fn/could_be_const.stderr52
-rw-r--r--src/tools/clippy/tests/ui/missing_doc.rs8
-rw-r--r--src/tools/clippy/tests/ui/missing_doc.stderr11
-rw-r--r--src/tools/clippy/tests/ui/needless_as_bytes.fixed50
-rw-r--r--src/tools/clippy/tests/ui/needless_as_bytes.rs50
-rw-r--r--src/tools/clippy/tests/ui/needless_as_bytes.stderr29
-rw-r--r--src/tools/clippy/tests/ui/needless_continue.rs17
-rw-r--r--src/tools/clippy/tests/ui/needless_continue.stderr10
-rw-r--r--src/tools/clippy/tests/ui/no_lints.rs3
-rw-r--r--src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs29
-rw-r--r--src/tools/clippy/tests/ui/no_mangle_with_rust_abi.stderr27
-rw-r--r--src/tools/clippy/tests/ui/no_mangle_with_rust_abi_2021.rs57
-rw-r--r--src/tools/clippy/tests/ui/no_mangle_with_rust_abi_2021.stderr83
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool.stderr8
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed30
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool_methods.rs24
-rw-r--r--src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr86
-rw-r--r--src/tools/clippy/tests/ui/question_mark.fixed56
-rw-r--r--src/tools/clippy/tests/ui/question_mark.rs71
-rw-r--r--src/tools/clippy/tests/ui/question_mark.stderr63
-rw-r--r--src/tools/clippy/tests/ui/rename.rs2
-rw-r--r--src/tools/clippy/tests/ui/rename.stderr14
-rw-r--r--src/tools/clippy/tests/ui/repeat_vec_with_capacity.fixed1
-rw-r--r--src/tools/clippy/tests/ui/repeat_vec_with_capacity.rs1
-rw-r--r--src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr6
-rw-r--r--src/tools/clippy/tests/ui/semicolon_outside_block.fixed8
-rw-r--r--src/tools/clippy/tests/ui/semicolon_outside_block.rs8
-rw-r--r--src/tools/clippy/tests/ui/semicolon_outside_block.stderr30
-rw-r--r--src/tools/clippy/tests/ui/suspicious_map.rs1
-rw-r--r--src/tools/clippy/tests/ui/suspicious_map.stderr4
-rw-r--r--src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.stderr2
-rw-r--r--src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr4
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_filter_map.rs13
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_filter_map.stderr53
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_find_map.rs9
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_find_map.stderr30
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_map_or.fixed64
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_map_or.rs67
-rw-r--r--src/tools/clippy/tests/ui/unnecessary_map_or.stderr99
-rw-r--r--src/tools/clippy/tests/ui/unused_io_amount.rs14
-rw-r--r--src/tools/clippy/triagebot.toml2
-rw-r--r--src/tools/clippy/util/gh-pages/index_template.html43
-rw-r--r--src/tools/clippy/util/gh-pages/script.js22
-rw-r--r--src/tools/clippy/util/gh-pages/style.css39
-rw-r--r--src/tools/clippy/util/gh-pages/theme.js4
-rw-r--r--src/tools/collect-license-metadata/src/main.rs39
-rw-r--r--src/tools/collect-license-metadata/src/reuse.rs4
-rw-r--r--src/tools/compiletest/Cargo.toml2
-rw-r--r--src/tools/compiletest/src/common.rs15
-rw-r--r--src/tools/compiletest/src/directive-list.rs9
-rw-r--r--src/tools/compiletest/src/header.rs32
-rw-r--r--src/tools/compiletest/src/header/cfg.rs11
-rw-r--r--src/tools/compiletest/src/header/needs.rs10
-rw-r--r--src/tools/compiletest/src/header/tests.rs72
-rw-r--r--src/tools/compiletest/src/lib.rs9
-rw-r--r--src/tools/compiletest/src/runtest.rs2
-rw-r--r--src/tools/compiletest/src/runtest/pretty.rs16
-rw-r--r--src/tools/generate-copyright/src/cargo_metadata.rs5
-rw-r--r--src/tools/generate-copyright/src/main.rs157
-rw-r--r--src/tools/generate-copyright/templates/COPYRIGHT-library.html53
-rw-r--r--src/tools/miri/.cargo/config.toml9
-rw-r--r--src/tools/miri/.github/workflows/ci.yml13
-rw-r--r--src/tools/miri/CONTRIBUTING.md27
-rw-r--r--src/tools/miri/README.md1
-rwxr-xr-xsrc/tools/miri/ci/ci.sh2
-rw-r--r--src/tools/miri/clippy.toml2
-rwxr-xr-xsrc/tools/miri/miri8
-rw-r--r--src/tools/miri/miri-script/Cargo.lock53
-rw-r--r--src/tools/miri/miri-script/Cargo.toml1
-rw-r--r--src/tools/miri/miri-script/src/commands.rs21
-rw-r--r--src/tools/miri/miri-script/src/coverage.rs91
-rw-r--r--src/tools/miri/miri-script/src/main.rs8
-rw-r--r--src/tools/miri/miri-script/src/util.rs11
-rw-r--r--src/tools/miri/rust-version2
-rw-r--r--src/tools/miri/src/alloc_addresses/mod.rs94
-rw-r--r--src/tools/miri/src/alloc_addresses/reuse_pool.rs2
-rw-r--r--src/tools/miri/src/alloc_bytes.rs2
-rw-r--r--src/tools/miri/src/bin/miri.rs88
-rw-r--r--src/tools/miri/src/borrow_tracker/mod.rs4
-rw-r--r--src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs2
-rw-r--r--src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs12
-rw-r--r--src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs2
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs16
-rw-r--r--src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs2
-rw-r--r--src/tools/miri/src/concurrency/cpu_affinity.rs2
-rw-r--r--src/tools/miri/src/concurrency/data_race.rs2
-rw-r--r--src/tools/miri/src/concurrency/range_object_map.rs2
-rw-r--r--src/tools/miri/src/concurrency/sync.rs189
-rw-r--r--src/tools/miri/src/concurrency/thread.rs59
-rw-r--r--src/tools/miri/src/concurrency/weak_memory.rs1
-rw-r--r--src/tools/miri/src/diagnostics.rs4
-rw-r--r--src/tools/miri/src/eval.rs16
-rw-r--r--src/tools/miri/src/helpers.rs24
-rw-r--r--src/tools/miri/src/intrinsics/mod.rs46
-rw-r--r--src/tools/miri/src/intrinsics/simd.rs45
-rw-r--r--src/tools/miri/src/lib.rs3
-rw-r--r--src/tools/miri/src/machine.rs38
-rw-r--r--src/tools/miri/src/math.rs164
-rw-r--r--src/tools/miri/src/operator.rs2
-rw-r--r--src/tools/miri/src/provenance_gc.rs32
-rw-r--r--src/tools/miri/src/range_map.rs2
-rw-r--r--src/tools/miri/src/shims/alloc.rs2
-rw-r--r--src/tools/miri/src/shims/backtrace.rs21
-rw-r--r--src/tools/miri/src/shims/extern_static.rs65
-rw-r--r--src/tools/miri/src/shims/foreign_items.rs130
-rw-r--r--src/tools/miri/src/shims/io_error.rs4
-rw-r--r--src/tools/miri/src/shims/native_lib.rs2
-rw-r--r--src/tools/miri/src/shims/panic.rs16
-rw-r--r--src/tools/miri/src/shims/tls.rs11
-rw-r--r--src/tools/miri/src/shims/unix/android/foreign_items.rs10
-rw-r--r--src/tools/miri/src/shims/unix/android/thread.rs37
-rw-r--r--src/tools/miri/src/shims/unix/env.rs2
-rw-r--r--src/tools/miri/src/shims/unix/fd.rs2
-rw-r--r--src/tools/miri/src/shims/unix/foreign_items.rs211
-rw-r--r--src/tools/miri/src/shims/unix/freebsd/foreign_items.rs21
-rw-r--r--src/tools/miri/src/shims/unix/fs.rs4
-rw-r--r--src/tools/miri/src/shims/unix/linux/eventfd.rs201
-rw-r--r--src/tools/miri/src/shims/unix/linux/foreign_items.rs109
-rw-r--r--src/tools/miri/src/shims/unix/linux/mem.rs4
-rw-r--r--src/tools/miri/src/shims/unix/linux/mod.rs1
-rw-r--r--src/tools/miri/src/shims/unix/linux/sync.rs126
-rw-r--r--src/tools/miri/src/shims/unix/linux/syscall.rs63
-rw-r--r--src/tools/miri/src/shims/unix/macos/foreign_items.rs88
-rw-r--r--src/tools/miri/src/shims/unix/macos/sync.rs62
-rw-r--r--src/tools/miri/src/shims/unix/mem.rs4
-rw-r--r--src/tools/miri/src/shims/unix/mod.rs2
-rw-r--r--src/tools/miri/src/shims/unix/solarish/foreign_items.rs36
-rw-r--r--src/tools/miri/src/shims/unix/sync.rs93
-rw-r--r--src/tools/miri/src/shims/unix/thread.rs66
-rw-r--r--src/tools/miri/src/shims/unix/unnamed_socket.rs2
-rw-r--r--src/tools/miri/src/shims/wasi/foreign_items.rs8
-rw-r--r--src/tools/miri/src/shims/windows/foreign_items.rs203
-rw-r--r--src/tools/miri/src/shims/windows/handle.rs64
-rw-r--r--src/tools/miri/src/shims/windows/sync.rs54
-rw-r--r--src/tools/miri/src/shims/windows/thread.rs14
-rw-r--r--src/tools/miri/src/shims/x86/aesni.rs33
-rw-r--r--src/tools/miri/src/shims/x86/avx.rs45
-rw-r--r--src/tools/miri/src/shims/x86/avx2.rs44
-rw-r--r--src/tools/miri/src/shims/x86/bmi.rs7
-rw-r--r--src/tools/miri/src/shims/x86/gfni.rs32
-rw-r--r--src/tools/miri/src/shims/x86/mod.rs562
-rw-r--r--src/tools/miri/src/shims/x86/sha.rs25
-rw-r--r--src/tools/miri/src/shims/x86/sse.rs22
-rw-r--r--src/tools/miri/src/shims/x86/sse2.rs32
-rw-r--r--src/tools/miri/src/shims/x86/sse3.rs9
-rw-r--r--src/tools/miri/src/shims/x86/sse41.rs25
-rw-r--r--src/tools/miri/src/shims/x86/sse42.rs88
-rw-r--r--src/tools/miri/src/shims/x86/ssse3.rs16
-rw-r--r--src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs65
-rw-r--r--src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr41
-rw-r--r--src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs71
-rw-r--r--src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr41
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs96
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr41
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.rs11
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.stderr14
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.rs21
-rw-r--r--src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.stderr14
-rw-r--r--src/tools/miri/tests/fail/intrinsic_fallback_is_spec.rs2
-rw-r--r--src/tools/miri/tests/fail/tree_borrows/write-during-2phase.rs4
-rw-r--r--src/tools/miri/tests/pass-dep/concurrency/linux-futex.rs11
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs119
-rw-r--r--src/tools/miri/tests/pass-dep/libc/libc-random.rs41
-rw-r--r--src/tools/miri/tests/pass-dep/libc/pthread-sync.rs24
-rw-r--r--src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs25
-rw-r--r--src/tools/miri/tests/pass/float.rs14
-rw-r--r--src/tools/miri/tests/pass/function_calls/abi_compat.rs24
-rw-r--r--src/tools/miri/tests/pass/shims/time-with-isolation.stdout2
-rw-r--r--src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs2
-rw-r--r--src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs193
-rw-r--r--src/tools/miri/tests/pass/stacked-borrows/2phase.rs23
-rw-r--r--src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs13
-rw-r--r--src/tools/miri/tests/pass/tls/windows-tls.rs18
-rw-r--r--src/tools/opt-dist/Cargo.toml2
-rw-r--r--src/tools/opt-dist/src/environment.rs8
-rw-r--r--src/tools/opt-dist/src/exec.rs4
-rw-r--r--src/tools/opt-dist/src/main.rs6
-rw-r--r--src/tools/opt-dist/src/tests.rs4
-rw-r--r--src/tools/run-make-support/Cargo.toml2
-rw-r--r--src/tools/run-make-support/src/external_deps/c_build.rs2
-rw-r--r--src/tools/run-make-support/src/external_deps/c_cxx_compiler/cc.rs (renamed from src/tools/run-make-support/src/external_deps/cc.rs)98
-rw-r--r--src/tools/run-make-support/src/external_deps/c_cxx_compiler/extras.rs97
-rw-r--r--src/tools/run-make-support/src/external_deps/c_cxx_compiler/gcc.rs66
-rw-r--r--src/tools/run-make-support/src/external_deps/c_cxx_compiler/mod.rs7
-rw-r--r--src/tools/run-make-support/src/external_deps/mod.rs2
-rw-r--r--src/tools/run-make-support/src/external_deps/rustc.rs23
-rw-r--r--src/tools/run-make-support/src/lib.rs7
-rw-r--r--src/tools/run-make-support/src/targets.rs6
-rw-r--r--src/tools/rust-analyzer/crates/parser/src/lexed_str.rs2
-rw-r--r--src/tools/rustbook/.gitignore1
-rw-r--r--src/tools/rustbook/Cargo.lock291
-rw-r--r--src/tools/rustbook/Cargo.toml2
-rw-r--r--src/tools/rustbook/src/main.rs31
-rw-r--r--src/tools/rustdoc-gui-test/Cargo.toml2
-rw-r--r--src/tools/rustdoc-js/tester.js55
-rw-r--r--src/tools/rustfmt/src/items.rs44
-rw-r--r--src/tools/rustfmt/src/macros.rs2
-rw-r--r--src/tools/rustfmt/src/spanned.rs6
-rw-r--r--src/tools/rustfmt/src/types.rs11
-rw-r--r--src/tools/rustfmt/src/visitor.rs17
-rw-r--r--src/tools/rustfmt/tests/source/unsafe-field.rs16
-rw-r--r--src/tools/rustfmt/tests/target/const-generics.rs25
-rw-r--r--src/tools/rustfmt/tests/target/unsafe-field.rs11
-rw-r--r--src/tools/suggest-tests/Cargo.toml2
-rw-r--r--src/tools/tidy/Cargo.toml2
-rw-r--r--src/tools/tidy/src/deps.rs24
-rw-r--r--src/tools/tidy/src/issues.txt1
-rw-r--r--src/tools/tidy/src/known_bug.rs18
-rw-r--r--src/tools/tidy/src/style.rs22
-rw-r--r--src/tools/tidy/src/target_specific_tests.rs34
-rw-r--r--src/tools/tidy/src/walk.rs1
-rw-r--r--src/tools/unicode-table-generator/src/cascading_map.rs2
-rw-r--r--src/tools/unicode-table-generator/src/main.rs2
-rw-r--r--src/tools/unicode-table-generator/src/range_search.rs2
-rw-r--r--src/tools/unicode-table-generator/src/raw_emitter.rs5
548 files changed, 10516 insertions, 4365 deletions
diff --git a/src/tools/build-manifest/src/main.rs b/src/tools/build-manifest/src/main.rs
index 925cbfe09a4..39d9158a1ff 100644
--- a/src/tools/build-manifest/src/main.rs
+++ b/src/tools/build-manifest/src/main.rs
@@ -157,7 +157,6 @@ static TARGETS: &[&str] = &[
     "thumbv8m.main-none-eabihf",
     "wasm32-unknown-emscripten",
     "wasm32-unknown-unknown",
-    "wasm32-wasi",
     "wasm32-wasip1",
     "wasm32-wasip1-threads",
     "wasm32-wasip2",
diff --git a/src/tools/build_helper/Cargo.toml b/src/tools/build_helper/Cargo.toml
deleted file mode 100644
index 66894e1abc4..00000000000
--- a/src/tools/build_helper/Cargo.toml
+++ /dev/null
@@ -1,10 +0,0 @@
-[package]
-name = "build_helper"
-version = "0.1.0"
-edition = "2021"
-
-# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
-
-[dependencies]
-serde = "1"
-serde_derive = "1"
diff --git a/src/tools/build_helper/README.md b/src/tools/build_helper/README.md
deleted file mode 100644
index f81b631c3fd..00000000000
--- a/src/tools/build_helper/README.md
+++ /dev/null
@@ -1 +0,0 @@
-Types and functions shared across tools in this workspace.
diff --git a/src/tools/build_helper/src/ci.rs b/src/tools/build_helper/src/ci.rs
deleted file mode 100644
index 60f319129a0..00000000000
--- a/src/tools/build_helper/src/ci.rs
+++ /dev/null
@@ -1,95 +0,0 @@
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-pub enum CiEnv {
-    /// Not a CI environment.
-    None,
-    /// The GitHub Actions environment, for Linux (including Docker), Windows and macOS builds.
-    GitHubActions,
-}
-
-impl CiEnv {
-    /// Obtains the current CI environment.
-    pub fn current() -> CiEnv {
-        if std::env::var("GITHUB_ACTIONS").map_or(false, |e| e == "true") {
-            CiEnv::GitHubActions
-        } else {
-            CiEnv::None
-        }
-    }
-
-    pub fn is_ci() -> bool {
-        Self::current() != CiEnv::None
-    }
-
-    /// Checks if running in rust-lang/rust managed CI job.
-    pub fn is_rust_lang_managed_ci_job() -> bool {
-        Self::is_ci()
-            // If both are present, we can assume it's an upstream CI job
-            // as they are always set unconditionally.
-            && std::env::var_os("CI_JOB_NAME").is_some()
-            && std::env::var_os("TOOLSTATE_REPO").is_some()
-    }
-}
-
-pub mod gha {
-    use std::sync::Mutex;
-
-    static ACTIVE_GROUPS: Mutex<Vec<String>> = Mutex::new(Vec::new());
-
-    /// All github actions log messages from this call to the Drop of the return value
-    /// will be grouped and hidden by default in logs. Note that since github actions doesn't
-    /// support group nesting, any active group will be first finished when a subgroup is started,
-    /// and then re-started when the subgroup finishes.
-    #[track_caller]
-    pub fn group(name: impl std::fmt::Display) -> Group {
-        let mut groups = ACTIVE_GROUPS.lock().unwrap();
-
-        // A group is currently active. End it first to avoid nesting.
-        if !groups.is_empty() {
-            end_group();
-        }
-
-        let name = name.to_string();
-        start_group(&name);
-        groups.push(name);
-        Group(())
-    }
-
-    /// A guard that closes the current github actions log group on drop.
-    #[must_use]
-    pub struct Group(());
-
-    impl Drop for Group {
-        fn drop(&mut self) {
-            end_group();
-
-            let mut groups = ACTIVE_GROUPS.lock().unwrap();
-            // Remove the current group
-            groups.pop();
-
-            // If there was some previous group, restart it
-            if is_in_gha() {
-                if let Some(name) = groups.last() {
-                    start_group(format!("{name} (continued)"));
-                }
-            }
-        }
-    }
-
-    fn start_group(name: impl std::fmt::Display) {
-        if is_in_gha() {
-            println!("::group::{name}");
-        } else {
-            println!("{name}")
-        }
-    }
-
-    fn end_group() {
-        if is_in_gha() {
-            println!("::endgroup::");
-        }
-    }
-
-    fn is_in_gha() -> bool {
-        std::env::var_os("GITHUB_ACTIONS").is_some()
-    }
-}
diff --git a/src/tools/build_helper/src/drop_bomb/mod.rs b/src/tools/build_helper/src/drop_bomb/mod.rs
deleted file mode 100644
index 0a5bb04b55b..00000000000
--- a/src/tools/build_helper/src/drop_bomb/mod.rs
+++ /dev/null
@@ -1,54 +0,0 @@
-//! This module implements "drop bombs" intended for use by command wrappers to ensure that the
-//! constructed commands are *eventually* executed. This is exactly like `rustc_errors::Diag` where
-//! we force every `Diag` to be consumed or we emit a bug, but we panic instead.
-//!
-//! This is adapted from <https://docs.rs/drop_bomb/latest/drop_bomb/> and simplified for our
-//! purposes.
-
-use std::ffi::{OsStr, OsString};
-use std::panic;
-
-#[cfg(test)]
-mod tests;
-
-#[derive(Debug)]
-pub struct DropBomb {
-    command: OsString,
-    defused: bool,
-    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 location.
-    #[track_caller]
-    pub fn arm<S: AsRef<OsStr>>(command: S) -> DropBomb {
-        DropBomb {
-            command: command.as_ref().into(),
-            defused: false,
-            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 fn defuse(&mut self) {
-        self.defused = true;
-    }
-}
-
-impl Drop for DropBomb {
-    fn drop(&mut self) {
-        if !self.defused && !std::thread::panicking() {
-            panic!(
-                "command constructed at `{}` was dropped without being executed: `{}`",
-                self.armed_location,
-                self.command.to_string_lossy()
-            )
-        }
-    }
-}
diff --git a/src/tools/build_helper/src/drop_bomb/tests.rs b/src/tools/build_helper/src/drop_bomb/tests.rs
deleted file mode 100644
index 4a488c0f670..00000000000
--- a/src/tools/build_helper/src/drop_bomb/tests.rs
+++ /dev/null
@@ -1,15 +0,0 @@
-use super::DropBomb;
-
-#[test]
-#[should_panic]
-fn test_arm() {
-    let bomb = DropBomb::arm("hi :3");
-    drop(bomb); // <- armed bomb should explode when not defused
-}
-
-#[test]
-fn test_defuse() {
-    let mut bomb = DropBomb::arm("hi :3");
-    bomb.defuse();
-    drop(bomb); // <- defused bomb should not explode
-}
diff --git a/src/tools/build_helper/src/git.rs b/src/tools/build_helper/src/git.rs
deleted file mode 100644
index 2aad5650fa8..00000000000
--- a/src/tools/build_helper/src/git.rs
+++ /dev/null
@@ -1,208 +0,0 @@
-use std::path::{Path, PathBuf};
-use std::process::{Command, Stdio};
-
-use crate::ci::CiEnv;
-
-pub struct GitConfig<'a> {
-    pub git_repository: &'a str,
-    pub nightly_branch: &'a str,
-    pub git_merge_commit_email: &'a str,
-}
-
-/// Runs a command and returns the output
-pub fn output_result(cmd: &mut Command) -> Result<String, String> {
-    let output = match cmd.stderr(Stdio::inherit()).output() {
-        Ok(status) => status,
-        Err(e) => return Err(format!("failed to run command: {:?}: {}", cmd, e)),
-    };
-    if !output.status.success() {
-        return Err(format!(
-            "command did not execute successfully: {:?}\n\
-             expected success, got: {}\n{}",
-            cmd,
-            output.status,
-            String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))?
-        ));
-    }
-    String::from_utf8(output.stdout).map_err(|err| format!("{err:?}"))
-}
-
-/// Finds the remote for rust-lang/rust.
-/// For example for these remotes it will return `upstream`.
-/// ```text
-/// origin  https://github.com/Nilstrieb/rust.git (fetch)
-/// origin  https://github.com/Nilstrieb/rust.git (push)
-/// upstream        https://github.com/rust-lang/rust (fetch)
-/// upstream        https://github.com/rust-lang/rust (push)
-/// ```
-pub fn get_rust_lang_rust_remote(
-    config: &GitConfig<'_>,
-    git_dir: Option<&Path>,
-) -> Result<String, String> {
-    let mut git = Command::new("git");
-    if let Some(git_dir) = git_dir {
-        git.current_dir(git_dir);
-    }
-    git.args(["config", "--local", "--get-regex", "remote\\..*\\.url"]);
-    let stdout = output_result(&mut git)?;
-
-    let rust_lang_remote = stdout
-        .lines()
-        .find(|remote| remote.contains(config.git_repository))
-        .ok_or_else(|| format!("{} remote not found", config.git_repository))?;
-
-    let remote_name =
-        rust_lang_remote.split('.').nth(1).ok_or_else(|| "remote name not found".to_owned())?;
-    Ok(remote_name.into())
-}
-
-pub fn rev_exists(rev: &str, git_dir: Option<&Path>) -> Result<bool, String> {
-    let mut git = Command::new("git");
-    if let Some(git_dir) = git_dir {
-        git.current_dir(git_dir);
-    }
-    git.args(["rev-parse", rev]);
-    let output = git.output().map_err(|err| format!("{err:?}"))?;
-
-    match output.status.code() {
-        Some(0) => Ok(true),
-        Some(128) => Ok(false),
-        None => Err(format!(
-            "git didn't exit properly: {}",
-            String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))?
-        )),
-        Some(code) => Err(format!(
-            "git command exited with status code: {code}: {}",
-            String::from_utf8(output.stderr).map_err(|err| format!("{err:?}"))?
-        )),
-    }
-}
-
-/// Returns the master branch from which we can take diffs to see changes.
-/// This will usually be rust-lang/rust master, but sometimes this might not exist.
-/// This could be because the user is updating their forked master branch using the GitHub UI
-/// and therefore doesn't need an upstream master branch checked out.
-/// We will then fall back to origin/master in the hope that at least this exists.
-pub fn updated_master_branch(
-    config: &GitConfig<'_>,
-    git_dir: Option<&Path>,
-) -> Result<String, String> {
-    let upstream_remote = get_rust_lang_rust_remote(config, git_dir)?;
-    let branch = config.nightly_branch;
-    for upstream_master in [format!("{upstream_remote}/{branch}"), format!("origin/{branch}")] {
-        if rev_exists(&upstream_master, git_dir)? {
-            return Ok(upstream_master);
-        }
-    }
-
-    Err("Cannot find any suitable upstream master branch".to_owned())
-}
-
-/// Finds the nearest merge commit by comparing the local `HEAD` with the upstream branch's state.
-/// To work correctly, the upstream remote must be properly configured using `git remote add <name> <url>`.
-/// In most cases `get_closest_merge_commit` is the function you are looking for as it doesn't require remote
-/// to be configured.
-fn git_upstream_merge_base(
-    config: &GitConfig<'_>,
-    git_dir: Option<&Path>,
-) -> Result<String, String> {
-    let updated_master = updated_master_branch(config, git_dir)?;
-    let mut git = Command::new("git");
-    if let Some(git_dir) = git_dir {
-        git.current_dir(git_dir);
-    }
-    Ok(output_result(git.arg("merge-base").arg(&updated_master).arg("HEAD"))?.trim().to_owned())
-}
-
-/// Searches for the nearest merge commit in the repository that also exists upstream.
-///
-/// It looks for the most recent commit made by the merge bot by matching the author's email
-/// address with the merge bot's email.
-pub fn get_closest_merge_commit(
-    git_dir: Option<&Path>,
-    config: &GitConfig<'_>,
-    target_paths: &[PathBuf],
-) -> Result<String, String> {
-    let mut git = Command::new("git");
-
-    if let Some(git_dir) = git_dir {
-        git.current_dir(git_dir);
-    }
-
-    let merge_base = {
-        if CiEnv::is_ci() {
-            git_upstream_merge_base(config, git_dir).unwrap()
-        } else {
-            // For non-CI environments, ignore rust-lang/rust upstream as it usually gets
-            // outdated very quickly.
-            "HEAD".to_string()
-        }
-    };
-
-    git.args([
-        "rev-list",
-        &format!("--author={}", config.git_merge_commit_email),
-        "-n1",
-        "--first-parent",
-        &merge_base,
-    ]);
-
-    if !target_paths.is_empty() {
-        git.arg("--").args(target_paths);
-    }
-
-    Ok(output_result(&mut git)?.trim().to_owned())
-}
-
-/// Returns the files that have been modified in the current branch compared to the master branch.
-/// The `extensions` parameter can be used to filter the files by their extension.
-/// Does not include removed files.
-/// If `extensions` is empty, all files will be returned.
-pub fn get_git_modified_files(
-    config: &GitConfig<'_>,
-    git_dir: Option<&Path>,
-    extensions: &[&str],
-) -> Result<Option<Vec<String>>, String> {
-    let merge_base = get_closest_merge_commit(git_dir, config, &[])?;
-
-    let mut git = Command::new("git");
-    if let Some(git_dir) = git_dir {
-        git.current_dir(git_dir);
-    }
-    let files = output_result(git.args(["diff-index", "--name-status", merge_base.trim()]))?
-        .lines()
-        .filter_map(|f| {
-            let (status, name) = f.trim().split_once(char::is_whitespace).unwrap();
-            if status == "D" {
-                None
-            } else if Path::new(name).extension().map_or(false, |ext| {
-                extensions.is_empty() || extensions.contains(&ext.to_str().unwrap())
-            }) {
-                Some(name.to_owned())
-            } else {
-                None
-            }
-        })
-        .collect();
-    Ok(Some(files))
-}
-
-/// Returns the files that haven't been added to git yet.
-pub fn get_git_untracked_files(
-    config: &GitConfig<'_>,
-    git_dir: Option<&Path>,
-) -> Result<Option<Vec<String>>, String> {
-    let Ok(_updated_master) = updated_master_branch(config, git_dir) else {
-        return Ok(None);
-    };
-    let mut git = Command::new("git");
-    if let Some(git_dir) = git_dir {
-        git.current_dir(git_dir);
-    }
-
-    let files = output_result(git.arg("ls-files").arg("--others").arg("--exclude-standard"))?
-        .lines()
-        .map(|s| s.trim().to_owned())
-        .collect();
-    Ok(Some(files))
-}
diff --git a/src/tools/build_helper/src/lib.rs b/src/tools/build_helper/src/lib.rs
deleted file mode 100644
index 4a4f0ca2a9d..00000000000
--- a/src/tools/build_helper/src/lib.rs
+++ /dev/null
@@ -1,31 +0,0 @@
-//! 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;
-pub mod util;
-
-/// The default set of crates for opt-dist to collect LLVM profiles.
-pub const LLVM_PGO_CRATES: &[&str] = &[
-    "syn-1.0.89",
-    "cargo-0.60.0",
-    "serde-1.0.136",
-    "ripgrep-13.0.0",
-    "regex-1.5.5",
-    "clap-3.1.6",
-    "hyper-0.14.18",
-];
-
-/// The default set of crates for opt-dist to collect rustc profiles.
-pub const RUSTC_PGO_CRATES: &[&str] = &[
-    "externs",
-    "ctfe-stress-5",
-    "cargo-0.60.0",
-    "token-stream-stress",
-    "match-stress",
-    "tuple-stress",
-    "diesel-1.4.8",
-    "bitmaps-3.1.0",
-];
diff --git a/src/tools/build_helper/src/metrics.rs b/src/tools/build_helper/src/metrics.rs
deleted file mode 100644
index 2d0c66a8f33..00000000000
--- a/src/tools/build_helper/src/metrics.rs
+++ /dev/null
@@ -1,92 +0,0 @@
-use serde_derive::{Deserialize, Serialize};
-
-#[derive(Serialize, Deserialize)]
-#[serde(rename_all = "snake_case")]
-pub struct JsonRoot {
-    #[serde(default)] // For version 0 the field was not present.
-    pub format_version: usize,
-    pub system_stats: JsonInvocationSystemStats,
-    pub invocations: Vec<JsonInvocation>,
-}
-
-#[derive(Serialize, Deserialize)]
-#[serde(rename_all = "snake_case")]
-pub struct JsonInvocation {
-    // Unix timestamp in seconds
-    //
-    // This is necessary to easily correlate this invocation with logs or other data.
-    pub start_time: u64,
-    pub duration_including_children_sec: f64,
-    pub children: Vec<JsonNode>,
-}
-
-#[derive(Serialize, Deserialize)]
-#[serde(tag = "kind", rename_all = "snake_case")]
-pub enum JsonNode {
-    RustbuildStep {
-        #[serde(rename = "type")]
-        type_: String,
-        debug_repr: String,
-
-        duration_excluding_children_sec: f64,
-        system_stats: JsonStepSystemStats,
-
-        children: Vec<JsonNode>,
-    },
-    TestSuite(TestSuite),
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct TestSuite {
-    pub metadata: TestSuiteMetadata,
-    pub tests: Vec<Test>,
-}
-
-#[derive(Serialize, Deserialize)]
-#[serde(tag = "kind", rename_all = "snake_case")]
-pub enum TestSuiteMetadata {
-    CargoPackage {
-        crates: Vec<String>,
-        target: String,
-        host: String,
-        stage: u32,
-    },
-    Compiletest {
-        suite: String,
-        mode: String,
-        compare_mode: Option<String>,
-        target: String,
-        host: String,
-        stage: u32,
-    },
-}
-
-#[derive(Serialize, Deserialize)]
-pub struct Test {
-    pub name: String,
-    #[serde(flatten)]
-    pub outcome: TestOutcome,
-}
-
-#[derive(Serialize, Deserialize)]
-#[serde(tag = "outcome", rename_all = "snake_case")]
-pub enum TestOutcome {
-    Passed,
-    Failed,
-    Ignored { ignore_reason: Option<String> },
-}
-
-#[derive(Serialize, Deserialize)]
-#[serde(rename_all = "snake_case")]
-pub struct JsonInvocationSystemStats {
-    pub cpu_threads_count: usize,
-    pub cpu_model: String,
-
-    pub memory_total_bytes: u64,
-}
-
-#[derive(Serialize, Deserialize)]
-#[serde(rename_all = "snake_case")]
-pub struct JsonStepSystemStats {
-    pub cpu_utilization_percent: f64,
-}
diff --git a/src/tools/build_helper/src/stage0_parser.rs b/src/tools/build_helper/src/stage0_parser.rs
deleted file mode 100644
index ff05b116989..00000000000
--- a/src/tools/build_helper/src/stage0_parser.rs
+++ /dev/null
@@ -1,76 +0,0 @@
-use std::collections::BTreeMap;
-
-#[derive(Default, Clone)]
-pub struct Stage0 {
-    pub compiler: VersionMetadata,
-    pub rustfmt: Option<VersionMetadata>,
-    pub config: Stage0Config,
-    pub checksums_sha256: BTreeMap<String, String>,
-}
-
-#[derive(Default, Clone)]
-pub struct VersionMetadata {
-    pub date: String,
-    pub version: String,
-}
-
-#[derive(Default, Clone)]
-pub struct Stage0Config {
-    pub dist_server: String,
-    pub artifacts_server: String,
-    pub artifacts_with_llvm_assertions_server: String,
-    pub git_merge_commit_email: String,
-    pub git_repository: String,
-    pub nightly_branch: String,
-}
-
-pub fn parse_stage0_file() -> Stage0 {
-    let stage0_content = include_str!("../../../stage0");
-
-    let mut stage0 = Stage0::default();
-    for line in stage0_content.lines() {
-        let line = line.trim();
-
-        if line.is_empty() {
-            continue;
-        }
-
-        // Ignore comments
-        if line.starts_with('#') {
-            continue;
-        }
-
-        let (key, value) = line.split_once('=').unwrap();
-
-        match key {
-            "dist_server" => stage0.config.dist_server = value.to_owned(),
-            "artifacts_server" => stage0.config.artifacts_server = value.to_owned(),
-            "artifacts_with_llvm_assertions_server" => {
-                stage0.config.artifacts_with_llvm_assertions_server = value.to_owned()
-            }
-            "git_merge_commit_email" => stage0.config.git_merge_commit_email = value.to_owned(),
-            "git_repository" => stage0.config.git_repository = value.to_owned(),
-            "nightly_branch" => stage0.config.nightly_branch = value.to_owned(),
-
-            "compiler_date" => stage0.compiler.date = value.to_owned(),
-            "compiler_version" => stage0.compiler.version = value.to_owned(),
-
-            "rustfmt_date" => {
-                stage0.rustfmt.get_or_insert(VersionMetadata::default()).date = value.to_owned();
-            }
-            "rustfmt_version" => {
-                stage0.rustfmt.get_or_insert(VersionMetadata::default()).version = value.to_owned();
-            }
-
-            dist if dist.starts_with("dist") => {
-                stage0.checksums_sha256.insert(key.to_owned(), value.to_owned());
-            }
-
-            unsupported => {
-                println!("'{unsupported}' field is not supported.");
-            }
-        }
-    }
-
-    stage0
-}
diff --git a/src/tools/build_helper/src/util.rs b/src/tools/build_helper/src/util.rs
deleted file mode 100644
index 72c05c4c48a..00000000000
--- a/src/tools/build_helper/src/util.rs
+++ /dev/null
@@ -1,75 +0,0 @@
-use std::fs::File;
-use std::io::{BufRead, BufReader};
-use std::path::Path;
-use std::process::Command;
-use std::sync::OnceLock;
-
-/// Invokes `build_helper::util::detail_exit` with `cfg!(test)`
-///
-/// This is a macro instead of a function so that it uses `cfg(test)` in the *calling* crate, not in build helper.
-#[macro_export]
-macro_rules! exit {
-    ($code:expr) => {
-        $crate::util::detail_exit($code, cfg!(test));
-    };
-}
-
-/// If code is not 0 (successful exit status), exit status is 101 (rust's default error code.)
-/// If `is_test` true and code is an error code, it will cause a panic.
-pub fn detail_exit(code: i32, is_test: bool) -> ! {
-    // if in test and code is an error code, panic with status code provided
-    if is_test {
-        panic!("status code: {}", code);
-    } else {
-        // otherwise,exit with provided status code
-        std::process::exit(code);
-    }
-}
-
-pub fn fail(s: &str) -> ! {
-    eprintln!("\n\n{}\n\n", s);
-    detail_exit(1, cfg!(test));
-}
-
-pub fn try_run(cmd: &mut Command, print_cmd_on_fail: bool) -> Result<(), ()> {
-    let status = match cmd.status() {
-        Ok(status) => status,
-        Err(e) => fail(&format!("failed to execute command: {:?}\nerror: {}", cmd, e)),
-    };
-    if !status.success() {
-        if print_cmd_on_fail {
-            println!(
-                "\n\ncommand did not execute successfully: {:?}\n\
-                 expected success, got: {}\n\n",
-                cmd, status
-            );
-        }
-        Err(())
-    } else {
-        Ok(())
-    }
-}
-
-/// Returns the submodule paths from the `.gitmodules` file in the given directory.
-pub fn parse_gitmodules(target_dir: &Path) -> &[String] {
-    static SUBMODULES_PATHS: OnceLock<Vec<String>> = OnceLock::new();
-    let gitmodules = target_dir.join(".gitmodules");
-    assert!(gitmodules.exists(), "'{}' file is missing.", gitmodules.display());
-
-    let init_submodules_paths = || {
-        let file = File::open(gitmodules).unwrap();
-
-        let mut submodules_paths = vec![];
-        for line in BufReader::new(file).lines().map_while(Result::ok) {
-            let line = line.trim();
-            if line.starts_with("path") {
-                let actual_path = line.split(' ').last().expect("Couldn't get value of path");
-                submodules_paths.push(actual_path.to_owned());
-            }
-        }
-
-        submodules_paths
-    };
-
-    SUBMODULES_PATHS.get_or_init(|| init_submodules_paths())
-}
diff --git a/src/tools/bump-stage0/Cargo.toml b/src/tools/bump-stage0/Cargo.toml
index de5d821133d..6ee7a831839 100644
--- a/src/tools/bump-stage0/Cargo.toml
+++ b/src/tools/bump-stage0/Cargo.toml
@@ -7,7 +7,7 @@ edition = "2021"
 
 [dependencies]
 anyhow = "1.0.34"
-build_helper = { path = "../build_helper" }
+build_helper = { path = "../../build_helper" }
 curl = "0.4.38"
 indexmap = { version = "2.0.0", features = ["serde"] }
 serde = { version = "1.0.125", features = ["derive"] }
diff --git a/src/tools/cargo b/src/tools/cargo
-Subproject e75214ea4936d2f2c909a71a1237042cc0e14b0
+Subproject 4c39aaff66862cc0da52fe529aa1990bb8bb9a2
diff --git a/src/tools/clippy/.github/workflows/clippy_dev.yml b/src/tools/clippy/.github/workflows/clippy_dev.yml
index cf0a8bde202..bcb3193ad67 100644
--- a/src/tools/clippy/.github/workflows/clippy_dev.yml
+++ b/src/tools/clippy/.github/workflows/clippy_dev.yml
@@ -1,17 +1,8 @@
 name: Clippy Dev Test
 
 on:
-  push:
-    branches:
-      - auto
-      - try
+  merge_group:
   pull_request:
-    # Only run on paths, that get checked by the clippy_dev tool
-    paths:
-    - 'CHANGELOG.md'
-    - 'README.md'
-    - '**.stderr'
-    - '**.rs'
 
 env:
   RUST_BACKTRACE: 1
@@ -47,28 +38,21 @@ jobs:
         cargo check
         git reset --hard HEAD
 
-  # These jobs doesn't actually test anything, but they're only used to tell
-  # bors the build completed, as there is no practical way to detect when a
-  # workflow is successful listening to webhooks only.
-  #
-  # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
-
-  end-success:
-    name: bors dev test finished
-    if: github.event.pusher.name == 'bors' && success()
-    runs-on: ubuntu-latest
-    needs: [clippy_dev]
-
-    steps:
-      - name: Mark the job as successful
-        run: exit 0
-
-  end-failure:
-    name: bors dev test finished
-    if: github.event.pusher.name == 'bors' && (failure() || cancelled())
+  conclusion_dev:
+    needs: [ clippy_dev ]
+    # We need to ensure this job does *not* get skipped if its dependencies fail,
+    # because a skipped job is considered a success by GitHub. So we have to
+    # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run
+    # when the workflow is canceled manually.
+    #
+    # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
+    if: ${{ !cancelled() }}
     runs-on: ubuntu-latest
-    needs: [clippy_dev]
-
     steps:
-      - name: Mark the job as a failure
-        run: exit 1
+      # Manually check the status of all dependencies. `if: failure()` does not work.
+      - name: Conclusion
+        run: |
+          # Print the dependent jobs to see them in the CI log
+          jq -C <<< '${{ toJson(needs) }}'
+          # Check if all jobs that we depend on (in the needs array) were successful.
+          jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'
diff --git a/src/tools/clippy/.github/workflows/clippy_bors.yml b/src/tools/clippy/.github/workflows/clippy_mq.yml
index 026771e6fcf..49622048050 100644
--- a/src/tools/clippy/.github/workflows/clippy_bors.yml
+++ b/src/tools/clippy/.github/workflows/clippy_mq.yml
@@ -1,10 +1,7 @@
-name: Clippy Test (bors)
+name: Clippy Test (merge queue)
 
 on:
-  push:
-    branches:
-      - auto
-      - try
+  merge_group:
 
 env:
   RUST_BACKTRACE: 1
@@ -13,11 +10,6 @@ env:
   CARGO_INCREMENTAL: 0
   RUSTFLAGS: -D warnings
 
-concurrency:
-  # For a given workflow, if we push to the same branch, cancel all previous builds on that branch.
-  group: "${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}"
-  cancel-in-progress: true
-
 defaults:
   run:
     shell: bash
@@ -218,28 +210,21 @@ jobs:
       env:
         INTEGRATION: ${{ matrix.integration }}
 
-  # These jobs doesn't actually test anything, but they're only used to tell
-  # bors the build completed, as there is no practical way to detect when a
-  # workflow is successful listening to webhooks only.
-  #
-  # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
-
-  end-success:
-    name: bors test finished
-    if: github.event.pusher.name == 'bors' && success()
+  conclusion:
+    needs: [ changelog, base, metadata_collection, integration_build, integration ]
+    # We need to ensure this job does *not* get skipped if its dependencies fail,
+    # because a skipped job is considered a success by GitHub. So we have to
+    # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run
+    # when the workflow is canceled manually.
+    #
+    # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
+    if: ${{ !cancelled() }}
     runs-on: ubuntu-latest
-    needs: [changelog, base, metadata_collection, integration_build, integration]
-
-    steps:
-      - name: Mark the job as successful
-        run: exit 0
-
-  end-failure:
-    name: bors test finished
-    if: github.event.pusher.name == 'bors' && (failure() || cancelled())
-    runs-on: ubuntu-latest
-    needs: [changelog, base, metadata_collection, integration_build, integration]
-
     steps:
-      - name: Mark the job as a failure
-        run: exit 1
+      # Manually check the status of all dependencies. `if: failure()` does not work.
+      - name: Conclusion
+        run: |
+          # Print the dependent jobs to see them in the CI log
+          jq -C <<< '${{ toJson(needs) }}'
+          # Check if all jobs that we depend on (in the needs array) were successful.
+          jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'
diff --git a/src/tools/clippy/.github/workflows/clippy.yml b/src/tools/clippy/.github/workflows/clippy_pr.yml
index 0a0538490cc..2e5b5bd41df 100644
--- a/src/tools/clippy/.github/workflows/clippy.yml
+++ b/src/tools/clippy/.github/workflows/clippy_pr.yml
@@ -1,24 +1,7 @@
 name: Clippy Test
 
 on:
-  push:
-    # Ignore bors branches, since they are covered by `clippy_bors.yml`
-    branches-ignore:
-      - auto
-      - try
-    # Don't run Clippy tests, when only text files were modified
-    paths-ignore:
-    - 'COPYRIGHT'
-    - 'LICENSE-*'
-    - '**.md'
-    - '**.txt'
   pull_request:
-    # Don't run Clippy tests, when only text files were modified
-    paths-ignore:
-    - 'COPYRIGHT'
-    - 'LICENSE-*'
-    - '**.md'
-    - '**.txt'
 
 env:
   RUST_BACKTRACE: 1
@@ -35,7 +18,7 @@ concurrency:
 
 jobs:
   base:
-    # NOTE: If you modify this job, make sure you copy the changes to clippy_bors.yml
+    # NOTE: If you modify this job, make sure you copy the changes to clippy_mq.yml
     runs-on: ubuntu-latest
 
     steps:
@@ -73,3 +56,24 @@ jobs:
       run: .github/driver.sh
       env:
         OS: ${{ runner.os }}
+
+  # We need to have the "conclusion" job also on PR CI, to make it possible
+  # to add PRs to a merge queue.
+  conclusion:
+    needs: [ base ]
+    # We need to ensure this job does *not* get skipped if its dependencies fail,
+    # because a skipped job is considered a success by GitHub. So we have to
+    # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run
+    # when the workflow is canceled manually.
+    #
+    # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
+    if: ${{ !cancelled() }}
+    runs-on: ubuntu-latest
+    steps:
+      # Manually check the status of all dependencies. `if: failure()` does not work.
+      - name: Conclusion
+        run: |
+          # Print the dependent jobs to see them in the CI log
+          jq -C <<< '${{ toJson(needs) }}'
+          # Check if all jobs that we depend on (in the needs array) were successful.
+          jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'
diff --git a/src/tools/clippy/.github/workflows/deploy.yml b/src/tools/clippy/.github/workflows/deploy.yml
index 94f494b65c4..32dc251c836 100644
--- a/src/tools/clippy/.github/workflows/deploy.yml
+++ b/src/tools/clippy/.github/workflows/deploy.yml
@@ -52,7 +52,7 @@ jobs:
       run: cargo generate-lockfile
 
     - name: Cache
-      uses: Swatinem/rust-cache@v2.7.0
+      uses: Swatinem/rust-cache@v2
       with:
         save-if: ${{ github.ref == 'refs/heads/master' }}
 
diff --git a/src/tools/clippy/.github/workflows/remark.yml b/src/tools/clippy/.github/workflows/remark.yml
index a1b011dc32d..0d402fe7064 100644
--- a/src/tools/clippy/.github/workflows/remark.yml
+++ b/src/tools/clippy/.github/workflows/remark.yml
@@ -1,13 +1,8 @@
 name: Remark
 
 on:
-  push:
-    branches:
-      - auto
-      - try
+  merge_group:
   pull_request:
-    paths:
-    - '**.md'
 
 jobs:
   remark:
@@ -45,28 +40,21 @@ jobs:
     - name: Build mdbook
       run: mdbook build book
 
-  # These jobs doesn't actually test anything, but they're only used to tell
-  # bors the build completed, as there is no practical way to detect when a
-  # workflow is successful listening to webhooks only.
-  #
-  # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
-
-  end-success:
-    name: bors remark test finished
-    if: github.event.pusher.name == 'bors' && success()
-    runs-on: ubuntu-latest
-    needs: [remark]
-
-    steps:
-      - name: Mark the job as successful
-        run: exit 0
-
-  end-failure:
-    name: bors remark test finished
-    if: github.event.pusher.name == 'bors' && (failure() || cancelled())
+  conclusion_remark:
+    needs: [ remark ]
+    # We need to ensure this job does *not* get skipped if its dependencies fail,
+    # because a skipped job is considered a success by GitHub. So we have to
+    # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run
+    # when the workflow is canceled manually.
+    #
+    # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
+    if: ${{ !cancelled() }}
     runs-on: ubuntu-latest
-    needs: [remark]
-
     steps:
-      - name: Mark the job as a failure
-        run: exit 1
+      # Manually check the status of all dependencies. `if: failure()` does not work.
+      - name: Conclusion
+        run: |
+          # Print the dependent jobs to see them in the CI log
+          jq -C <<< '${{ toJson(needs) }}'
+          # Check if all jobs that we depend on (in the needs array) were successful.
+          jq --exit-status 'all(.result == "success")' <<< '${{ toJson(needs) }}'
diff --git a/src/tools/clippy/CHANGELOG.md b/src/tools/clippy/CHANGELOG.md
index 4bdbc91db93..dd3124ee9a3 100644
--- a/src/tools/clippy/CHANGELOG.md
+++ b/src/tools/clippy/CHANGELOG.md
@@ -5331,6 +5331,7 @@ Released 2018-09-13
 [`almost_complete_range`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_complete_range
 [`almost_swapped`]: https://rust-lang.github.io/rust-clippy/master/index.html#almost_swapped
 [`approx_constant`]: https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
+[`arbitrary_source_item_ordering`]: https://rust-lang.github.io/rust-clippy/master/index.html#arbitrary_source_item_ordering
 [`arc_with_non_send_sync`]: https://rust-lang.github.io/rust-clippy/master/index.html#arc_with_non_send_sync
 [`arithmetic_side_effects`]: https://rust-lang.github.io/rust-clippy/master/index.html#arithmetic_side_effects
 [`as_conversions`]: https://rust-lang.github.io/rust-clippy/master/index.html#as_conversions
@@ -5689,6 +5690,7 @@ Released 2018-09-13
 [`manual_unwrap_or_default`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_unwrap_or_default
 [`manual_while_let_some`]: https://rust-lang.github.io/rust-clippy/master/index.html#manual_while_let_some
 [`many_single_char_names`]: https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names
+[`map_all_any_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_all_any_identity
 [`map_clone`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_clone
 [`map_collect_result_unit`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_collect_result_unit
 [`map_entry`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_entry
@@ -5696,6 +5698,7 @@ Released 2018-09-13
 [`map_flatten`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_flatten
 [`map_identity`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_identity
 [`map_unwrap_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or
+[`map_with_unused_argument_over_ranges`]: https://rust-lang.github.io/rust-clippy/master/index.html#map_with_unused_argument_over_ranges
 [`match_as_ref`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_as_ref
 [`match_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_bool
 [`match_like_matches_macro`]: https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro
@@ -5761,6 +5764,7 @@ Released 2018-09-13
 [`mutex_integer`]: https://rust-lang.github.io/rust-clippy/master/index.html#mutex_integer
 [`naive_bytecount`]: https://rust-lang.github.io/rust-clippy/master/index.html#naive_bytecount
 [`needless_arbitrary_self_type`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_arbitrary_self_type
+[`needless_as_bytes`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_as_bytes
 [`needless_bitwise_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bitwise_bool
 [`needless_bool`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool
 [`needless_bool_assign`]: https://rust-lang.github.io/rust-clippy/master/index.html#needless_bool_assign
@@ -6067,6 +6071,7 @@ Released 2018-09-13
 [`unnecessary_literal_bound`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_bound
 [`unnecessary_literal_unwrap`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_literal_unwrap
 [`unnecessary_map_on_constructor`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_on_constructor
+[`unnecessary_map_or`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_map_or
 [`unnecessary_min_or_max`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_min_or_max
 [`unnecessary_mut_passed`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_mut_passed
 [`unnecessary_operation`]: https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_operation
@@ -6205,12 +6210,14 @@ Released 2018-09-13
 [`max-trait-bounds`]: https://doc.rust-lang.org/clippy/lint_configuration.html#max-trait-bounds
 [`min-ident-chars-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#min-ident-chars-threshold
 [`missing-docs-in-crate-items`]: https://doc.rust-lang.org/clippy/lint_configuration.html#missing-docs-in-crate-items
+[`module-item-order-groupings`]: https://doc.rust-lang.org/clippy/lint_configuration.html#module-item-order-groupings
 [`msrv`]: https://doc.rust-lang.org/clippy/lint_configuration.html#msrv
 [`pass-by-value-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pass-by-value-size-limit
 [`pub-underscore-fields-behavior`]: https://doc.rust-lang.org/clippy/lint_configuration.html#pub-underscore-fields-behavior
 [`semicolon-inside-block-ignore-singleline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-inside-block-ignore-singleline
 [`semicolon-outside-block-ignore-multiline`]: https://doc.rust-lang.org/clippy/lint_configuration.html#semicolon-outside-block-ignore-multiline
 [`single-char-binding-names-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#single-char-binding-names-threshold
+[`source-item-ordering`]: https://doc.rust-lang.org/clippy/lint_configuration.html#source-item-ordering
 [`stack-size-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#stack-size-threshold
 [`standard-macro-braces`]: https://doc.rust-lang.org/clippy/lint_configuration.html#standard-macro-braces
 [`struct-field-name-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#struct-field-name-threshold
@@ -6218,6 +6225,7 @@ Released 2018-09-13
 [`too-large-for-stack`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-large-for-stack
 [`too-many-arguments-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-arguments-threshold
 [`too-many-lines-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#too-many-lines-threshold
+[`trait-assoc-item-kinds-order`]: https://doc.rust-lang.org/clippy/lint_configuration.html#trait-assoc-item-kinds-order
 [`trivial-copy-size-limit`]: https://doc.rust-lang.org/clippy/lint_configuration.html#trivial-copy-size-limit
 [`type-complexity-threshold`]: https://doc.rust-lang.org/clippy/lint_configuration.html#type-complexity-threshold
 [`unnecessary-box-size`]: https://doc.rust-lang.org/clippy/lint_configuration.html#unnecessary-box-size
diff --git a/src/tools/clippy/CONTRIBUTING.md b/src/tools/clippy/CONTRIBUTING.md
index b1a59238c82..1f6c918fc6c 100644
--- a/src/tools/clippy/CONTRIBUTING.md
+++ b/src/tools/clippy/CONTRIBUTING.md
@@ -21,7 +21,6 @@ All contributors are expected to follow the [Rust Code of Conduct].
     - [Rust Analyzer](#rust-analyzer)
   - [How Clippy works](#how-clippy-works)
   - [Issue and PR triage](#issue-and-pr-triage)
-  - [Bors and Homu](#bors-and-homu)
   - [Contributions](#contributions)
   - [License](#license)
 
@@ -213,16 +212,6 @@ We have prioritization labels and a sync-blocker label, which are described belo
 Or rather: before the sync this should be addressed,
 e.g. by removing a lint again, so it doesn't hit beta/stable.
 
-## Bors and Homu
-
-We use a bot powered by [Homu][homu] to help automate testing and landing of pull
-requests in Clippy. The bot's username is @bors.
-
-You can find the Clippy bors queue [here][homu_queue].
-
-If you have @bors permissions, you can find an overview of the available
-commands [here][homu_instructions].
-
 [triage]: https://forge.rust-lang.org/release/triage-procedure.html
 [l-crash]: https://github.com/rust-lang/rust-clippy/labels/L-crash
 [l-bug]: https://github.com/rust-lang/rust-clippy/labels/L-bug
@@ -230,9 +219,6 @@ commands [here][homu_instructions].
 [p-medium]: https://github.com/rust-lang/rust-clippy/labels/P-medium
 [p-high]: https://github.com/rust-lang/rust-clippy/labels/P-high
 [l-sync-blocker]: https://github.com/rust-lang/rust-clippy/labels/L-sync-blocker
-[homu]: https://github.com/rust-lang/homu
-[homu_instructions]: https://bors.rust-lang.org/
-[homu_queue]: https://bors.rust-lang.org/queue/clippy
 
 ## Contributions
 
@@ -244,7 +230,7 @@ All PRs should include a `changelog` entry with a short comment explaining the c
 "what do you believe is important from an outsider's perspective?" Often, PRs are only related to a single property of a
 lint, and then it's good to mention that one. Otherwise, it's better to include too much detail than too little.
 
-Clippy's [changelog] is created from these comments. Every release, someone gets all commits from bors with a
+Clippy's [changelog] is created from these comments. Every release, someone gets all merge commits with a
 `changelog: XYZ` entry and combines them into the changelog. This is a manual process.
 
 Examples:
diff --git a/src/tools/clippy/Cargo.toml b/src/tools/clippy/Cargo.toml
index 1f7784fc489..50a2afbfc07 100644
--- a/src/tools/clippy/Cargo.toml
+++ b/src/tools/clippy/Cargo.toml
@@ -39,7 +39,7 @@ toml = "0.7.3"
 walkdir = "2.3"
 filetime = "0.2.9"
 itertools = "0.12"
-pulldown-cmark = "0.11"
+pulldown-cmark = { version = "0.11", default-features = false, features = ["html"] }
 rinja = { version = "0.3", default-features = false, features = ["config"] }
 
 # UI test dependencies
diff --git a/src/tools/clippy/README.md b/src/tools/clippy/README.md
index ec76a6dfb08..cb3a22d4288 100644
--- a/src/tools/clippy/README.md
+++ b/src/tools/clippy/README.md
@@ -1,11 +1,10 @@
 # Clippy
 
-[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test%20(bors)/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test+(bors)%22+event%3Apush+branch%3Aauto)
 [![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](#license)
 
 A collection of lints to catch common mistakes and improve your [Rust](https://github.com/rust-lang/rust) code.
 
-[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
+[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
 
 Lints are divided into categories, each with a default [lint level](https://doc.rust-lang.org/rustc/lints/levels.html).
 You can choose how much Clippy is supposed to ~~annoy~~ help you by changing the lint level by category.
diff --git a/src/tools/clippy/book/src/README.md b/src/tools/clippy/book/src/README.md
index 7bdfb97c3ac..5d2c3972b06 100644
--- a/src/tools/clippy/book/src/README.md
+++ b/src/tools/clippy/book/src/README.md
@@ -1,12 +1,11 @@
 # Clippy
 
-[![Clippy Test](https://github.com/rust-lang/rust-clippy/workflows/Clippy%20Test%20(bors)/badge.svg?branch=auto&event=push)](https://github.com/rust-lang/rust-clippy/actions?query=workflow%3A%22Clippy+Test+(bors)%22+event%3Apush+branch%3Aauto)
 [![License: MIT OR Apache-2.0](https://img.shields.io/crates/l/clippy.svg)](https://github.com/rust-lang/rust-clippy#license)
 
 A collection of lints to catch common mistakes and improve your
 [Rust](https://github.com/rust-lang/rust) code.
 
-[There are over 700 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
+[There are over 750 lints included in this crate!](https://rust-lang.github.io/rust-clippy/master/index.html)
 
 Lints are divided into categories, each with a default [lint
 level](https://doc.rust-lang.org/rustc/lints/levels.html). You can choose how
diff --git a/src/tools/clippy/book/src/development/README.md b/src/tools/clippy/book/src/development/README.md
index 8f09f66f595..b33cdc00ead 100644
--- a/src/tools/clippy/book/src/development/README.md
+++ b/src/tools/clippy/book/src/development/README.md
@@ -53,7 +53,6 @@ book](../lints.md).
 > - IDE setup
 > - High level overview on how Clippy works
 > - Triage procedure
-> - Bors and Homu
 
 [ast]: https://rustc-dev-guide.rust-lang.org/syntax-intro.html
 [hir]: https://rustc-dev-guide.rust-lang.org/hir.html
diff --git a/src/tools/clippy/book/src/development/common_tools_writing_lints.md b/src/tools/clippy/book/src/development/common_tools_writing_lints.md
index 09171d86a20..77910917963 100644
--- a/src/tools/clippy/book/src/development/common_tools_writing_lints.md
+++ b/src/tools/clippy/book/src/development/common_tools_writing_lints.md
@@ -68,7 +68,7 @@ impl<'tcx> LateLintPass<'tcx> for MyStructLint {
         // Check our expr is calling a method
         if let hir::ExprKind::MethodCall(path, _, _self_arg, ..) = &expr.kind
             // Check the name of this method is `some_method`
-            && path.ident.name == sym!(some_method)
+            && path.ident.name.as_str() == "some_method"
             // Optionally, check the type of the self argument.
             // - See "Checking for a specific type"
         {
@@ -167,7 +167,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
         // Check if item is a method/function
         if let ImplItemKind::Fn(ref signature, _) = impl_item.kind
             // Check the method is named `some_method`
-            && impl_item.ident.name == sym!(some_method)
+            && impl_item.ident.name.as_str() == "some_method"
             // We can also check it has a parameter `self`
             && signature.decl.implicit_self.has_implicit_self()
             // We can go further and even check if its return type is `String`
diff --git a/src/tools/clippy/book/src/development/method_checking.md b/src/tools/clippy/book/src/development/method_checking.md
index 56d1be37519..9c5d4b516db 100644
--- a/src/tools/clippy/book/src/development/method_checking.md
+++ b/src/tools/clippy/book/src/development/method_checking.md
@@ -3,8 +3,8 @@
 In some scenarios we might want to check for methods when developing
 a lint. There are two kinds of questions that we might be curious about:
 
-- Invocation: Does an expression call a specific method?
-- Definition: Does an `impl` define a method?
+-   Invocation: Does an expression call a specific method?
+-   Definition: Does an `impl` define a method?
 
 ## Checking if an `expr` is calling a specific method
 
@@ -23,7 +23,7 @@ impl<'tcx> LateLintPass<'tcx> for OurFancyMethodLint {
         // Check our expr is calling a method with pattern matching
         if let hir::ExprKind::MethodCall(path, _, [self_arg, ..]) = &expr.kind
             // Check if the name of this method is `our_fancy_method`
-            && path.ident.name == sym!(our_fancy_method)
+            && path.ident.name.as_str() == "our_fancy_method"
             // We can check the type of the self argument whenever necessary.
             // (It's necessary if we want to check that method is specifically belonging to a specific trait,
             // for example, a `map` method could belong to user-defined trait instead of to `Iterator`)
@@ -41,10 +41,6 @@ information on the pattern matching. As mentioned in [Define
 Lints](defining_lints.md#lint-types), the `methods` lint type is full of pattern
 matching with `MethodCall` in case the reader wishes to explore more.
 
-Additionally, we use the [`clippy_utils::sym!`][sym] macro to conveniently
-convert an input `our_fancy_method` into a `Symbol` and compare that symbol to
-the [`Ident`]'s name in the [`PathSegment`] in the [`MethodCall`].
-
 ## Checking if a `impl` block implements a method
 
 While sometimes we want to check whether a method is being called or not, other
@@ -71,7 +67,7 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
         // Check if item is a method/function
         if let ImplItemKind::Fn(ref signature, _) = impl_item.kind
             // Check the method is named `our_fancy_method`
-            && impl_item.ident.name == sym!(our_fancy_method)
+            && impl_item.ident.name.as_str() == "our_fancy_method"
             // We can also check it has a parameter `self`
             && signature.decl.implicit_self.has_implicit_self()
             // We can go even further and even check if its return type is `String`
@@ -85,9 +81,6 @@ impl<'tcx> LateLintPass<'tcx> for MyTypeImpl {
 
 [`check_impl_item`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_lint/trait.LateLintPass.html#method.check_impl_item
 [`ExprKind`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/hir/enum.ExprKind.html
-[`Ident`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_span/symbol/struct.Ident.html
 [`ImplItem`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_hir/hir/struct.ImplItem.html
 [`LateLintPass`]: https://doc.rust-lang.org/stable/nightly-rustc/rustc_lint/trait.LateLintPass.html
 [`MethodCall`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/hir/enum.ExprKind.html#variant.MethodCall
-[`PathSegment`]: https://doc.rust-lang.org/beta/nightly-rustc/rustc_hir/hir/struct.PathSegment.html
-[sym]: https://doc.rust-lang.org/stable/nightly-rustc/clippy_utils/macro.sym.html
diff --git a/src/tools/clippy/book/src/lint_configuration.md b/src/tools/clippy/book/src/lint_configuration.md
index 43b551ae216..670b5cbef82 100644
--- a/src/tools/clippy/book/src/lint_configuration.md
+++ b/src/tools/clippy/book/src/lint_configuration.md
@@ -456,7 +456,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", "AccessKit", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]`
+**Default Value:** `["KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "MHz", "GHz", "THz", "AccessKit", "CoAP", "CoreFoundation", "CoreGraphics", "CoreText", "DevOps", "Direct2D", "Direct3D", "DirectWrite", "DirectX", "ECMAScript", "GPLv2", "GPLv3", "GitHub", "GitLab", "IPv4", "IPv6", "ClojureScript", "CoffeeScript", "JavaScript", "PostScript", "PureScript", "TypeScript", "WebAssembly", "NaN", "NaNs", "OAuth", "GraphQL", "OCaml", "OpenAL", "OpenDNS", "OpenGL", "OpenMP", "OpenSSH", "OpenSSL", "OpenStreetMap", "OpenTelemetry", "OpenType", "WebGL", "WebGL2", "WebGPU", "WebRTC", "WebSocket", "WebTransport", "WebP", "OpenExr", "YCbCr", "sRGB", "TensorFlow", "TrueType", "iOS", "macOS", "FreeBSD", "NetBSD", "OpenBSD", "TeX", "LaTeX", "BibTeX", "BibLaTeX", "MinGW", "CamelCase"]`
 
 ---
 **Affected lints:**
@@ -666,6 +666,16 @@ crate. For example, `pub(crate)` items.
 * [`missing_docs_in_private_items`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_docs_in_private_items)
 
 
+## `module-item-order-groupings`
+The named groupings of different source item kinds within modules.
+
+**Default Value:** `[["modules", ["extern_crate", "mod", "foreign_mod"]], ["use", ["use"]], ["macros", ["macro"]], ["global_asm", ["global_asm"]], ["UPPER_SNAKE_CASE", ["static", "const"]], ["PascalCase", ["ty_alias", "enum", "struct", "union", "trait", "trait_alias", "impl"]], ["lower_snake_case", ["fn"]]]`
+
+---
+**Affected lints:**
+* [`arbitrary_source_item_ordering`](https://rust-lang.github.io/rust-clippy/master/index.html#arbitrary_source_item_ordering)
+
+
 ## `msrv`
 The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml`
 
@@ -710,6 +720,7 @@ The minimum rust version that the project supports. Defaults to the `rust-versio
 * [`manual_try_fold`](https://rust-lang.github.io/rust-clippy/master/index.html#manual_try_fold)
 * [`map_clone`](https://rust-lang.github.io/rust-clippy/master/index.html#map_clone)
 * [`map_unwrap_or`](https://rust-lang.github.io/rust-clippy/master/index.html#map_unwrap_or)
+* [`map_with_unused_argument_over_ranges`](https://rust-lang.github.io/rust-clippy/master/index.html#map_with_unused_argument_over_ranges)
 * [`match_like_matches_macro`](https://rust-lang.github.io/rust-clippy/master/index.html#match_like_matches_macro)
 * [`mem_replace_with_default`](https://rust-lang.github.io/rust-clippy/master/index.html#mem_replace_with_default)
 * [`missing_const_for_fn`](https://rust-lang.github.io/rust-clippy/master/index.html#missing_const_for_fn)
@@ -783,6 +794,16 @@ The maximum number of single char bindings a scope may have
 * [`many_single_char_names`](https://rust-lang.github.io/rust-clippy/master/index.html#many_single_char_names)
 
 
+## `source-item-ordering`
+Which kind of elements should be ordered internally, possible values being `enum`, `impl`, `module`, `struct`, `trait`.
+
+**Default Value:** `["enum", "impl", "module", "struct", "trait"]`
+
+---
+**Affected lints:**
+* [`arbitrary_source_item_ordering`](https://rust-lang.github.io/rust-clippy/master/index.html#arbitrary_source_item_ordering)
+
+
 ## `stack-size-threshold`
 The maximum allowed stack size for functions in bytes
 
@@ -862,6 +883,16 @@ The maximum number of lines a function or method can have
 * [`too_many_lines`](https://rust-lang.github.io/rust-clippy/master/index.html#too_many_lines)
 
 
+## `trait-assoc-item-kinds-order`
+The order of associated items in traits.
+
+**Default Value:** `["const", "type", "fn"]`
+
+---
+**Affected lints:**
+* [`arbitrary_source_item_ordering`](https://rust-lang.github.io/rust-clippy/master/index.html#arbitrary_source_item_ordering)
+
+
 ## `trivial-copy-size-limit`
 The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by
 reference. By default there is no limit
diff --git a/src/tools/clippy/clippy_config/src/conf.rs b/src/tools/clippy/clippy_config/src/conf.rs
index 4757c0b1339..600d5b6e2c8 100644
--- a/src/tools/clippy/clippy_config/src/conf.rs
+++ b/src/tools/clippy/clippy_config/src/conf.rs
@@ -1,6 +1,10 @@
 use crate::ClippyConfiguration;
 use crate::msrvs::Msrv;
-use crate::types::{DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename};
+use crate::types::{
+    DisallowedPath, MacroMatcher, MatchLintBehaviour, PubUnderscoreFieldsBehaviour, Rename, SourceItemOrdering,
+    SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind,
+    SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds,
+};
 use rustc_errors::Applicability;
 use rustc_session::Session;
 use rustc_span::edit_distance::edit_distance;
@@ -17,8 +21,9 @@ use std::{cmp, env, fmt, fs, io};
 #[rustfmt::skip]
 const DEFAULT_DOC_VALID_IDENTS: &[&str] = &[
     "KiB", "MiB", "GiB", "TiB", "PiB", "EiB",
+    "MHz", "GHz", "THz",
     "AccessKit",
-    "CoreFoundation", "CoreGraphics", "CoreText",
+    "CoAP", "CoreFoundation", "CoreGraphics", "CoreText",
     "DevOps",
     "Direct2D", "Direct3D", "DirectWrite", "DirectX",
     "ECMAScript",
@@ -46,6 +51,29 @@ const DEFAULT_ALLOWED_IDENTS_BELOW_MIN_CHARS: &[&str] = &["i", "j", "x", "y", "z
 const DEFAULT_ALLOWED_PREFIXES: &[&str] = &["to", "as", "into", "from", "try_into", "try_from"];
 const DEFAULT_ALLOWED_TRAITS_WITH_RENAMED_PARAMS: &[&str] =
     &["core::convert::From", "core::convert::TryFrom", "core::str::FromStr"];
+const DEFAULT_MODULE_ITEM_ORDERING_GROUPS: &[(&str, &[SourceItemOrderingModuleItemKind])] = {
+    #[allow(clippy::enum_glob_use)] // Very local glob use for legibility.
+    use SourceItemOrderingModuleItemKind::*;
+    &[
+        ("modules", &[ExternCrate, Mod, ForeignMod]),
+        ("use", &[Use]),
+        ("macros", &[Macro]),
+        ("global_asm", &[GlobalAsm]),
+        ("UPPER_SNAKE_CASE", &[Static, Const]),
+        ("PascalCase", &[TyAlias, Enum, Struct, Union, Trait, TraitAlias, Impl]),
+        ("lower_snake_case", &[Fn]),
+    ]
+};
+const DEFAULT_TRAIT_ASSOC_ITEM_KINDS_ORDER: &[SourceItemOrderingTraitAssocItemKind] = {
+    #[allow(clippy::enum_glob_use)] // Very local glob use for legibility.
+    use SourceItemOrderingTraitAssocItemKind::*;
+    &[Const, Type, Fn]
+};
+const DEFAULT_SOURCE_ITEM_ORDERING: &[SourceItemOrderingCategory] = {
+    #[allow(clippy::enum_glob_use)] // Very local glob use for legibility.
+    use SourceItemOrderingCategory::*;
+    &[Enum, Impl, Module, Struct, Trait]
+};
 
 /// Conf with parse errors
 #[derive(Default)]
@@ -102,7 +130,9 @@ pub fn sanitize_explanation(raw_docs: &str) -> String {
     // Remove tags and hidden code:
     let mut explanation = String::with_capacity(128);
     let mut in_code = false;
-    for line in raw_docs.lines().map(str::trim) {
+    for line in raw_docs.lines() {
+        let line = line.strip_prefix(' ').unwrap_or(line);
+
         if let Some(lang) = line.strip_prefix("```") {
             let tag = lang.split_once(',').map_or(lang, |(left, _)| left);
             if !in_code && matches!(tag, "" | "rust" | "ignore" | "should_panic" | "no_run" | "compile_fail") {
@@ -530,6 +560,9 @@ define_Conf! {
     /// crate. For example, `pub(crate)` items.
     #[lints(missing_docs_in_private_items)]
     missing_docs_in_crate_items: bool = false,
+    /// The named groupings of different source item kinds within modules.
+    #[lints(arbitrary_source_item_ordering)]
+    module_item_order_groupings: SourceItemOrderingModuleItemGroupings = DEFAULT_MODULE_ITEM_ORDERING_GROUPS.into(),
     /// The minimum rust version that the project supports. Defaults to the `rust-version` field in `Cargo.toml`
     #[default_text = "current version"]
     #[lints(
@@ -570,6 +603,7 @@ define_Conf! {
         manual_try_fold,
         map_clone,
         map_unwrap_or,
+        map_with_unused_argument_over_ranges,
         match_like_matches_macro,
         mem_replace_with_default,
         missing_const_for_fn,
@@ -608,6 +642,9 @@ define_Conf! {
     /// The maximum number of single char bindings a scope may have
     #[lints(many_single_char_names)]
     single_char_binding_names_threshold: u64 = 4,
+    /// Which kind of elements should be ordered internally, possible values being `enum`, `impl`, `module`, `struct`, `trait`.
+    #[lints(arbitrary_source_item_ordering)]
+    source_item_ordering: SourceItemOrdering = DEFAULT_SOURCE_ITEM_ORDERING.into(),
     /// The maximum allowed stack size for functions in bytes
     #[lints(large_stack_frames)]
     stack_size_threshold: u64 = 512_000,
@@ -637,6 +674,9 @@ define_Conf! {
     /// The maximum number of lines a function or method can have
     #[lints(too_many_lines)]
     too_many_lines_threshold: u64 = 100,
+    /// The order of associated items in traits.
+    #[lints(arbitrary_source_item_ordering)]
+    trait_assoc_item_kinds_order: SourceItemOrderingTraitAssocItemKinds = DEFAULT_TRAIT_ASSOC_ITEM_KINDS_ORDER.into(),
     /// The maximum size (in bytes) to consider a `Copy` type for passing by value instead of by
     /// reference. By default there is no limit
     #[default_text = "target_pointer_width * 2"]
diff --git a/src/tools/clippy/clippy_config/src/lib.rs b/src/tools/clippy/clippy_config/src/lib.rs
index 42651521f8d..1c3f32c2514 100644
--- a/src/tools/clippy/clippy_config/src/lib.rs
+++ b/src/tools/clippy/clippy_config/src/lib.rs
@@ -20,6 +20,7 @@ extern crate rustc_driver;
 extern crate rustc_errors;
 extern crate rustc_session;
 extern crate rustc_span;
+extern crate smallvec;
 
 mod conf;
 mod metadata;
diff --git a/src/tools/clippy/clippy_config/src/msrvs.rs b/src/tools/clippy/clippy_config/src/msrvs.rs
index 2f4da4cba3d..764ca8fb50a 100644
--- a/src/tools/clippy/clippy_config/src/msrvs.rs
+++ b/src/tools/clippy/clippy_config/src/msrvs.rs
@@ -3,6 +3,7 @@ use rustc_attr::parse_version;
 use rustc_session::{RustcVersion, Session};
 use rustc_span::{Symbol, sym};
 use serde::Deserialize;
+use smallvec::{SmallVec, smallvec};
 use std::fmt;
 
 macro_rules! msrv_aliases {
@@ -18,7 +19,7 @@ macro_rules! msrv_aliases {
 // names may refer to stabilized feature flags or library items
 msrv_aliases! {
     1,83,0 { CONST_EXTERN_FN, CONST_FLOAT_BITS_CONV, CONST_FLOAT_CLASSIFY }
-    1,82,0 { IS_NONE_OR }
+    1,82,0 { IS_NONE_OR, REPEAT_N }
     1,81,0 { LINT_REASONS_STABILIZATION }
     1,80,0 { BOX_INTO_ITER}
     1,77,0 { C_STR_LITERALS }
@@ -54,7 +55,7 @@ msrv_aliases! {
     1,33,0 { UNDERSCORE_IMPORTS }
     1,30,0 { ITERATOR_FIND_MAP, TOOL_ATTRIBUTES }
     1,29,0 { ITER_FLATTEN }
-    1,28,0 { FROM_BOOL }
+    1,28,0 { FROM_BOOL, REPEAT_WITH }
     1,27,0 { ITERATOR_TRY_FOLD }
     1,26,0 { RANGE_INCLUSIVE, STRING_RETAIN }
     1,24,0 { IS_ASCII_DIGIT }
@@ -67,7 +68,7 @@ msrv_aliases! {
 /// Tracks the current MSRV from `clippy.toml`, `Cargo.toml` or set via `#[clippy::msrv]`
 #[derive(Debug, Clone)]
 pub struct Msrv {
-    stack: Vec<RustcVersion>,
+    stack: SmallVec<[RustcVersion; 2]>,
 }
 
 impl fmt::Display for Msrv {
@@ -87,14 +88,14 @@ impl<'de> Deserialize<'de> for Msrv {
     {
         let v = String::deserialize(deserializer)?;
         parse_version(Symbol::intern(&v))
-            .map(|v| Msrv { stack: vec![v] })
+            .map(|v| Msrv { stack: smallvec![v] })
             .ok_or_else(|| serde::de::Error::custom("not a valid Rust version"))
     }
 }
 
 impl Msrv {
     pub fn empty() -> Msrv {
-        Msrv { stack: Vec::new() }
+        Msrv { stack: SmallVec::new() }
     }
 
     pub fn read_cargo(&mut self, sess: &Session) {
@@ -103,7 +104,7 @@ impl Msrv {
             .and_then(|v| parse_version(Symbol::intern(&v)));
 
         match (self.current(), cargo_msrv) {
-            (None, Some(cargo_msrv)) => self.stack = vec![cargo_msrv],
+            (None, Some(cargo_msrv)) => self.stack = smallvec![cargo_msrv],
             (Some(clippy_msrv), Some(cargo_msrv)) => {
                 if clippy_msrv != cargo_msrv {
                     sess.dcx().warn(format!(
diff --git a/src/tools/clippy/clippy_config/src/types.rs b/src/tools/clippy/clippy_config/src/types.rs
index bab63911182..fe576424148 100644
--- a/src/tools/clippy/clippy_config/src/types.rs
+++ b/src/tools/clippy/clippy_config/src/types.rs
@@ -1,5 +1,6 @@
 use serde::de::{self, Deserializer, Visitor};
 use serde::{Deserialize, Serialize, ser};
+use std::collections::HashMap;
 use std::fmt;
 
 #[derive(Debug, Deserialize)]
@@ -102,6 +103,306 @@ impl<'de> Deserialize<'de> for MacroMatcher {
     }
 }
 
+/// Represents the item categories that can be ordered by the source ordering lint.
+#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+#[serde(rename_all = "snake_case")]
+pub enum SourceItemOrderingCategory {
+    Enum,
+    Impl,
+    Module,
+    Struct,
+    Trait,
+}
+
+/// Represents which item categories are enabled for ordering.
+///
+/// The [`Deserialize`] implementation checks that there are no duplicates in
+/// the user configuration.
+pub struct SourceItemOrdering(Vec<SourceItemOrderingCategory>);
+
+impl SourceItemOrdering {
+    pub fn contains(&self, category: &SourceItemOrderingCategory) -> bool {
+        self.0.contains(category)
+    }
+}
+
+impl<T> From<T> for SourceItemOrdering
+where
+    T: Into<Vec<SourceItemOrderingCategory>>,
+{
+    fn from(value: T) -> Self {
+        Self(value.into())
+    }
+}
+
+impl core::fmt::Debug for SourceItemOrdering {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
+impl<'de> Deserialize<'de> for SourceItemOrdering {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let items = Vec::<SourceItemOrderingCategory>::deserialize(deserializer)?;
+        let mut items_set = std::collections::HashSet::new();
+
+        for item in &items {
+            if items_set.contains(item) {
+                return Err(de::Error::custom(format!(
+                    "The category \"{item:?}\" was enabled more than once in the source ordering configuration."
+                )));
+            }
+            items_set.insert(item);
+        }
+
+        Ok(Self(items))
+    }
+}
+
+impl Serialize for SourceItemOrdering {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: ser::Serializer,
+    {
+        self.0.serialize(serializer)
+    }
+}
+
+/// Represents the items that can occur within a module.
+#[derive(Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
+#[serde(rename_all = "snake_case")]
+pub enum SourceItemOrderingModuleItemKind {
+    ExternCrate,
+    Mod,
+    ForeignMod,
+    Use,
+    Macro,
+    GlobalAsm,
+    Static,
+    Const,
+    TyAlias,
+    Enum,
+    Struct,
+    Union,
+    Trait,
+    TraitAlias,
+    Impl,
+    Fn,
+}
+
+impl SourceItemOrderingModuleItemKind {
+    pub fn all_variants() -> Vec<Self> {
+        #[allow(clippy::enum_glob_use)] // Very local glob use for legibility.
+        use SourceItemOrderingModuleItemKind::*;
+        vec![
+            ExternCrate,
+            Mod,
+            ForeignMod,
+            Use,
+            Macro,
+            GlobalAsm,
+            Static,
+            Const,
+            TyAlias,
+            Enum,
+            Struct,
+            Union,
+            Trait,
+            TraitAlias,
+            Impl,
+            Fn,
+        ]
+    }
+}
+
+/// Represents the configured ordering of items within a module.
+///
+/// The [`Deserialize`] implementation checks that no item kinds have been
+/// omitted and that there are no duplicates in the user configuration.
+#[derive(Clone)]
+pub struct SourceItemOrderingModuleItemGroupings {
+    groups: Vec<(String, Vec<SourceItemOrderingModuleItemKind>)>,
+    lut: HashMap<SourceItemOrderingModuleItemKind, usize>,
+}
+
+impl SourceItemOrderingModuleItemGroupings {
+    fn build_lut(
+        groups: &[(String, Vec<SourceItemOrderingModuleItemKind>)],
+    ) -> HashMap<SourceItemOrderingModuleItemKind, usize> {
+        let mut lut = HashMap::new();
+        for (group_index, (_, items)) in groups.iter().enumerate() {
+            for item in items {
+                lut.insert(item.clone(), group_index);
+            }
+        }
+        lut
+    }
+
+    pub fn module_level_order_of(&self, item: &SourceItemOrderingModuleItemKind) -> Option<usize> {
+        self.lut.get(item).copied()
+    }
+}
+
+impl From<&[(&str, &[SourceItemOrderingModuleItemKind])]> for SourceItemOrderingModuleItemGroupings {
+    fn from(value: &[(&str, &[SourceItemOrderingModuleItemKind])]) -> Self {
+        let groups: Vec<(String, Vec<SourceItemOrderingModuleItemKind>)> =
+            value.iter().map(|item| (item.0.to_string(), item.1.to_vec())).collect();
+        let lut = Self::build_lut(&groups);
+        Self { groups, lut }
+    }
+}
+
+impl core::fmt::Debug for SourceItemOrderingModuleItemGroupings {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.groups.fmt(f)
+    }
+}
+
+impl<'de> Deserialize<'de> for SourceItemOrderingModuleItemGroupings {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let groups = Vec::<(String, Vec<SourceItemOrderingModuleItemKind>)>::deserialize(deserializer)?;
+        let items_total: usize = groups.iter().map(|(_, v)| v.len()).sum();
+        let lut = Self::build_lut(&groups);
+
+        let mut expected_items = SourceItemOrderingModuleItemKind::all_variants();
+        for item in lut.keys() {
+            expected_items.retain(|i| i != item);
+        }
+
+        let all_items = SourceItemOrderingModuleItemKind::all_variants();
+        if expected_items.is_empty() && items_total == all_items.len() {
+            let Some(use_group_index) = lut.get(&SourceItemOrderingModuleItemKind::Use) else {
+                return Err(de::Error::custom("Error in internal LUT."));
+            };
+            let Some((_, use_group_items)) = groups.get(*use_group_index) else {
+                return Err(de::Error::custom("Error in internal LUT."));
+            };
+            if use_group_items.len() > 1 {
+                return Err(de::Error::custom(
+                    "The group containing the \"use\" item kind may not contain any other item kinds. \
+                    The \"use\" items will (generally) be sorted by rustfmt already. \
+                    Therefore it makes no sense to implement linting rules that may conflict with rustfmt.",
+                ));
+            }
+
+            Ok(Self { groups, lut })
+        } else if items_total != all_items.len() {
+            Err(de::Error::custom(format!(
+                "Some module item kinds were configured more than once, or were missing, in the source ordering configuration. \
+                The module item kinds are: {all_items:?}"
+            )))
+        } else {
+            Err(de::Error::custom(format!(
+                "Not all module item kinds were part of the configured source ordering rule. \
+                All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \
+                The module item kinds are: {all_items:?}"
+            )))
+        }
+    }
+}
+
+impl Serialize for SourceItemOrderingModuleItemGroupings {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: ser::Serializer,
+    {
+        self.groups.serialize(serializer)
+    }
+}
+
+/// Represents all kinds of trait associated items.
+#[derive(Clone, Debug, Deserialize, PartialEq, PartialOrd, Serialize)]
+#[serde(rename_all = "snake_case")]
+pub enum SourceItemOrderingTraitAssocItemKind {
+    Const,
+    Fn,
+    Type,
+}
+
+impl SourceItemOrderingTraitAssocItemKind {
+    pub fn all_variants() -> Vec<Self> {
+        #[allow(clippy::enum_glob_use)] // Very local glob use for legibility.
+        use SourceItemOrderingTraitAssocItemKind::*;
+        vec![Const, Fn, Type]
+    }
+}
+
+/// Represents the order in which associated trait items should be ordered.
+///
+/// The reason to wrap a `Vec` in a newtype is to be able to implement
+/// [`Deserialize`]. Implementing `Deserialize` allows for implementing checks
+/// on configuration completeness at the time of loading the clippy config,
+/// letting the user know if there's any issues with the config (e.g. not
+/// listing all item kinds that should be sorted).
+#[derive(Clone)]
+pub struct SourceItemOrderingTraitAssocItemKinds(Vec<SourceItemOrderingTraitAssocItemKind>);
+
+impl SourceItemOrderingTraitAssocItemKinds {
+    pub fn index_of(&self, item: &SourceItemOrderingTraitAssocItemKind) -> Option<usize> {
+        self.0.iter().position(|i| i == item)
+    }
+}
+
+impl<T> From<T> for SourceItemOrderingTraitAssocItemKinds
+where
+    T: Into<Vec<SourceItemOrderingTraitAssocItemKind>>,
+{
+    fn from(value: T) -> Self {
+        Self(value.into())
+    }
+}
+
+impl core::fmt::Debug for SourceItemOrderingTraitAssocItemKinds {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        self.0.fmt(f)
+    }
+}
+
+impl<'de> Deserialize<'de> for SourceItemOrderingTraitAssocItemKinds {
+    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
+    where
+        D: Deserializer<'de>,
+    {
+        let items = Vec::<SourceItemOrderingTraitAssocItemKind>::deserialize(deserializer)?;
+
+        let mut expected_items = SourceItemOrderingTraitAssocItemKind::all_variants();
+        for item in &items {
+            expected_items.retain(|i| i != item);
+        }
+
+        let all_items = SourceItemOrderingTraitAssocItemKind::all_variants();
+        if expected_items.is_empty() && items.len() == all_items.len() {
+            Ok(Self(items))
+        } else if items.len() != all_items.len() {
+            Err(de::Error::custom(format!(
+                "Some trait associated item kinds were configured more than once, or were missing, in the source ordering configuration. \
+                The trait associated item kinds are: {all_items:?}",
+            )))
+        } else {
+            Err(de::Error::custom(format!(
+                "Not all trait associated item kinds were part of the configured source ordering rule. \
+                All item kinds must be provided in the config, otherwise the required source ordering would remain ambiguous. \
+                The trait associated item kinds are: {all_items:?}"
+            )))
+        }
+    }
+}
+
+impl Serialize for SourceItemOrderingTraitAssocItemKinds {
+    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
+    where
+        S: ser::Serializer,
+    {
+        self.0.serialize(serializer)
+    }
+}
+
 // these impls are never actually called but are used by the various config options that default to
 // empty lists
 macro_rules! unimplemented_serialize {
diff --git a/src/tools/clippy/clippy_dev/src/serve.rs b/src/tools/clippy/clippy_dev/src/serve.rs
index d367fefec61..a2d1236629f 100644
--- a/src/tools/clippy/clippy_dev/src/serve.rs
+++ b/src/tools/clippy/clippy_dev/src/serve.rs
@@ -20,8 +20,14 @@ pub fn run(port: u16, lint: Option<String>) -> ! {
 
     loop {
         let index_time = mtime("util/gh-pages/index.html");
+        let times = [
+            "clippy_lints/src",
+            "util/gh-pages/index_template.html",
+            "tests/compile-test.rs",
+        ]
+        .map(mtime);
 
-        if index_time < mtime("clippy_lints/src") || index_time < mtime("util/gh-pages/index_template.html") {
+        if times.iter().any(|&time| index_time < time) {
             Command::new(env::var("CARGO").unwrap_or("cargo".into()))
                 .arg("collect-metadata")
                 .spawn()
diff --git a/src/tools/clippy/clippy_dev/src/setup/vscode.rs b/src/tools/clippy/clippy_dev/src/setup/vscode.rs
index 204f4af2cf1..a37c873eed4 100644
--- a/src/tools/clippy/clippy_dev/src/setup/vscode.rs
+++ b/src/tools/clippy/clippy_dev/src/setup/vscode.rs
@@ -84,7 +84,7 @@ fn delete_vs_task_file(path: &Path) -> bool {
 /// It may fail silently.
 fn try_delete_vs_directory_if_empty() {
     let path = Path::new(VSCODE_DIR);
-    if path.read_dir().map_or(false, |mut iter| iter.next().is_none()) {
+    if path.read_dir().is_ok_and(|mut iter| iter.next().is_none()) {
         // The directory is empty. We just try to delete it but allow a silence
         // fail as an empty `.vscode` directory is still valid
         let _silence_result = fs::remove_dir(path);
diff --git a/src/tools/clippy/clippy_dev/src/update_lints.rs b/src/tools/clippy/clippy_dev/src/update_lints.rs
index d6ed36d52f4..795456ad3c5 100644
--- a/src/tools/clippy/clippy_dev/src/update_lints.rs
+++ b/src/tools/clippy/clippy_dev/src/update_lints.rs
@@ -762,13 +762,19 @@ fn parse_contents(contents: &str, module: &str, lints: &mut Vec<Lint>) {
             Literal{..}(desc)
         );
 
-        if let Some(LintDeclSearchResult {
-            token_kind: TokenKind::CloseBrace,
-            range,
-            ..
-        }) = iter.next()
-        {
-            lints.push(Lint::new(name, group, desc, module, start..range.end));
+        if let Some(end) = iter.find_map(|t| {
+            if let LintDeclSearchResult {
+                token_kind: TokenKind::CloseBrace,
+                range,
+                ..
+            } = t
+            {
+                Some(range.end)
+            } else {
+                None
+            }
+        }) {
+            lints.push(Lint::new(name, group, desc, module, start..end));
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
new file mode 100644
index 00000000000..8719f61a890
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/arbitrary_source_item_ordering.rs
@@ -0,0 +1,531 @@
+use clippy_config::Conf;
+use clippy_config::types::{
+    SourceItemOrderingCategory, SourceItemOrderingModuleItemGroupings, SourceItemOrderingModuleItemKind,
+    SourceItemOrderingTraitAssocItemKind, SourceItemOrderingTraitAssocItemKinds,
+};
+use clippy_utils::diagnostics::span_lint_and_note;
+use rustc_hir::{
+    AssocItemKind, FieldDef, HirId, ImplItemRef, IsAuto, Item, ItemKind, Mod, QPath, TraitItemRef, TyKind, UseKind,
+    Variant, VariantData,
+};
+use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::lint::in_external_macro;
+use rustc_session::impl_lint_pass;
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Confirms that items are sorted in source files as per configuration.
+    ///
+    /// ### Why restrict this?
+    ///
+    /// Keeping a consistent ordering throughout the codebase helps with working
+    /// as a team, and possibly improves maintainability of the codebase. The
+    /// idea is that by defining a consistent and enforceable rule for how
+    /// source files are structured, less time will be wasted during reviews on
+    /// a topic that is (under most circumstances) not relevant to the logic
+    /// implemented in the code. Sometimes this will be referred to as
+    /// "bikeshedding".
+    ///
+    /// ### Default Ordering and Configuration
+    ///
+    /// As there is no generally applicable rule, and each project may have
+    /// different requirements, the lint can be configured with high
+    /// granularity. The configuration is split into two stages:
+    ///
+    /// 1. Which item kinds that should have an internal order enforced.
+    /// 2. Individual ordering rules per item kind.
+    ///
+    /// The item kinds that can be linted are:
+    /// - Module (with customized groupings, alphabetical within)
+    /// - Trait (with customized order of associated items, alphabetical within)
+    /// - Enum, Impl, Struct (purely alphabetical)
+    ///
+    /// #### Module Item Order
+    ///
+    /// Due to the large variation of items within modules, the ordering can be
+    /// configured on a very granular level. Item kinds can be grouped together
+    /// arbitrarily, items within groups will be ordered alphabetically. The
+    /// following table shows the default groupings:
+    ///
+    /// | Group              | Item Kinds           |
+    /// |--------------------|----------------------|
+    /// | `modules`          | "mod", "foreign_mod" |
+    /// | `use`              | "use"                |
+    /// | `macros`           | "macro"              |
+    /// | `global_asm`       | "global_asm"         |
+    /// | `UPPER_SNAKE_CASE` | "static", "const"    |
+    /// | `PascalCase`       | "ty_alias", "opaque_ty", "enum", "struct", "union", "trait", "trait_alias", "impl" |
+    /// | `lower_snake_case` | "fn"                 |
+    ///
+    /// All item kinds must be accounted for to create an enforceable linting
+    /// rule set.
+    ///
+    /// ### Known Problems
+    ///
+    /// #### Performance Impact
+    ///
+    /// Keep in mind, that ordering source code alphabetically can lead to
+    /// reduced performance in cases where the most commonly used enum variant
+    /// isn't the first entry anymore, and similar optimizations that can reduce
+    /// branch misses, cache locality and such. Either don't use this lint if
+    /// that's relevant, or disable the lint in modules or items specifically
+    /// where it matters. Other solutions can be to use profile guided
+    /// optimization (PGO), post-link optimization (e.g. using BOLT for LLVM),
+    /// or other advanced optimization methods. A good starting point to dig
+    /// into optimization is [cargo-pgo][cargo-pgo].
+    ///
+    /// #### Lints on a Contains basis
+    ///
+    /// The lint can be disabled only on a "contains" basis, but not per element
+    /// within a "container", e.g. the lint works per-module, per-struct,
+    /// per-enum, etc. but not for "don't order this particular enum variant".
+    ///
+    /// #### Module documentation
+    ///
+    /// Module level rustdoc comments are not part of the resulting syntax tree
+    /// and as such cannot be linted from within `check_mod`. Instead, the
+    /// `rustdoc::missing_documentation` lint may be used.
+    ///
+    /// #### Module Tests
+    ///
+    /// This lint does not implement detection of module tests (or other feature
+    /// dependent elements for that matter). To lint the location of mod tests,
+    /// the lint `items_after_test_module` can be used instead.
+    ///
+    /// ### Example
+    ///
+    /// ```no_run
+    /// trait TraitUnordered {
+    ///     const A: bool;
+    ///     const C: bool;
+    ///     const B: bool;
+    ///
+    ///     type SomeType;
+    ///
+    ///     fn a();
+    ///     fn c();
+    ///     fn b();
+    /// }
+    /// ```
+    ///
+    /// Use instead:
+    /// ```no_run
+    /// trait TraitOrdered {
+    ///     const A: bool;
+    ///     const B: bool;
+    ///     const C: bool;
+    ///
+    ///     type SomeType;
+    ///
+    ///     fn a();
+    ///     fn b();
+    ///     fn c();
+    /// }
+    /// ```
+    ///
+    /// [cargo-pgo]: https://github.com/Kobzol/cargo-pgo/blob/main/README.md
+    ///
+    #[clippy::version = "1.82.0"]
+    pub ARBITRARY_SOURCE_ITEM_ORDERING,
+    restriction,
+    "arbitrary source item ordering"
+}
+
+impl_lint_pass!(ArbitrarySourceItemOrdering => [ARBITRARY_SOURCE_ITEM_ORDERING]);
+
+#[derive(Debug)]
+#[allow(clippy::struct_excessive_bools)] // Bools are cached feature flags.
+pub struct ArbitrarySourceItemOrdering {
+    assoc_types_order: SourceItemOrderingTraitAssocItemKinds,
+    enable_ordering_for_enum: bool,
+    enable_ordering_for_impl: bool,
+    enable_ordering_for_module: bool,
+    enable_ordering_for_struct: bool,
+    enable_ordering_for_trait: bool,
+    module_item_order_groupings: SourceItemOrderingModuleItemGroupings,
+}
+
+impl ArbitrarySourceItemOrdering {
+    pub fn new(conf: &'static Conf) -> Self {
+        #[allow(clippy::enum_glob_use)] // Very local glob use for legibility.
+        use SourceItemOrderingCategory::*;
+        Self {
+            assoc_types_order: conf.trait_assoc_item_kinds_order.clone(),
+            enable_ordering_for_enum: conf.source_item_ordering.contains(&Enum),
+            enable_ordering_for_impl: conf.source_item_ordering.contains(&Impl),
+            enable_ordering_for_module: conf.source_item_ordering.contains(&Module),
+            enable_ordering_for_struct: conf.source_item_ordering.contains(&Struct),
+            enable_ordering_for_trait: conf.source_item_ordering.contains(&Trait),
+            module_item_order_groupings: conf.module_item_order_groupings.clone(),
+        }
+    }
+
+    /// Produces a linting warning for incorrectly ordered impl items.
+    fn lint_impl_item<T: LintContext>(&self, cx: &T, item: &ImplItemRef, before_item: &ImplItemRef) {
+        span_lint_and_note(
+            cx,
+            ARBITRARY_SOURCE_ITEM_ORDERING,
+            item.span,
+            format!(
+                "incorrect ordering of impl items (defined order: {:?})",
+                self.assoc_types_order
+            ),
+            Some(before_item.span),
+            format!("should be placed before `{}`", before_item.ident.as_str(),),
+        );
+    }
+
+    /// Produces a linting warning for incorrectly ordered item members.
+    fn lint_member_name<T: LintContext>(
+        cx: &T,
+        ident: &rustc_span::symbol::Ident,
+        before_ident: &rustc_span::symbol::Ident,
+    ) {
+        span_lint_and_note(
+            cx,
+            ARBITRARY_SOURCE_ITEM_ORDERING,
+            ident.span,
+            "incorrect ordering of items (must be alphabetically ordered)",
+            Some(before_ident.span),
+            format!("should be placed before `{}`", before_ident.as_str(),),
+        );
+    }
+
+    fn lint_member_item<T: LintContext>(cx: &T, item: &Item<'_>, before_item: &Item<'_>) {
+        let span = if item.ident.as_str().is_empty() {
+            &item.span
+        } else {
+            &item.ident.span
+        };
+
+        let (before_span, note) = if before_item.ident.as_str().is_empty() {
+            (
+                &before_item.span,
+                "should be placed before the following item".to_owned(),
+            )
+        } else {
+            (
+                &before_item.ident.span,
+                format!("should be placed before `{}`", before_item.ident.as_str(),),
+            )
+        };
+
+        // This catches false positives where generated code gets linted.
+        if span == before_span {
+            return;
+        }
+
+        span_lint_and_note(
+            cx,
+            ARBITRARY_SOURCE_ITEM_ORDERING,
+            *span,
+            "incorrect ordering of items (must be alphabetically ordered)",
+            Some(*before_span),
+            note,
+        );
+    }
+
+    /// Produces a linting warning for incorrectly ordered trait items.
+    fn lint_trait_item<T: LintContext>(&self, cx: &T, item: &TraitItemRef, before_item: &TraitItemRef) {
+        span_lint_and_note(
+            cx,
+            ARBITRARY_SOURCE_ITEM_ORDERING,
+            item.span,
+            format!(
+                "incorrect ordering of trait items (defined order: {:?})",
+                self.assoc_types_order
+            ),
+            Some(before_item.span),
+            format!("should be placed before `{}`", before_item.ident.as_str(),),
+        );
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for ArbitrarySourceItemOrdering {
+    fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
+        match &item.kind {
+            ItemKind::Enum(enum_def, _generics) if self.enable_ordering_for_enum => {
+                let mut cur_v: Option<&Variant<'_>> = None;
+                for variant in enum_def.variants {
+                    if in_external_macro(cx.sess(), variant.span) {
+                        continue;
+                    }
+
+                    if let Some(cur_v) = cur_v {
+                        if cur_v.ident.name.as_str() > variant.ident.name.as_str() && cur_v.span != variant.span {
+                            Self::lint_member_name(cx, &variant.ident, &cur_v.ident);
+                        }
+                    }
+                    cur_v = Some(variant);
+                }
+            },
+            ItemKind::Struct(VariantData::Struct { fields, .. }, _generics) if self.enable_ordering_for_struct => {
+                let mut cur_f: Option<&FieldDef<'_>> = None;
+                for field in *fields {
+                    if in_external_macro(cx.sess(), field.span) {
+                        continue;
+                    }
+
+                    if let Some(cur_f) = cur_f {
+                        if cur_f.ident.name.as_str() > field.ident.name.as_str() && cur_f.span != field.span {
+                            Self::lint_member_name(cx, &field.ident, &cur_f.ident);
+                        }
+                    }
+                    cur_f = Some(field);
+                }
+            },
+            ItemKind::Trait(is_auto, _safety, _generics, _generic_bounds, item_ref)
+                if self.enable_ordering_for_trait && *is_auto == IsAuto::No =>
+            {
+                let mut cur_t: Option<&TraitItemRef> = None;
+
+                for item in *item_ref {
+                    if in_external_macro(cx.sess(), item.span) {
+                        continue;
+                    }
+
+                    if let Some(cur_t) = cur_t {
+                        let cur_t_kind = convert_assoc_item_kind(cur_t.kind);
+                        let cur_t_kind_index = self.assoc_types_order.index_of(&cur_t_kind);
+                        let item_kind = convert_assoc_item_kind(item.kind);
+                        let item_kind_index = self.assoc_types_order.index_of(&item_kind);
+
+                        if cur_t_kind == item_kind && cur_t.ident.name.as_str() > item.ident.name.as_str() {
+                            Self::lint_member_name(cx, &item.ident, &cur_t.ident);
+                        } else if cur_t_kind_index > item_kind_index {
+                            self.lint_trait_item(cx, item, cur_t);
+                        }
+                    }
+                    cur_t = Some(item);
+                }
+            },
+            ItemKind::Impl(trait_impl) if self.enable_ordering_for_impl => {
+                let mut cur_t: Option<&ImplItemRef> = None;
+
+                for item in trait_impl.items {
+                    if in_external_macro(cx.sess(), item.span) {
+                        continue;
+                    }
+
+                    if let Some(cur_t) = cur_t {
+                        let cur_t_kind = convert_assoc_item_kind(cur_t.kind);
+                        let cur_t_kind_index = self.assoc_types_order.index_of(&cur_t_kind);
+                        let item_kind = convert_assoc_item_kind(item.kind);
+                        let item_kind_index = self.assoc_types_order.index_of(&item_kind);
+
+                        if cur_t_kind == item_kind && cur_t.ident.name.as_str() > item.ident.name.as_str() {
+                            Self::lint_member_name(cx, &item.ident, &cur_t.ident);
+                        } else if cur_t_kind_index > item_kind_index {
+                            self.lint_impl_item(cx, item, cur_t);
+                        }
+                    }
+                    cur_t = Some(item);
+                }
+            },
+            _ => {}, // Catch-all for `ItemKinds` that don't have fields.
+        }
+    }
+
+    fn check_mod(&mut self, cx: &LateContext<'tcx>, module: &'tcx Mod<'tcx>, _: HirId) {
+        struct CurItem<'a> {
+            item: &'a Item<'a>,
+            order: usize,
+            name: String,
+        }
+        let mut cur_t: Option<CurItem<'_>> = None;
+
+        if !self.enable_ordering_for_module {
+            return;
+        }
+
+        let items = module.item_ids.iter().map(|&id| cx.tcx.hir().item(id));
+
+        // Iterates over the items within a module.
+        //
+        // As of 2023-05-09, the Rust compiler will hold the entries in the same
+        // order as they appear in the source code, which is convenient for us,
+        // as no sorting by source map/line of code has to be applied.
+        //
+        for item in items {
+            if in_external_macro(cx.sess(), item.span) {
+                continue;
+            }
+
+            // The following exceptions (skipping with `continue;`) may not be
+            // complete, edge cases have not been explored further than what
+            // appears in the existing code base.
+            if item.ident.name == rustc_span::symbol::kw::Empty {
+                if let ItemKind::Impl(_) = item.kind {
+                    // Sorting trait impls for unnamed types makes no sense.
+                    if get_item_name(item).is_empty() {
+                        continue;
+                    }
+                } else if let ItemKind::ForeignMod { .. } = item.kind {
+                    continue;
+                } else if let ItemKind::GlobalAsm(_) = item.kind {
+                    continue;
+                } else if let ItemKind::Use(path, use_kind) = item.kind {
+                    if path.segments.is_empty() {
+                        // Use statements that contain braces get caught here.
+                        // They will still be linted internally.
+                        continue;
+                    } else if path.segments.len() >= 2
+                        && (path.segments[0].ident.name == rustc_span::sym::std
+                            || path.segments[0].ident.name == rustc_span::sym::core)
+                        && path.segments[1].ident.name == rustc_span::sym::prelude
+                    {
+                        // Filters the autogenerated prelude use statement.
+                        // e.g. `use std::prelude::rustc_2021`
+                    } else if use_kind == UseKind::Glob {
+                        // Filters glob kinds of uses.
+                        // e.g. `use std::sync::*`
+                    } else {
+                        // This can be used for debugging.
+                        // println!("Unknown autogenerated use statement: {:?}", item);
+                    }
+                    continue;
+                }
+            }
+
+            if item.ident.name.as_str().starts_with('_') {
+                // Filters out unnamed macro-like impls for various derives,
+                // e.g. serde::Serialize or num_derive::FromPrimitive.
+                continue;
+            }
+
+            if item.ident.name == rustc_span::sym::std && item.span.is_dummy() {
+                if let ItemKind::ExternCrate(None) = item.kind {
+                    // Filters the auto-included Rust standard library.
+                    continue;
+                }
+                println!("Unknown item: {item:?}");
+            }
+
+            let item_kind = convert_module_item_kind(&item.kind);
+            let module_level_order = self
+                .module_item_order_groupings
+                .module_level_order_of(&item_kind)
+                .unwrap_or_default();
+
+            if let Some(cur_t) = cur_t.as_ref() {
+                use std::cmp::Ordering; // Better legibility.
+                match module_level_order.cmp(&cur_t.order) {
+                    Ordering::Less => {
+                        Self::lint_member_item(cx, item, cur_t.item);
+                    },
+                    Ordering::Equal if item_kind == SourceItemOrderingModuleItemKind::Use => {
+                        // Skip ordering use statements, as these should be ordered by rustfmt.
+                    },
+                    Ordering::Equal if cur_t.name > get_item_name(item) => {
+                        Self::lint_member_item(cx, item, cur_t.item);
+                    },
+                    Ordering::Equal | Ordering::Greater => {
+                        // Nothing to do in this case, they're already in the right order.
+                    },
+                }
+            }
+
+            // Makes a note of the current item for comparison with the next.
+            cur_t = Some(CurItem {
+                order: module_level_order,
+                item,
+                name: get_item_name(item),
+            });
+        }
+    }
+}
+
+/// Converts a [`rustc_hir::AssocItemKind`] to a
+/// [`SourceItemOrderingTraitAssocItemKind`].
+///
+/// This is implemented here because `rustc_hir` is not a dependency of
+/// `clippy_config`.
+fn convert_assoc_item_kind(value: AssocItemKind) -> SourceItemOrderingTraitAssocItemKind {
+    #[allow(clippy::enum_glob_use)] // Very local glob use for legibility.
+    use SourceItemOrderingTraitAssocItemKind::*;
+    match value {
+        AssocItemKind::Const { .. } => Const,
+        AssocItemKind::Type { .. } => Type,
+        AssocItemKind::Fn { .. } => Fn,
+    }
+}
+
+/// Converts a [`rustc_hir::ItemKind`] to a
+/// [`SourceItemOrderingModuleItemKind`].
+///
+/// This is implemented here because `rustc_hir` is not a dependency of
+/// `clippy_config`.
+fn convert_module_item_kind(value: &ItemKind<'_>) -> SourceItemOrderingModuleItemKind {
+    #[allow(clippy::enum_glob_use)] // Very local glob use for legibility.
+    use SourceItemOrderingModuleItemKind::*;
+    match value {
+        ItemKind::ExternCrate(..) => ExternCrate,
+        ItemKind::Use(..) => Use,
+        ItemKind::Static(..) => Static,
+        ItemKind::Const(..) => Const,
+        ItemKind::Fn(..) => Fn,
+        ItemKind::Macro(..) => Macro,
+        ItemKind::Mod(..) => Mod,
+        ItemKind::ForeignMod { .. } => ForeignMod,
+        ItemKind::GlobalAsm(..) => GlobalAsm,
+        ItemKind::TyAlias(..) => TyAlias,
+        ItemKind::Enum(..) => Enum,
+        ItemKind::Struct(..) => Struct,
+        ItemKind::Union(..) => Union,
+        ItemKind::Trait(..) => Trait,
+        ItemKind::TraitAlias(..) => TraitAlias,
+        ItemKind::Impl(..) => Impl,
+    }
+}
+
+/// Gets the item name for sorting purposes, which in the general case is
+/// `item.ident.name`.
+///
+/// For trait impls, the name used for sorting will be the written path of
+/// `item.self_ty` plus the written path of `item.of_trait`, joined with
+/// exclamation marks. Exclamation marks are used because they are the first
+/// printable ASCII character.
+///
+/// Trait impls generated using a derive-macro will have their path rewritten,
+/// such that for example `Default` is `$crate::default::Default`, and
+/// `std::clone::Clone` is `$crate::clone::Clone`. This behaviour is described
+/// further in the [Rust Reference, Paths Chapter][rust_ref].
+///
+/// [rust_ref]: https://doc.rust-lang.org/reference/paths.html#crate-1
+fn get_item_name(item: &Item<'_>) -> String {
+    match item.kind {
+        ItemKind::Impl(im) => {
+            if let TyKind::Path(path) = im.self_ty.kind {
+                match path {
+                    QPath::Resolved(_, path) => {
+                        let segs = path.segments.iter();
+                        let mut segs: Vec<String> = segs.map(|s| s.ident.name.as_str().to_owned()).collect();
+
+                        if let Some(of_trait) = im.of_trait {
+                            let mut trait_segs: Vec<String> = of_trait
+                                .path
+                                .segments
+                                .iter()
+                                .map(|s| s.ident.name.as_str().to_owned())
+                                .collect();
+                            segs.append(&mut trait_segs);
+                        }
+
+                        segs.push(String::new());
+                        segs.join("!!")
+                    },
+                    QPath::TypeRelative(_, _path_seg) => {
+                        // This case doesn't exist in the clippy tests codebase.
+                        String::new()
+                    },
+                    QPath::LangItem(_, _) => String::new(),
+                }
+            } else {
+                // Impls for anything that isn't a named type can be skipped.
+                String::new()
+            }
+        },
+        _ => item.ident.name.as_str().to_owned(),
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/assigning_clones.rs b/src/tools/clippy/clippy_lints/src/assigning_clones.rs
index 0b82c0cd04c..00626a37ef8 100644
--- a/src/tools/clippy/clippy_lints/src/assigning_clones.rs
+++ b/src/tools/clippy/clippy_lints/src/assigning_clones.rs
@@ -96,7 +96,7 @@ impl<'tcx> LateLintPass<'tcx> for AssigningClones {
                 },
                 _ => return,
             }
-            && let Ok(Some(resolved_fn)) = Instance::try_resolve(cx.tcx, cx.param_env, fn_id, fn_gen_args)
+            && let Ok(Some(resolved_fn)) = Instance::try_resolve(cx.tcx, cx.typing_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.
diff --git a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs
index a5a7b9f74a6..1879391ec29 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes.rs
@@ -3,11 +3,11 @@ use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_from_proc_macro;
 use rustc_ast::{AttrStyle, Attribute};
 use rustc_errors::Applicability;
-use rustc_lint::{LateContext, LintContext};
+use rustc_lint::{EarlyContext, LintContext};
 use rustc_middle::lint::in_external_macro;
 
 // Separate each crate's features.
-pub fn check<'cx>(cx: &LateContext<'cx>, attr: &'cx Attribute) {
+pub fn check<'cx>(cx: &EarlyContext<'cx>, attr: &'cx Attribute) {
     if !in_external_macro(cx.sess(), attr.span)
         && let AttrStyle::Outer = attr.style
         && let Some(ident) = attr.ident()
diff --git a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs
index 5d4e864b9b0..788377fe83c 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/allow_attributes_without_reason.rs
@@ -2,12 +2,12 @@ use super::{ALLOW_ATTRIBUTES_WITHOUT_REASON, Attribute};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::is_from_proc_macro;
 use rustc_ast::{MetaItemInner, MetaItemKind};
-use rustc_lint::{LateContext, LintContext};
+use rustc_lint::{EarlyContext, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_span::sym;
 use rustc_span::symbol::Symbol;
 
-pub(super) fn check<'cx>(cx: &LateContext<'cx>, name: Symbol, items: &[MetaItemInner], attr: &'cx Attribute) {
+pub(super) fn check<'cx>(cx: &EarlyContext<'cx>, name: Symbol, items: &[MetaItemInner], attr: &'cx Attribute) {
     // Check if the reason is present
     if let Some(item) = items.last().and_then(MetaItemInner::meta_item)
         && let MetaItemKind::NameValue(_) = &item.kind
diff --git a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs
index 0baf889faa0..fecf3166406 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/blanket_clippy_restriction_lints.rs
@@ -2,11 +2,11 @@ use super::BLANKET_CLIPPY_RESTRICTION_LINTS;
 use super::utils::extract_clippy_lint;
 use clippy_utils::diagnostics::{span_lint_and_help, span_lint_and_then};
 use rustc_ast::MetaItemInner;
-use rustc_lint::{LateContext, Level, LintContext};
+use rustc_lint::{EarlyContext, Level, LintContext};
 use rustc_span::symbol::Symbol;
 use rustc_span::{DUMMY_SP, sym};
 
-pub(super) fn check(cx: &LateContext<'_>, name: Symbol, items: &[MetaItemInner]) {
+pub(super) fn check(cx: &EarlyContext<'_>, name: Symbol, items: &[MetaItemInner]) {
     for lint in items {
         if let Some(lint_name) = extract_clippy_lint(lint) {
             if lint_name.as_str() == "restriction" && name != sym::allow {
@@ -23,7 +23,7 @@ pub(super) fn check(cx: &LateContext<'_>, name: Symbol, items: &[MetaItemInner])
     }
 }
 
-pub(super) fn check_command_line(cx: &LateContext<'_>) {
+pub(super) fn check_command_line(cx: &EarlyContext<'_>) {
     for (name, level) in &cx.sess().opts.lint_opts {
         if name == "clippy::restriction" && *level > Level::Allow {
             span_lint_and_then(
diff --git a/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs b/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs
index 1898c145c76..d3153ec6613 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/deprecated_semver.rs
@@ -1,11 +1,11 @@
 use super::DEPRECATED_SEMVER;
 use clippy_utils::diagnostics::span_lint;
 use rustc_ast::{LitKind, MetaItemLit};
-use rustc_lint::LateContext;
+use rustc_lint::EarlyContext;
 use rustc_span::Span;
 use semver::Version;
 
-pub(super) fn check(cx: &LateContext<'_>, span: Span, lit: &MetaItemLit) {
+pub(super) fn check(cx: &EarlyContext<'_>, span: Span, lit: &MetaItemLit) {
     if let LitKind::Str(is, _) = lit.kind {
         if is.as_str() == "TBD" || Version::parse(is.as_str()).is_ok() {
             return;
diff --git a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs
index 55f8e1072db..2ddbc7a6a76 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/duplicated_attributes.rs
@@ -2,12 +2,12 @@ use super::DUPLICATED_ATTRIBUTES;
 use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_ast::{Attribute, MetaItem};
 use rustc_data_structures::fx::FxHashMap;
-use rustc_lint::LateContext;
+use rustc_lint::EarlyContext;
 use rustc_span::{Span, sym};
 use std::collections::hash_map::Entry;
 
 fn emit_if_duplicated(
-    cx: &LateContext<'_>,
+    cx: &EarlyContext<'_>,
     attr: &MetaItem,
     attr_paths: &mut FxHashMap<String, Span>,
     complete_path: String,
@@ -26,7 +26,7 @@ fn emit_if_duplicated(
 }
 
 fn check_duplicated_attr(
-    cx: &LateContext<'_>,
+    cx: &EarlyContext<'_>,
     attr: &MetaItem,
     attr_paths: &mut FxHashMap<String, Span>,
     parent: &mut Vec<String>,
@@ -65,7 +65,7 @@ fn check_duplicated_attr(
     }
 }
 
-pub fn check(cx: &LateContext<'_>, attrs: &[Attribute]) {
+pub fn check(cx: &EarlyContext<'_>, attrs: &[Attribute]) {
     let mut attr_paths = FxHashMap::default();
 
     for attr in attrs {
diff --git a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs
index 5d2ea36b366..32c28c09c36 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/mixed_attributes_style.rs
@@ -3,7 +3,7 @@ use clippy_utils::diagnostics::span_lint;
 use rustc_ast::{AttrKind, AttrStyle, Attribute};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::sync::Lrc;
-use rustc_lint::{LateContext, LintContext};
+use rustc_lint::{EarlyContext, LintContext};
 use rustc_span::source_map::SourceMap;
 use rustc_span::{SourceFile, Span, Symbol};
 
@@ -32,7 +32,7 @@ impl From<&AttrKind> for SimpleAttrKind {
     }
 }
 
-pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute]) {
+pub(super) fn check(cx: &EarlyContext<'_>, item_span: Span, attrs: &[Attribute]) {
     let mut inner_attr_kind: FxHashSet<SimpleAttrKind> = FxHashSet::default();
     let mut outer_attr_kind: FxHashSet<SimpleAttrKind> = FxHashSet::default();
 
@@ -64,7 +64,7 @@ pub(super) fn check(cx: &LateContext<'_>, item_span: Span, attrs: &[Attribute])
     }
 }
 
-fn lint_mixed_attrs(cx: &LateContext<'_>, attrs: &[Attribute]) {
+fn lint_mixed_attrs(cx: &EarlyContext<'_>, attrs: &[Attribute]) {
     let mut attrs_iter = attrs.iter().filter(|attr| !attr.span.from_expansion());
     let span = if let (Some(first), Some(last)) = (attrs_iter.next(), attrs_iter.last()) {
         first.span.with_hi(last.span.hi())
diff --git a/src/tools/clippy/clippy_lints/src/attrs/mod.rs b/src/tools/clippy/clippy_lints/src/attrs/mod.rs
index 1a34ca99fc2..c29c9f08cf7 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/mod.rs
@@ -14,8 +14,8 @@ mod utils;
 
 use clippy_config::Conf;
 use clippy_config::msrvs::{self, Msrv};
-use rustc_ast::{Attribute, MetaItemInner, MetaItemKind};
-use rustc_hir::{ImplItem, Item, ItemKind, TraitItem};
+use rustc_ast::{self as ast, Attribute, MetaItemInner, MetaItemKind};
+use rustc_hir::{ImplItem, Item, TraitItem};
 use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
 use rustc_span::sym;
@@ -414,15 +414,7 @@ pub struct Attributes {
 }
 
 impl_lint_pass!(Attributes => [
-    ALLOW_ATTRIBUTES,
-    ALLOW_ATTRIBUTES_WITHOUT_REASON,
     INLINE_ALWAYS,
-    DEPRECATED_SEMVER,
-    USELESS_ATTRIBUTE,
-    BLANKET_CLIPPY_RESTRICTION_LINTS,
-    SHOULD_PANIC_WITHOUT_EXPECT,
-    MIXED_ATTRIBUTES_STYLE,
-    DUPLICATED_ATTRIBUTES,
 ]);
 
 impl Attributes {
@@ -434,53 +426,11 @@ impl Attributes {
 }
 
 impl<'tcx> LateLintPass<'tcx> for Attributes {
-    fn check_crate(&mut self, cx: &LateContext<'tcx>) {
-        blanket_clippy_restriction_lints::check_command_line(cx);
-        duplicated_attributes::check(cx, cx.tcx.hir().krate_attrs());
-    }
-
-    fn check_attribute(&mut self, cx: &LateContext<'tcx>, attr: &'tcx Attribute) {
-        if let Some(items) = &attr.meta_item_list() {
-            if let Some(ident) = attr.ident() {
-                if is_lint_level(ident.name, attr.id) {
-                    blanket_clippy_restriction_lints::check(cx, ident.name, items);
-                }
-                if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) {
-                    allow_attributes::check(cx, attr);
-                }
-                if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION)
-                {
-                    allow_attributes_without_reason::check(cx, ident.name, items, attr);
-                }
-                if items.is_empty() || !attr.has_name(sym::deprecated) {
-                    return;
-                }
-                for item in items {
-                    if let MetaItemInner::MetaItem(mi) = &item
-                        && let MetaItemKind::NameValue(lit) = &mi.kind
-                        && mi.has_name(sym::since)
-                    {
-                        deprecated_semver::check(cx, item.span(), lit);
-                    }
-                }
-            }
-        }
-        if attr.has_name(sym::should_panic) {
-            should_panic_without_expect::check(cx, attr);
-        }
-    }
-
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
         let attrs = cx.tcx.hir().attrs(item.hir_id());
         if is_relevant_item(cx, item) {
             inline_always::check(cx, item.span, item.ident.name, attrs);
         }
-        match item.kind {
-            ItemKind::ExternCrate(..) | ItemKind::Use(..) => useless_attribute::check(cx, item, attrs),
-            _ => {},
-        }
-        mixed_attributes_style::check(cx, item.span, attrs);
-        duplicated_attributes::check(cx, attrs);
     }
 
     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) {
@@ -526,3 +476,77 @@ impl EarlyLintPass for EarlyAttributes {
 
     extract_msrv_attr!(EarlyContext);
 }
+
+pub struct PostExpansionEarlyAttributes {
+    msrv: Msrv,
+}
+
+impl PostExpansionEarlyAttributes {
+    pub fn new(conf: &'static Conf) -> Self {
+        Self {
+            msrv: conf.msrv.clone(),
+        }
+    }
+}
+
+impl_lint_pass!(PostExpansionEarlyAttributes => [
+    ALLOW_ATTRIBUTES,
+    ALLOW_ATTRIBUTES_WITHOUT_REASON,
+    DEPRECATED_SEMVER,
+    USELESS_ATTRIBUTE,
+    BLANKET_CLIPPY_RESTRICTION_LINTS,
+    SHOULD_PANIC_WITHOUT_EXPECT,
+    MIXED_ATTRIBUTES_STYLE,
+    DUPLICATED_ATTRIBUTES,
+]);
+
+impl EarlyLintPass for PostExpansionEarlyAttributes {
+    fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &ast::Crate) {
+        blanket_clippy_restriction_lints::check_command_line(cx);
+        duplicated_attributes::check(cx, &krate.attrs);
+    }
+
+    fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) {
+        if let Some(items) = &attr.meta_item_list() {
+            if let Some(ident) = attr.ident() {
+                if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) {
+                    allow_attributes::check(cx, attr);
+                }
+                if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION)
+                {
+                    allow_attributes_without_reason::check(cx, ident.name, items, attr);
+                }
+                if is_lint_level(ident.name, attr.id) {
+                    blanket_clippy_restriction_lints::check(cx, ident.name, items);
+                }
+                if items.is_empty() || !attr.has_name(sym::deprecated) {
+                    return;
+                }
+                for item in items {
+                    if let MetaItemInner::MetaItem(mi) = &item
+                        && let MetaItemKind::NameValue(lit) = &mi.kind
+                        && mi.has_name(sym::since)
+                    {
+                        deprecated_semver::check(cx, item.span(), lit);
+                    }
+                }
+            }
+        }
+
+        if attr.has_name(sym::should_panic) {
+            should_panic_without_expect::check(cx, attr);
+        }
+    }
+
+    fn check_item(&mut self, cx: &EarlyContext<'_>, item: &'_ ast::Item) {
+        match item.kind {
+            ast::ItemKind::ExternCrate(..) | ast::ItemKind::Use(..) => useless_attribute::check(cx, item, &item.attrs),
+            _ => {},
+        }
+
+        mixed_attributes_style::check(cx, item.span, &item.attrs);
+        duplicated_attributes::check(cx, &item.attrs);
+    }
+
+    extract_msrv_attr!(EarlyContext);
+}
diff --git a/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs b/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs
index 2d45cbbf621..fadd5272880 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/should_panic_without_expect.rs
@@ -4,12 +4,12 @@ use rustc_ast::token::{Token, TokenKind};
 use rustc_ast::tokenstream::TokenTree;
 use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind};
 use rustc_errors::Applicability;
-use rustc_lint::LateContext;
+use rustc_lint::EarlyContext;
 use rustc_span::sym;
 
-pub(super) fn check(cx: &LateContext<'_>, attr: &Attribute) {
+pub(super) fn check(cx: &EarlyContext<'_>, attr: &Attribute) {
     if let AttrKind::Normal(normal_attr) = &attr.kind {
-        if let AttrArgs::Eq(_, AttrArgsEq::Hir(_)) = &normal_attr.item.args {
+        if let AttrArgs::Eq(_, AttrArgsEq::Ast(_)) = &normal_attr.item.args {
             // `#[should_panic = ".."]` found, good
             return;
         }
diff --git a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
index 72e6ce59d59..e21853598c3 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/useless_attribute.rs
@@ -1,15 +1,14 @@
+use super::USELESS_ATTRIBUTE;
 use super::utils::{extract_clippy_lint, is_lint_level, is_word};
-use super::{Attribute, USELESS_ATTRIBUTE};
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::source::{SpanRangeExt, first_line_of_span};
-use rustc_ast::MetaItemInner;
+use rustc_ast::{Attribute, Item, ItemKind, MetaItemInner};
 use rustc_errors::Applicability;
-use rustc_hir::{Item, ItemKind};
-use rustc_lint::{LateContext, LintContext};
+use rustc_lint::{EarlyContext, LintContext};
 use rustc_middle::lint::in_external_macro;
 use rustc_span::sym;
 
-pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute]) {
+pub(super) fn check(cx: &EarlyContext<'_>, item: &Item, attrs: &[Attribute]) {
     let skip_unused_imports = attrs.iter().any(|attr| attr.has_name(sym::macro_use));
 
     for attr in attrs {
@@ -17,7 +16,7 @@ pub(super) fn check(cx: &LateContext<'_>, item: &Item<'_>, attrs: &[Attribute])
             return;
         }
         if let Some(lint_list) = &attr.meta_item_list() {
-            if attr.ident().map_or(false, |ident| is_lint_level(ident.name, attr.id)) {
+            if attr.ident().is_some_and(|ident| is_lint_level(ident.name, attr.id)) {
                 for lint in lint_list {
                     match item.kind {
                         ItemKind::Use(..) => {
diff --git a/src/tools/clippy/clippy_lints/src/attrs/utils.rs b/src/tools/clippy/clippy_lints/src/attrs/utils.rs
index 9b10ae83651..3bb02688bf2 100644
--- a/src/tools/clippy/clippy_lints/src/attrs/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/attrs/utils.rs
@@ -50,7 +50,7 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_
         block
             .expr
             .as_ref()
-            .map_or(false, |e| is_relevant_expr(cx, typeck_results, e)),
+            .is_some_and(|e| is_relevant_expr(cx, typeck_results, e)),
         |stmt| match &stmt.kind {
             StmtKind::Let(_) => true,
             StmtKind::Expr(expr) | StmtKind::Semi(expr) => is_relevant_expr(cx, typeck_results, expr),
@@ -60,7 +60,7 @@ fn is_relevant_block(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_
 }
 
 fn is_relevant_expr(cx: &LateContext<'_>, typeck_results: &ty::TypeckResults<'_>, expr: &Expr<'_>) -> bool {
-    if macro_backtrace(expr.span).last().map_or(false, |macro_call| {
+    if macro_backtrace(expr.span).last().is_some_and(|macro_call| {
         is_panic(cx, macro_call.def_id) || cx.tcx.item_name(macro_call.def_id) == sym::unreachable
     }) {
         return false;
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 9952d0af99e..6948cf560a5 100644
--- a/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
+++ b/src/tools/clippy/clippy_lints/src/await_holding_invalid.rs
@@ -15,7 +15,7 @@ declare_clippy_lint! {
     /// `MutexGuard`.
     ///
     /// ### Why is this bad?
-    /// The Mutex types found in [`std::sync`][https://doc.rust-lang.org/stable/std/sync/] and
+    /// 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.
     ///
diff --git a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
index db5792188dd..adac2f27ea8 100644
--- a/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
+++ b/src/tools/clippy/clippy_lints/src/bool_assert_comparison.rs
@@ -60,9 +60,9 @@ fn is_impl_not_trait_with_bool_out<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -
                 trait_id,
             )
         })
-        .map_or(false, |assoc_item| {
-            let proj = Ty::new_projection_from_args(cx.tcx, assoc_item.def_id, cx.tcx.mk_args_trait(ty, []));
-            let nty = cx.tcx.normalize_erasing_regions(cx.param_env, proj);
+        .is_some_and(|assoc_item| {
+            let proj = Ty::new_projection(cx.tcx, assoc_item.def_id, cx.tcx.mk_args_trait(ty, []));
+            let nty = cx.tcx.normalize_erasing_regions(cx.typing_env(), proj);
 
             nty.is_bool()
         })
diff --git a/src/tools/clippy/clippy_lints/src/booleans.rs b/src/tools/clippy/clippy_lints/src/booleans.rs
index 26888f7e3a0..896bd5fd03d 100644
--- a/src/tools/clippy/clippy_lints/src/booleans.rs
+++ b/src/tools/clippy/clippy_lints/src/booleans.rs
@@ -203,6 +203,21 @@ fn check_simplify_not(cx: &LateContext<'_>, msrv: &Msrv, expr: &Expr<'_>) {
         && let Some(suggestion) = simplify_not(cx, msrv, inner)
         && cx.tcx.lint_level_at_node(NONMINIMAL_BOOL, expr.hir_id).0 != Level::Allow
     {
+        use clippy_utils::sugg::{Sugg, has_enclosing_paren};
+        let maybe_par = if let Some(sug) = Sugg::hir_opt(cx, inner) {
+            match sug {
+                Sugg::BinOp(..) => true,
+                Sugg::MaybeParen(sug) if !has_enclosing_paren(&sug) => true,
+                _ => false,
+            }
+        } else {
+            false
+        };
+        let suggestion = if maybe_par {
+            format!("({suggestion})")
+        } else {
+            suggestion
+        };
         span_lint_and_sugg(
             cx,
             NONMINIMAL_BOOL,
@@ -652,5 +667,5 @@ fn implements_ord(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     let ty = cx.typeck_results().expr_ty(expr);
     cx.tcx
         .get_diagnostic_item(sym::Ord)
-        .map_or(false, |id| implements_trait(cx, ty, id, &[]))
+        .is_some_and(|id| implements_trait(cx, ty, id, &[]))
 }
diff --git a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
index cba8224b84c..f2551a05b1a 100644
--- a/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/borrow_deref_ref.rs
@@ -4,7 +4,7 @@ use clippy_utils::source::SpanRangeExt;
 use clippy_utils::ty::implements_trait;
 use clippy_utils::{get_parent_expr, is_from_proc_macro, is_lint_allowed};
 use rustc_errors::Applicability;
-use rustc_hir::{ExprKind, UnOp};
+use rustc_hir::{BorrowKind, ExprKind, UnOp};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::mir::Mutability;
 use rustc_middle::ty;
@@ -49,7 +49,7 @@ declare_lint_pass!(BorrowDerefRef => [BORROW_DEREF_REF]);
 
 impl<'tcx> LateLintPass<'tcx> for BorrowDerefRef {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, e: &rustc_hir::Expr<'tcx>) {
-        if let ExprKind::AddrOf(_, Mutability::Not, addrof_target) = e.kind
+        if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, addrof_target) = e.kind
             && let ExprKind::Unary(UnOp::Deref, deref_target) = addrof_target.kind
             && !matches!(deref_target.kind, ExprKind::Unary(UnOp::Deref, ..))
             && !e.span.from_expansion()
diff --git a/src/tools/clippy/clippy_lints/src/box_default.rs b/src/tools/clippy/clippy_lints/src/box_default.rs
index bf1d077fec2..a1ca23e65ff 100644
--- a/src/tools/clippy/clippy_lints/src/box_default.rs
+++ b/src/tools/clippy/clippy_lints/src/box_default.rs
@@ -45,7 +45,7 @@ impl LateLintPass<'_> for BoxDefault {
             // And that method is `new`
             && seg.ident.name == sym::new
             // And the call is that of a `Box` method
-            && path_def_id(cx, ty).map_or(false, |id| Some(id) == cx.tcx.lang_items().owned_box())
+            && path_def_id(cx, ty).is_some_and(|id| Some(id) == cx.tcx.lang_items().owned_box())
             // And the single argument to the call is another function call
             // This is the `T::default()` (or default equivalent) of `Box::new(T::default())`
             && let ExprKind::Call(arg_path, _) = arg.kind
@@ -83,9 +83,9 @@ fn is_plain_default(cx: &LateContext<'_>, arg_path: &Expr<'_>) -> bool {
 }
 
 fn is_local_vec_expn(cx: &LateContext<'_>, expr: &Expr<'_>, ref_expr: &Expr<'_>) -> bool {
-    macro_backtrace(expr.span).next().map_or(false, |call| {
-        cx.tcx.is_diagnostic_item(sym::vec_macro, call.def_id) && call.span.eq_ctxt(ref_expr.span)
-    })
+    macro_backtrace(expr.span)
+        .next()
+        .is_some_and(|call| cx.tcx.is_diagnostic_item(sym::vec_macro, call.def_id) && call.span.eq_ctxt(ref_expr.span))
 }
 
 #[derive(Default)]
diff --git a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
index b11b967f8bc..205357afd84 100644
--- a/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/cast_ptr_alignment.rs
@@ -20,7 +20,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>) {
         );
         lint_cast_ptr_alignment(cx, expr, cast_from, cast_to);
     } else if let ExprKind::MethodCall(method_path, self_arg, [], _) = &expr.kind {
-        if method_path.ident.name == sym!(cast)
+        if method_path.ident.name.as_str() == "cast"
             && let Some(generic_args) = method_path.args
             && let [GenericArg::Type(cast_to)] = generic_args.args
             // There probably is no obvious reason to do this, just to be consistent with `as` cases.
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 86c5f6b9f0b..46d67e615c7 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
@@ -39,7 +39,7 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, msrv: &Msrv) {
             (Mutability::Not, Mutability::Not) | (Mutability::Mut, Mutability::Mut))
         // The `U` in `pointer::cast` have to be `Sized`
         // as explained here: https://github.com/rust-lang/rust/issues/60602.
-        && to_pointee_ty.is_sized(cx.tcx, cx.param_env)
+        && to_pointee_ty.is_sized(cx.tcx, cx.typing_env())
     {
         let mut app = Applicability::MachineApplicable;
         let turbofish = match &cast_to_hir_ty.kind {
diff --git a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
index 811d33c8bde..332e897def7 100644
--- a/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
+++ b/src/tools/clippy/clippy_lints/src/casts/unnecessary_cast.rs
@@ -159,8 +159,7 @@ pub(super) fn check<'tcx>(
         // The same is true if the expression encompassing the cast expression is a unary
         // expression or an addressof expression.
         let needs_block = matches!(cast_expr.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..))
-            || get_parent_expr(cx, expr)
-                .map_or(false, |e| matches!(e.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..)));
+            || get_parent_expr(cx, expr).is_some_and(|e| matches!(e.kind, ExprKind::Unary(..) | ExprKind::AddrOf(..)));
 
         span_lint_and_sugg(
             cx,
@@ -268,8 +267,7 @@ fn is_cast_from_ty_alias<'tcx>(cx: &LateContext<'tcx>, expr: impl Visitable<'tcx
                 if !snippet
                     .split("->")
                     .skip(1)
-                    .map(|s| snippet_eq_ty(s, cast_from) || s.split("where").any(|ty| snippet_eq_ty(ty, cast_from)))
-                    .any(|a| a)
+                    .any(|s| snippet_eq_ty(s, cast_from) || s.split("where").any(|ty| snippet_eq_ty(ty, cast_from)))
                 {
                     return ControlFlow::Break(());
                 }
diff --git a/src/tools/clippy/clippy_lints/src/checked_conversions.rs b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
index 2e7f91a842e..f76e399517c 100644
--- a/src/tools/clippy/clippy_lints/src/checked_conversions.rs
+++ b/src/tools/clippy/clippy_lints/src/checked_conversions.rs
@@ -232,7 +232,7 @@ fn get_types_from_cast<'a>(
     // or `to_type::MAX as from_type`
     let call_from_cast: Option<(&Expr<'_>, &str)> = if let ExprKind::Cast(limit, from_type) = &expr.kind
         // to_type::max_value(), from_type
-        && let TyKind::Path(ref from_type_path) = &from_type.kind
+        && let TyKind::Path(from_type_path) = &from_type.kind
         && let Some(from_sym) = int_ty_to_sym(from_type_path)
     {
         Some((limit, from_sym))
@@ -245,7 +245,7 @@ fn get_types_from_cast<'a>(
         if let ExprKind::Call(from_func, [limit]) = &expr.kind
             // `from_type::from, to_type::max_value()`
             // `from_type::from`
-            && let ExprKind::Path(ref path) = &from_func.kind
+            && let ExprKind::Path(path) = &from_func.kind
             && let Some(from_sym) = get_implementing_type(path, INTS, "from")
         {
             Some((limit, from_sym))
diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
index 477435236a5..383fae7992b 100644
--- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
+++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs
@@ -30,7 +30,7 @@ declare_clippy_lint! {
     #[clippy::version = "1.35.0"]
     pub COGNITIVE_COMPLEXITY,
     nursery,
-    "functions that should be split up into multiple functions"
+    "functions that should be split up into multiple functions",
     @eval_always = true
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/comparison_chain.rs b/src/tools/clippy/clippy_lints/src/comparison_chain.rs
index b9baf9af248..c85e3500ebd 100644
--- a/src/tools/clippy/clippy_lints/src/comparison_chain.rs
+++ b/src/tools/clippy/clippy_lints/src/comparison_chain.rs
@@ -110,7 +110,7 @@ impl<'tcx> LateLintPass<'tcx> for ComparisonChain {
                 let is_ord = cx
                     .tcx
                     .get_diagnostic_item(sym::Ord)
-                    .map_or(false, |id| implements_trait(cx, ty, id, &[]));
+                    .is_some_and(|id| implements_trait(cx, ty, id, &[]));
 
                 if !is_ord {
                     return;
diff --git a/src/tools/clippy/clippy_lints/src/copies.rs b/src/tools/clippy/clippy_lints/src/copies.rs
index d90a22bb62a..3ecd36d3711 100644
--- a/src/tools/clippy/clippy_lints/src/copies.rs
+++ b/src/tools/clippy/clippy_lints/src/copies.rs
@@ -345,7 +345,7 @@ fn eq_binding_names(s: &Stmt<'_>, names: &[(HirId, Symbol)]) -> bool {
         let mut i = 0usize;
         let mut res = true;
         l.pat.each_binding_or_first(&mut |_, _, _, name| {
-            if names.get(i).map_or(false, |&(_, n)| n == name.name) {
+            if names.get(i).is_some_and(|&(_, n)| n == name.name) {
                 i += 1;
             } else {
                 res = false;
@@ -389,12 +389,10 @@ fn eq_stmts(
         let new_bindings = &moved_bindings[old_count..];
         blocks
             .iter()
-            .all(|b| get_stmt(b).map_or(false, |s| eq_binding_names(s, new_bindings)))
+            .all(|b| get_stmt(b).is_some_and(|s| eq_binding_names(s, new_bindings)))
     } else {
         true
-    }) && blocks
-        .iter()
-        .all(|b| get_stmt(b).map_or(false, |s| eq.eq_stmt(s, stmt)))
+    }) && blocks.iter().all(|b| get_stmt(b).is_some_and(|s| eq.eq_stmt(s, stmt)))
 }
 
 #[expect(clippy::too_many_lines)]
@@ -451,9 +449,7 @@ fn scan_block_for_eq<'tcx>(
     //     x + 50
     let expr_hash_eq = if let Some(e) = block.expr {
         let hash = hash_expr(cx, e);
-        blocks
-            .iter()
-            .all(|b| b.expr.map_or(false, |e| hash_expr(cx, e) == hash))
+        blocks.iter().all(|b| b.expr.is_some_and(|e| hash_expr(cx, e) == hash))
     } else {
         blocks.iter().all(|b| b.expr.is_none())
     };
@@ -514,7 +510,7 @@ fn scan_block_for_eq<'tcx>(
         });
     if let Some(e) = block.expr {
         for block in blocks {
-            if block.expr.map_or(false, |expr| !eq.eq_expr(expr, e)) {
+            if block.expr.is_some_and(|expr| !eq.eq_expr(expr, e)) {
                 moved_locals.truncate(moved_locals_at_start);
                 return BlockEq {
                     start_end_eq,
@@ -533,31 +529,29 @@ fn scan_block_for_eq<'tcx>(
 }
 
 fn check_for_warn_of_moved_symbol(cx: &LateContext<'_>, symbols: &[(HirId, Symbol)], if_expr: &Expr<'_>) -> bool {
-    get_enclosing_block(cx, if_expr.hir_id).map_or(false, |block| {
+    get_enclosing_block(cx, if_expr.hir_id).is_some_and(|block| {
         let ignore_span = block.span.shrink_to_lo().to(if_expr.span);
 
         symbols
             .iter()
             .filter(|&&(_, name)| !name.as_str().starts_with('_'))
             .any(|&(_, name)| {
-                let mut walker = ContainsName {
-                    name,
-                    result: false,
-                    cx,
-                };
+                let mut walker = ContainsName { name, cx };
 
                 // Scan block
-                block
+                let mut res = block
                     .stmts
                     .iter()
                     .filter(|stmt| !ignore_span.overlaps(stmt.span))
-                    .for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt));
+                    .try_for_each(|stmt| intravisit::walk_stmt(&mut walker, stmt));
 
                 if let Some(expr) = block.expr {
-                    intravisit::walk_expr(&mut walker, expr);
+                    if res.is_continue() {
+                        res = intravisit::walk_expr(&mut walker, expr);
+                    }
                 }
 
-                walker.result
+                res.is_break()
             })
     })
 }
diff --git a/src/tools/clippy/clippy_lints/src/copy_iterator.rs b/src/tools/clippy/clippy_lints/src/copy_iterator.rs
index 50fd76a3a47..4ecf3e41611 100644
--- a/src/tools/clippy/clippy_lints/src/copy_iterator.rs
+++ b/src/tools/clippy/clippy_lints/src/copy_iterator.rs
@@ -37,7 +37,7 @@ declare_lint_pass!(CopyIterator => [COPY_ITERATOR]);
 impl<'tcx> LateLintPass<'tcx> for CopyIterator {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
         if let ItemKind::Impl(Impl {
-            of_trait: Some(ref trait_ref),
+            of_trait: Some(trait_ref),
             ..
         }) = item.kind
             && let ty = cx.tcx.type_of(item.owner_id).instantiate_identity()
diff --git a/src/tools/clippy/clippy_lints/src/ctfe.rs b/src/tools/clippy/clippy_lints/src/ctfe.rs
index 2fe37a64db6..589b99518a0 100644
--- a/src/tools/clippy/clippy_lints/src/ctfe.rs
+++ b/src/tools/clippy/clippy_lints/src/ctfe.rs
@@ -1,29 +1,15 @@
 use rustc_hir::def_id::LocalDefId;
 use rustc_hir::intravisit::FnKind;
 use rustc_hir::{Body, FnDecl};
-use rustc_lint::Level::Deny;
-use rustc_lint::{LateContext, LateLintPass, Lint};
+use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::declare_lint_pass;
 use rustc_span::Span;
 
-/// Ensures that Constant-time Function Evaluation is being done (specifically, MIR lint passes).
-/// As Clippy deactivates codegen, this lint ensures that CTFE (used in hard errors) is still ran.
-pub static CLIPPY_CTFE: &Lint = &Lint {
-    name: &"clippy::CLIPPY_CTFE",
-    default_level: Deny,
-    desc: "Ensure CTFE is being made",
-    edition_lint_opts: None,
-    report_in_external_macro: true,
-    future_incompatible: None,
-    is_externally_loaded: true,
-    crate_level_only: false,
-    eval_always: true,
-    ..Lint::default_fields_for_macro()
-};
-
-// No static CLIPPY_CTFE_INFO because we want this lint to be invisible
-
-declare_lint_pass! { ClippyCtfe => [CLIPPY_CTFE] }
+declare_lint_pass! {
+    /// Ensures that Constant-time Function Evaluation is being done (specifically, MIR lint passes).
+    /// As Clippy deactivates codegen, this lint ensures that CTFE (used in hard errors) is still ran.
+    ClippyCtfe => []
+}
 
 impl<'tcx> LateLintPass<'tcx> for ClippyCtfe {
     fn check_fn(
diff --git a/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs b/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs
index a785a9d377c..4d908af4084 100644
--- a/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs
+++ b/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs
@@ -4,7 +4,7 @@ macro_rules! declare_clippy_lint {
     (@
         $(#[doc = $lit:literal])*
         pub $lint_name:ident,
-        $category:ident,
+        $level:ident,
         $lintcategory:expr,
         $desc:literal,
         $version_expr:expr,
@@ -15,7 +15,7 @@ macro_rules! declare_clippy_lint {
             $(#[doc = $lit])*
             #[clippy::version = $version_lit]
             pub clippy::$lint_name,
-            $category,
+            $level,
             $desc,
             report_in_external_macro:true
             $(, @eval_always = $eval_always)?
@@ -35,12 +35,13 @@ macro_rules! declare_clippy_lint {
         pub $lint_name:ident,
         restriction,
         $desc:literal
-        $(@eval_always = $eval_always: literal)?
+        $(, @eval_always = $eval_always: literal)?
     ) => {
         declare_clippy_lint! {@
             $(#[doc = $lit])*
             pub $lint_name, Allow, crate::LintCategory::Restriction, $desc,
-            Some($version), $version $(, $eval_always)?
+            Some($version), $version
+            $(, $eval_always)?
         }
     };
     (
@@ -49,12 +50,13 @@ macro_rules! declare_clippy_lint {
         pub $lint_name:ident,
         style,
         $desc:literal
-        $(@eval_always = $eval_always: literal)?
+        $(, @eval_always = $eval_always: literal)?
     ) => {
         declare_clippy_lint! {@
             $(#[doc = $lit])*
             pub $lint_name, Warn, crate::LintCategory::Style, $desc,
-            Some($version), $version $(, $eval_always)?
+            Some($version), $version
+            $(, $eval_always)?
         }
     };
     (
@@ -63,12 +65,13 @@ macro_rules! declare_clippy_lint {
         pub $lint_name:ident,
         correctness,
         $desc:literal
-        $(@eval_always = $eval_always: literal)?
+        $(, @eval_always = $eval_always: literal)?
     ) => {
         declare_clippy_lint! {@
             $(#[doc = $lit])*
             pub $lint_name, Deny, crate::LintCategory::Correctness, $desc,
-            Some($version), $version $(, $eval_always)?
+            Some($version), $version
+            $(, $eval_always)?
 
         }
     };
@@ -78,12 +81,13 @@ macro_rules! declare_clippy_lint {
         pub $lint_name:ident,
         perf,
         $desc:literal
-        $(@eval_always = $eval_always: literal)?
+        $(, @eval_always = $eval_always: literal)?
     ) => {
         declare_clippy_lint! {@
             $(#[doc = $lit])*
             pub $lint_name, Warn, crate::LintCategory::Perf, $desc,
-            Some($version), $version $(, $eval_always)?
+            Some($version), $version
+            $(, $eval_always)?
         }
     };
     (
@@ -92,12 +96,13 @@ macro_rules! declare_clippy_lint {
         pub $lint_name:ident,
         complexity,
         $desc:literal
-        $(@eval_always = $eval_always: literal)?
+        $(, @eval_always = $eval_always: literal)?
     ) => {
         declare_clippy_lint! {@
             $(#[doc = $lit])*
             pub $lint_name, Warn, crate::LintCategory::Complexity, $desc,
-            Some($version), $version $(, $eval_always)?
+            Some($version), $version
+            $(, $eval_always)?
         }
     };
     (
@@ -106,12 +111,13 @@ macro_rules! declare_clippy_lint {
         pub $lint_name:ident,
         suspicious,
         $desc:literal
-        $(@eval_always = $eval_always: literal)?
+        $(, @eval_always = $eval_always: literal)?
     ) => {
         declare_clippy_lint! {@
             $(#[doc = $lit])*
             pub $lint_name, Warn, crate::LintCategory::Suspicious, $desc,
-            Some($version), $version $(, $eval_always)?
+            Some($version), $version
+            $(, $eval_always)?
         }
     };
     (
@@ -120,12 +126,13 @@ macro_rules! declare_clippy_lint {
         pub $lint_name:ident,
         nursery,
         $desc:literal
-        $(@eval_always = $eval_always: literal)?
+        $(, @eval_always = $eval_always: literal)?
     ) => {
         declare_clippy_lint! {@
             $(#[doc = $lit])*
             pub $lint_name, Allow, crate::LintCategory::Nursery, $desc,
-            Some($version), $version $(, $eval_always)?
+            Some($version), $version
+            $(, $eval_always)?
         }
     };
     (
@@ -134,12 +141,13 @@ macro_rules! declare_clippy_lint {
         pub $lint_name:ident,
         pedantic,
         $desc:literal
-        $(@eval_always = $eval_always: literal)?
+        $(, @eval_always = $eval_always: literal)?
     ) => {
         declare_clippy_lint! {@
             $(#[doc = $lit])*
             pub $lint_name, Allow, crate::LintCategory::Pedantic, $desc,
-            Some($version), $version $(, $eval_always)?
+            Some($version), $version
+            $(, $eval_always)?
         }
     };
     (
@@ -148,12 +156,13 @@ macro_rules! declare_clippy_lint {
         pub $lint_name:ident,
         cargo,
         $desc:literal
-        $(@eval_always = $eval_always: literal)?
+        $(, @eval_always = $eval_always: literal)?
     ) => {
         declare_clippy_lint! {@
             $(#[doc = $lit])*
             pub $lint_name, Allow, crate::LintCategory::Cargo, $desc,
-            Some($version), $version $(, $eval_always)?
+            Some($version), $version
+            $(, $eval_always)?
         }
     };
 
diff --git a/src/tools/clippy/clippy_lints/src/declared_lints.rs b/src/tools/clippy/clippy_lints/src/declared_lints.rs
index 3c4e75df8ab..dff60f76b74 100644
--- a/src/tools/clippy/clippy_lints/src/declared_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/declared_lints.rs
@@ -28,12 +28,15 @@ pub static LINTS: &[&crate::LintInfo] = &[
     #[cfg(feature = "internal")]
     crate::utils::internal_lints::produce_ice::PRODUCE_ICE_INFO,
     #[cfg(feature = "internal")]
+    crate::utils::internal_lints::slow_symbol_comparisons::SLOW_SYMBOL_COMPARISONS_INFO,
+    #[cfg(feature = "internal")]
     crate::utils::internal_lints::unnecessary_def_path::UNNECESSARY_DEF_PATH_INFO,
     #[cfg(feature = "internal")]
     crate::utils::internal_lints::unsorted_clippy_utils_paths::UNSORTED_CLIPPY_UTILS_PATHS_INFO,
     crate::absolute_paths::ABSOLUTE_PATHS_INFO,
     crate::almost_complete_range::ALMOST_COMPLETE_RANGE_INFO,
     crate::approx_const::APPROX_CONSTANT_INFO,
+    crate::arbitrary_source_item_ordering::ARBITRARY_SOURCE_ITEM_ORDERING_INFO,
     crate::arc_with_non_send_sync::ARC_WITH_NON_SEND_SYNC_INFO,
     crate::as_conversions::AS_CONVERSIONS_INFO,
     crate::asm_syntax::INLINE_ASM_X86_ATT_SYNTAX_INFO,
@@ -414,14 +417,17 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::methods::MANUAL_SPLIT_ONCE_INFO,
     crate::methods::MANUAL_STR_REPEAT_INFO,
     crate::methods::MANUAL_TRY_FOLD_INFO,
+    crate::methods::MAP_ALL_ANY_IDENTITY_INFO,
     crate::methods::MAP_CLONE_INFO,
     crate::methods::MAP_COLLECT_RESULT_UNIT_INFO,
     crate::methods::MAP_ERR_IGNORE_INFO,
     crate::methods::MAP_FLATTEN_INFO,
     crate::methods::MAP_IDENTITY_INFO,
     crate::methods::MAP_UNWRAP_OR_INFO,
+    crate::methods::MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES_INFO,
     crate::methods::MUT_MUTEX_LOCK_INFO,
     crate::methods::NAIVE_BYTECOUNT_INFO,
+    crate::methods::NEEDLESS_AS_BYTES_INFO,
     crate::methods::NEEDLESS_CHARACTER_ITERATION_INFO,
     crate::methods::NEEDLESS_COLLECT_INFO,
     crate::methods::NEEDLESS_OPTION_AS_DEREF_INFO,
@@ -475,6 +481,7 @@ pub static LINTS: &[&crate::LintInfo] = &[
     crate::methods::UNNECESSARY_JOIN_INFO,
     crate::methods::UNNECESSARY_LAZY_EVALUATIONS_INFO,
     crate::methods::UNNECESSARY_LITERAL_UNWRAP_INFO,
+    crate::methods::UNNECESSARY_MAP_OR_INFO,
     crate::methods::UNNECESSARY_MIN_OR_MAX_INFO,
     crate::methods::UNNECESSARY_RESULT_MAP_OR_ELSE_INFO,
     crate::methods::UNNECESSARY_SORT_BY_INFO,
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 4808c372754..ef6b141920d 100644
--- a/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
+++ b/src/tools/clippy/clippy_lints/src/default_numeric_fallback.rs
@@ -253,6 +253,6 @@ impl<'tcx> From<Ty<'tcx>> for ExplicitTyBound {
 
 impl<'tcx> From<Option<Ty<'tcx>>> for ExplicitTyBound {
     fn from(v: Option<Ty<'tcx>>) -> Self {
-        Self(v.map_or(false, Ty::is_numeric))
+        Self(v.is_some_and(Ty::is_numeric))
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/dereference.rs b/src/tools/clippy/clippy_lints/src/dereference.rs
index b167d7f2208..1eebfc22af2 100644
--- a/src/tools/clippy/clippy_lints/src/dereference.rs
+++ b/src/tools/clippy/clippy_lints/src/dereference.rs
@@ -15,9 +15,10 @@ use rustc_hir::{
     self as hir, BindingMode, Body, BodyId, BorrowKind, Expr, ExprKind, HirId, MatchSource, Mutability, Node, Pat,
     PatKind, Path, QPath, TyKind, UnOp,
 };
+use rustc_hir::def_id::DefId;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability};
-use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt, TypeVisitableExt, TypeckResults};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitableExt, TypeckResults};
 use rustc_session::impl_lint_pass;
 use rustc_span::symbol::sym;
 use rustc_span::{Span, Symbol};
@@ -753,10 +754,10 @@ impl TyCoercionStability {
     fn for_defined_ty<'tcx>(cx: &LateContext<'tcx>, ty: DefinedTy<'tcx>, for_return: bool) -> Self {
         match ty {
             DefinedTy::Hir(ty) => Self::for_hir_ty(ty),
-            DefinedTy::Mir(ty) => Self::for_mir_ty(
+            DefinedTy::Mir { def_site_def_id, ty } => Self::for_mir_ty(
                 cx.tcx,
-                ty.param_env,
-                cx.tcx.instantiate_bound_regions_with_erased(ty.value),
+                def_site_def_id,
+                cx.tcx.instantiate_bound_regions_with_erased(ty),
                 for_return,
             ),
         }
@@ -823,12 +824,15 @@ impl TyCoercionStability {
         }
     }
 
-    fn for_mir_ty<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>, for_return: bool) -> Self {
+    fn for_mir_ty<'tcx>(tcx: TyCtxt<'tcx>, def_site_def_id: Option<DefId>, ty: Ty<'tcx>, for_return: bool) -> Self {
         let ty::Ref(_, mut ty, _) = *ty.kind() else {
             return Self::None;
         };
 
-        ty = tcx.try_normalize_erasing_regions(param_env, ty).unwrap_or(ty);
+        if let Some(def_id) = def_site_def_id {
+            let typing_env = ty::TypingEnv::non_body_analysis(tcx, def_id);
+            ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty);
+        }
         loop {
             break match *ty.kind() {
                 ty::Ref(_, ref_ty, _) => {
@@ -959,7 +963,7 @@ fn report<'tcx>(
             // expr_str (the suggestion) is never shown if is_final_ufcs is true, since it's
             // `expr.kind == ExprKind::Call`. Therefore, this is, afaik, always unnecessary.
             /*
-            expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence().order() < PREC_PREFIX {
+            expr_str = if !expr_is_macro_call && is_final_ufcs && expr.precedence() < PREC_PREFIX {
                 Cow::Owned(format!("({expr_str})"))
             } else {
                 expr_str
@@ -999,7 +1003,7 @@ fn report<'tcx>(
                         Node::Expr(e) => match e.kind {
                             ExprKind::Call(callee, _) if callee.hir_id != data.first_expr.hir_id => (0, false),
                             ExprKind::Call(..) => (PREC_UNAMBIGUOUS, matches!(expr.kind, ExprKind::Field(..))),
-                            _ => (e.precedence().order(), false),
+                            _ => (e.precedence(), false),
                         },
                         _ => (0, false),
                     };
@@ -1012,7 +1016,7 @@ fn report<'tcx>(
                     );
 
                     let sugg = if !snip_is_macro
-                        && (calls_field || expr.precedence().order() < precedence)
+                        && (calls_field || expr.precedence() < precedence)
                         && !has_enclosing_paren(&snip)
                         && !is_in_tuple
                     {
@@ -1027,7 +1031,7 @@ fn report<'tcx>(
         State::ExplicitDeref { mutability } => {
             if is_block_like(expr)
                 && let ty::Ref(_, ty, _) = data.adjusted_ty.kind()
-                && ty.is_sized(cx.tcx, cx.param_env)
+                && ty.is_sized(cx.tcx, cx.typing_env())
             {
                 // Rustc bug: auto deref doesn't work on block expression when targeting sized types.
                 return;
@@ -1067,7 +1071,7 @@ fn report<'tcx>(
                     let (snip, snip_is_macro) =
                         snippet_with_context(cx, expr.span, data.first_expr.span.ctxt(), "..", &mut app);
                     let sugg =
-                        if !snip_is_macro && expr.precedence().order() < precedence && !has_enclosing_paren(&snip) {
+                        if !snip_is_macro && expr.precedence() < precedence && !has_enclosing_paren(&snip) {
                             format!("{prefix}({snip})")
                         } else {
                             format!("{prefix}{snip}")
@@ -1154,7 +1158,7 @@ impl<'tcx> Dereferencing<'tcx> {
                         },
                         Some(parent) if !parent.span.from_expansion() => {
                             // Double reference might be needed at this point.
-                            if parent.precedence().order() == PREC_UNAMBIGUOUS {
+                            if parent.precedence() == PREC_UNAMBIGUOUS {
                                 // Parentheses would be needed here, don't lint.
                                 *outer_pat = None;
                             } else {
diff --git a/src/tools/clippy/clippy_lints/src/derivable_impls.rs b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
index 2920bbb4c81..767dda552bc 100644
--- a/src/tools/clippy/clippy_lints/src/derivable_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/derivable_impls.rs
@@ -81,7 +81,7 @@ fn is_path_self(e: &Expr<'_>) -> bool {
 fn contains_trait_object(ty: Ty<'_>) -> bool {
     match ty.kind() {
         ty::Ref(_, ty, _) => contains_trait_object(*ty),
-        ty::Adt(def, args) => def.is_box() && args[0].as_type().map_or(false, contains_trait_object),
+        ty::Adt(def, args) => def.is_box() && args[0].as_type().is_some_and(contains_trait_object),
         ty::Dynamic(..) => true,
         _ => false,
     }
@@ -187,7 +187,7 @@ fn check_enum<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>, func_expr: &Ex
 impl<'tcx> LateLintPass<'tcx> for DerivableImpls {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
         if let ItemKind::Impl(Impl {
-            of_trait: Some(ref trait_ref),
+            of_trait: Some(trait_ref),
             items: [child],
             self_ty,
             ..
diff --git a/src/tools/clippy/clippy_lints/src/derive.rs b/src/tools/clippy/clippy_lints/src/derive.rs
index e8e21edd494..1a34b87e42a 100644
--- a/src/tools/clippy/clippy_lints/src/derive.rs
+++ b/src/tools/clippy/clippy_lints/src/derive.rs
@@ -1,3 +1,5 @@
+use std::ops::ControlFlow;
+
 use clippy_utils::diagnostics::{span_lint_and_note, span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::ty::{implements_trait, implements_trait_with_env, is_copy};
 use clippy_utils::{has_non_exhaustive_attr, is_lint_allowed, match_def_path, paths};
@@ -9,7 +11,6 @@ use rustc_hir::{
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::hir::nested_filter;
-use rustc_middle::traits::Reveal;
 use rustc_middle::ty::{
     self, ClauseKind, GenericArgKind, GenericParamDefKind, ParamEnv, TraitPredicate, Ty, TyCtxt, Upcast,
 };
@@ -202,7 +203,7 @@ declare_lint_pass!(Derive => [
 impl<'tcx> LateLintPass<'tcx> for Derive {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
         if let ItemKind::Impl(Impl {
-            of_trait: Some(ref trait_ref),
+            of_trait: Some(trait_ref),
             ..
         }) = item.kind
         {
@@ -325,7 +326,7 @@ fn check_copy_clone<'tcx>(cx: &LateContext<'tcx>, item: &Item<'_>, trait_ref: &h
     // there's a Copy impl for any instance of the adt.
     if !is_copy(cx, ty) {
         if ty_subs.non_erasable_generics().next().is_some() {
-            let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(&copy_id).map_or(false, |impls| {
+            let has_copy_impl = cx.tcx.all_local_trait_impls(()).get(&copy_id).is_some_and(|impls| {
                 impls.iter().any(|&id| {
                     matches!(cx.tcx.type_of(id).instantiate_identity().kind(), ty::Adt(adt, _)
                                         if ty_adt.did() == adt.did())
@@ -371,9 +372,8 @@ fn check_unsafe_derive_deserialize<'tcx>(
     ty: Ty<'tcx>,
 ) {
     fn has_unsafe<'tcx>(cx: &LateContext<'tcx>, item: &'tcx Item<'_>) -> bool {
-        let mut visitor = UnsafeVisitor { cx, has_unsafe: false };
-        walk_item(&mut visitor, item);
-        visitor.has_unsafe
+        let mut visitor = UnsafeVisitor { cx };
+        walk_item(&mut visitor, item).is_break()
     }
 
     if let Some(trait_def_id) = trait_ref.trait_def_id()
@@ -406,38 +406,37 @@ fn check_unsafe_derive_deserialize<'tcx>(
 
 struct UnsafeVisitor<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
-    has_unsafe: bool,
 }
 
 impl<'tcx> Visitor<'tcx> for UnsafeVisitor<'_, 'tcx> {
+    type Result = ControlFlow<()>;
     type NestedFilter = nested_filter::All;
 
-    fn visit_fn(&mut self, kind: FnKind<'tcx>, decl: &'tcx FnDecl<'_>, body_id: BodyId, _: Span, id: LocalDefId) {
-        if self.has_unsafe {
-            return;
-        }
-
+    fn visit_fn(
+        &mut self,
+        kind: FnKind<'tcx>,
+        decl: &'tcx FnDecl<'_>,
+        body_id: BodyId,
+        _: Span,
+        id: LocalDefId,
+    ) -> Self::Result {
         if let Some(header) = kind.header()
             && header.safety == Safety::Unsafe
         {
-            self.has_unsafe = true;
+            ControlFlow::Break(())
+        } else {
+            walk_fn(self, kind, decl, body_id, id)
         }
-
-        walk_fn(self, kind, decl, body_id, id);
     }
 
-    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) {
-        if self.has_unsafe {
-            return;
-        }
-
+    fn visit_expr(&mut self, expr: &'tcx Expr<'_>) -> Self::Result {
         if let ExprKind::Block(block, _) = expr.kind {
             if block.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
-                self.has_unsafe = true;
+                return ControlFlow::Break(());
             }
         }
 
-        walk_expr(self, expr);
+        walk_expr(self, expr)
     }
 
     fn nested_visit_map(&mut self) -> Self::Map {
@@ -454,13 +453,13 @@ fn check_partial_eq_without_eq<'tcx>(cx: &LateContext<'tcx>, span: Span, trait_r
         && cx.tcx.is_diagnostic_item(sym::PartialEq, def_id)
         && !has_non_exhaustive_attr(cx.tcx, *adt)
         && !ty_implements_eq_trait(cx.tcx, ty, eq_trait_def_id)
-        && let param_env = param_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id)
+        && let typing_env = typing_env_env_for_derived_eq(cx.tcx, adt.did(), eq_trait_def_id)
         && let Some(local_def_id) = adt.did().as_local()
         // If all of our fields implement `Eq`, we can implement `Eq` too
         && adt
             .all_fields()
             .map(|f| f.ty(cx.tcx, args))
-            .all(|ty| implements_trait_with_env(cx.tcx, param_env, ty, eq_trait_def_id, None, &[]))
+            .all(|ty| implements_trait_with_env(cx.tcx, typing_env, ty, eq_trait_def_id, None, &[]))
     {
         span_lint_hir_and_then(
             cx,
@@ -485,7 +484,7 @@ fn ty_implements_eq_trait<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, eq_trait_id: De
 }
 
 /// Creates the `ParamEnv` used for the give type's derived `Eq` impl.
-fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ParamEnv<'_> {
+fn typing_env_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) -> ty::TypingEnv<'_> {
     // Initial map from generic index to param def.
     // Vec<(param_def, needs_eq)>
     let mut params = tcx
@@ -506,7 +505,7 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) ->
         }
     }
 
-    ParamEnv::new(
+    let param_env = ParamEnv::new(
         tcx.mk_clauses_from_iter(ty_predicates.iter().map(|&(p, _)| p).chain(
             params.iter().filter(|&&(_, needs_eq)| needs_eq).map(|&(param, _)| {
                 ClauseKind::Trait(TraitPredicate {
@@ -516,6 +515,9 @@ fn param_env_for_derived_eq(tcx: TyCtxt<'_>, did: DefId, eq_trait_id: DefId) ->
                 .upcast(tcx)
             }),
         )),
-        Reveal::UserFacing,
-    )
+    );
+    ty::TypingEnv {
+        typing_mode: ty::TypingMode::non_body_analysis(),
+        param_env,
+    }
 }
diff --git a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
index b51d343132b..bdd49bf8aa7 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_macros.rs
@@ -2,7 +2,6 @@ use clippy_config::Conf;
 use clippy_utils::create_disallowed_map;
 use clippy_utils::diagnostics::{span_lint_and_then, span_lint_hir_and_then};
 use clippy_utils::macros::macro_backtrace;
-use rustc_ast::Attribute;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_errors::Diag;
 use rustc_hir::def_id::DefIdMap;
@@ -14,6 +13,8 @@ use rustc_middle::ty::TyCtxt;
 use rustc_session::impl_lint_pass;
 use rustc_span::{ExpnId, MacroKind, Span};
 
+use crate::utils::attr_collector::AttrStorage;
+
 declare_clippy_lint! {
     /// ### What it does
     /// Denies the configured macros in clippy.toml
@@ -64,14 +65,19 @@ pub struct DisallowedMacros {
     // Track the most recently seen node that can have a `derive` attribute.
     // Needed to use the correct lint level.
     derive_src: Option<OwnerId>,
+
+    // When a macro is disallowed in an early pass, it's stored
+    // and emitted during the late pass. This happens for attributes.
+    earlies: AttrStorage,
 }
 
 impl DisallowedMacros {
-    pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf) -> Self {
+    pub fn new(tcx: TyCtxt<'_>, conf: &'static Conf, earlies: AttrStorage) -> Self {
         Self {
             disallowed: create_disallowed_map(tcx, &conf.disallowed_macros),
             seen: FxHashSet::default(),
             derive_src: None,
+            earlies,
         }
     }
 
@@ -114,6 +120,15 @@ impl DisallowedMacros {
 impl_lint_pass!(DisallowedMacros => [DISALLOWED_MACROS]);
 
 impl LateLintPass<'_> for DisallowedMacros {
+    fn check_crate(&mut self, cx: &LateContext<'_>) {
+        // once we check a crate in the late pass we can emit the early pass lints
+        if let Some(attr_spans) = self.earlies.clone().0.get() {
+            for span in attr_spans {
+                self.check(cx, *span, None);
+            }
+        }
+    }
+
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
         self.check(cx, expr.span, None);
         // `$t + $t` can have the context of $t, check also the span of the binary operator
@@ -164,8 +179,4 @@ impl LateLintPass<'_> for DisallowedMacros {
     fn check_path(&mut self, cx: &LateContext<'_>, path: &Path<'_>, _: HirId) {
         self.check(cx, path.span, None);
     }
-
-    fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) {
-        self.check(cx, attr.span, self.derive_src);
-    }
 }
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 f79264e6b04..53c24a3faf1 100644
--- a/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
+++ b/src/tools/clippy/clippy_lints/src/disallowed_script_idents.rs
@@ -80,7 +80,7 @@ impl EarlyLintPass for DisallowedScriptIdents {
         let mut symbols: Vec<_> = symbols.iter().collect();
         symbols.sort_unstable_by_key(|k| k.1);
 
-        for (symbol, &span) in &symbols {
+        for &(symbol, &span) in &symbols {
             // 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();
diff --git a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
index c9173030a55..40377dd841e 100644
--- a/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/missing_headers.rs
@@ -47,7 +47,7 @@ pub fn check(
         ),
         _ => (),
     }
-    if !headers.panics && panic_info.map_or(false, |el| !el.1) {
+    if !headers.panics && panic_info.is_some_and(|el| !el.1) {
         span_lint_and_note(
             cx,
             MISSING_PANICS_DOC,
diff --git a/src/tools/clippy/clippy_lints/src/doc/mod.rs b/src/tools/clippy/clippy_lints/src/doc/mod.rs
index 89c6a4e08dc..df7c37a192a 100644
--- a/src/tools/clippy/clippy_lints/src/doc/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/doc/mod.rs
@@ -427,11 +427,11 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
-    /// Checks if the first line in the documentation of items listed in module page is too long.
+    /// Checks if the first paragraph in the documentation of items listed in the module page is too long.
     ///
     /// ### Why is this bad?
-    /// Documentation will show the first paragraph of the doscstring in the summary page of a
-    /// module, so having a nice, short summary in the first paragraph is part of writing good docs.
+    /// Documentation will show the first paragraph of the docstring in the summary page of a
+    /// module. Having a nice, short summary in the first paragraph is part of writing good docs.
     ///
     /// ### Example
     /// ```no_run
@@ -453,7 +453,7 @@ declare_clippy_lint! {
     #[clippy::version = "1.82.0"]
     pub TOO_LONG_FIRST_DOC_PARAGRAPH,
     nursery,
-    "ensure that the first line of a documentation paragraph isn't too long"
+    "ensure the first documentation paragraph is short"
 }
 
 declare_clippy_lint! {
diff --git a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
index c7dd7292a14..617982f4da3 100644
--- a/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/drop_forget_ref.rs
@@ -99,7 +99,7 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
                 sym::mem_forget if is_copy => return,
                 sym::mem_drop if is_type_lang_item(cx, arg_ty, LangItem::ManuallyDrop) => return,
                 sym::mem_drop
-                    if !(arg_ty.needs_drop(cx.tcx, cx.param_env)
+                    if !(arg_ty.needs_drop(cx.tcx, cx.typing_env())
                         || is_must_use_func_call(cx, arg)
                         || is_must_use_ty(cx, arg_ty)
                         || drop_is_single_call_in_arm) =>
@@ -107,12 +107,12 @@ impl<'tcx> LateLintPass<'tcx> for DropForgetRef {
                     (DROP_NON_DROP, DROP_NON_DROP_SUMMARY.into(), Some(arg.span))
                 },
                 sym::mem_forget => {
-                    if arg_ty.needs_drop(cx.tcx, cx.param_env) {
+                    if arg_ty.needs_drop(cx.tcx, cx.typing_env()) {
                         (
                             MEM_FORGET,
                             Cow::Owned(format!(
                                 "usage of `mem::forget` on {}",
-                                if arg_ty.ty_adt_def().map_or(false, |def| def.has_dtor(cx.tcx)) {
+                                if arg_ty.ty_adt_def().is_some_and(|def| def.has_dtor(cx.tcx)) {
                                     "`Drop` type"
                                 } else {
                                     "type with `Drop` fields"
diff --git a/src/tools/clippy/clippy_lints/src/empty_drop.rs b/src/tools/clippy/clippy_lints/src/empty_drop.rs
index b66dd2108fc..10a84b1b2ff 100644
--- a/src/tools/clippy/clippy_lints/src/empty_drop.rs
+++ b/src/tools/clippy/clippy_lints/src/empty_drop.rs
@@ -36,7 +36,7 @@ declare_lint_pass!(EmptyDrop => [EMPTY_DROP]);
 impl LateLintPass<'_> for EmptyDrop {
     fn check_item(&mut self, cx: &LateContext<'_>, item: &Item<'_>) {
         if let ItemKind::Impl(Impl {
-            of_trait: Some(ref trait_ref),
+            of_trait: Some(trait_ref),
             items: [child],
             ..
         }) = item.kind
diff --git a/src/tools/clippy/clippy_lints/src/eta_reduction.rs b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
index 6ca599ed361..6c87a05ace3 100644
--- a/src/tools/clippy/clippy_lints/src/eta_reduction.rs
+++ b/src/tools/clippy/clippy_lints/src/eta_reduction.rs
@@ -9,8 +9,7 @@ use rustc_hir::{BindingMode, Expr, ExprKind, FnRetTy, Param, PatKind, QPath, Saf
 use rustc_infer::infer::TyCtxtInferExt;
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{
-    self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt,
-    TypeckResults,
+    self, Binder, ClosureKind, FnSig, GenericArg, GenericArgKind, List, Region, Ty, TypeVisitableExt, TypeckResults,
 };
 use rustc_session::declare_lint_pass;
 use rustc_span::symbol::sym;
@@ -197,18 +196,23 @@ fn check_clousure<'tcx>(cx: &LateContext<'tcx>, outer_receiver: Option<&Expr<'tc
             {
                 span_lint_and_then(cx, REDUNDANT_CLOSURE, expr.span, "redundant closure", |diag| {
                     if let Some(mut snippet) = snippet_opt(cx, callee.span) {
-                        if path_to_local(callee).map_or(false, |l| {
+                        if path_to_local(callee).is_some_and(|l| {
                             // FIXME: Do we really need this `local_used_in` check?
                             // Isn't it checking something like... `callee(callee)`?
                             // If somehow this check is needed, add some test for it,
                             // 'cuz currently nothing changes after deleting this check.
                             local_used_in(cx, l, args) || local_used_after_expr(cx, l, expr)
                         }) {
-                            match cx.tcx.infer_ctxt().build(cx.typing_mode()).err_ctxt().type_implements_fn_trait(
-                                cx.param_env,
-                                Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
-                                ty::PredicatePolarity::Positive,
-                            ) {
+                            match cx
+                                .tcx
+                                .infer_ctxt()
+                                .build(cx.typing_mode())
+                                .err_ctxt()
+                                .type_implements_fn_trait(
+                                    cx.param_env,
+                                    Binder::bind_with_vars(callee_ty_adjusted, List::empty()),
+                                    ty::PredicatePolarity::Positive,
+                                ) {
                                 // Mutable closure is used after current expr; we cannot consume it.
                                 Ok((ClosureKind::FnMut, _)) => snippet = format!("&mut {snippet}"),
                                 Ok((ClosureKind::Fn, _)) if !callee_ty_raw.is_ref() => {
diff --git a/src/tools/clippy/clippy_lints/src/explicit_write.rs b/src/tools/clippy/clippy_lints/src/explicit_write.rs
index 4e4434ec7d1..0550c22761a 100644
--- a/src/tools/clippy/clippy_lints/src/explicit_write.rs
+++ b/src/tools/clippy/clippy_lints/src/explicit_write.rs
@@ -58,7 +58,7 @@ impl<'tcx> LateLintPass<'tcx> for ExplicitWrite {
             // match call to write_fmt
             && let ExprKind::MethodCall(write_fun, write_recv, [write_arg], _) = *look_in_block(cx, &write_call.kind)
             && let ExprKind::Call(write_recv_path, []) = write_recv.kind
-            && write_fun.ident.name == sym!(write_fmt)
+            && write_fun.ident.name.as_str() == "write_fmt"
             && let Some(def_id) = path_def_id(cx, write_recv_path)
         {
             // match calls to std::io::stdout() / std::io::stderr ()
diff --git a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
index 6ad879b9fe7..81152da8c85 100644
--- a/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
+++ b/src/tools/clippy/clippy_lints/src/extra_unused_type_parameters.rs
@@ -6,7 +6,7 @@ use rustc_errors::Applicability;
 use rustc_hir::intravisit::{Visitor, walk_impl_item, walk_item, walk_param_bound, walk_ty};
 use rustc_hir::{
     BodyId, ExprKind, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem, ImplItemKind, Item, ItemKind,
-    PredicateOrigin, Ty, WherePredicate,
+    PredicateOrigin, Ty, WherePredicate, WherePredicateKind
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::nested_filter;
@@ -205,12 +205,13 @@ impl<'tcx> Visitor<'tcx> for TypeWalker<'_, 'tcx> {
     }
 
     fn visit_where_predicate(&mut self, predicate: &'tcx WherePredicate<'tcx>) {
-        if let WherePredicate::BoundPredicate(predicate) = predicate {
+        let span = predicate.span;
+        if let WherePredicateKind::BoundPredicate(predicate) = predicate.kind {
             // Collect spans for any bounds on type parameters.
             if let Some((def_id, _)) = predicate.bounded_ty.peel_refs().as_generic_param() {
                 match predicate.origin {
                     PredicateOrigin::GenericParam => {
-                        self.inline_bounds.insert(def_id, predicate.span);
+                        self.inline_bounds.insert(def_id, span);
                     },
                     PredicateOrigin::WhereClause => {
                         self.where_bounds.insert(def_id);
diff --git a/src/tools/clippy/clippy_lints/src/from_over_into.rs b/src/tools/clippy/clippy_lints/src/from_over_into.rs
index d5a2e06863d..14da0b515a5 100644
--- a/src/tools/clippy/clippy_lints/src/from_over_into.rs
+++ b/src/tools/clippy/clippy_lints/src/from_over_into.rs
@@ -1,3 +1,5 @@
+use std::ops::ControlFlow;
+
 use clippy_config::Conf;
 use clippy_config::msrvs::{self, Msrv};
 use clippy_utils::diagnostics::span_lint_and_then;
@@ -115,25 +117,26 @@ impl<'tcx> LateLintPass<'tcx> for FromOverInto {
 }
 
 /// Finds the occurrences of `Self` and `self`
+///
+/// Returns `ControlFlow::break` if any of the `self`/`Self` usages were from an expansion, or the
+/// body contained a binding already named `val`.
 struct SelfFinder<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
     /// Occurrences of `Self`
     upper: Vec<Span>,
     /// Occurrences of `self`
     lower: Vec<Span>,
-    /// If any of the `self`/`Self` usages were from an expansion, or the body contained a binding
-    /// already named `val`
-    invalid: bool,
 }
 
 impl<'tcx> Visitor<'tcx> for SelfFinder<'_, 'tcx> {
+    type Result = ControlFlow<()>;
     type NestedFilter = OnlyBodies;
 
     fn nested_visit_map(&mut self) -> Self::Map {
         self.cx.tcx.hir()
     }
 
-    fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) {
+    fn visit_path(&mut self, path: &Path<'tcx>, _id: HirId) -> Self::Result {
         for segment in path.segments {
             match segment.ident.name {
                 kw::SelfLower => self.lower.push(segment.ident.span),
@@ -141,17 +144,19 @@ impl<'tcx> Visitor<'tcx> for SelfFinder<'_, 'tcx> {
                 _ => continue,
             }
 
-            self.invalid |= segment.ident.span.from_expansion();
+            if segment.ident.span.from_expansion() {
+                return ControlFlow::Break(());
+            }
         }
 
-        if !self.invalid {
-            walk_path(self, path);
-        }
+        walk_path(self, path)
     }
 
-    fn visit_name(&mut self, name: Symbol) {
+    fn visit_name(&mut self, name: Symbol) -> Self::Result {
         if name == sym::val {
-            self.invalid = true;
+            ControlFlow::Break(())
+        } else {
+            ControlFlow::Continue(())
         }
     }
 }
@@ -209,11 +214,9 @@ fn convert_to_from(
         cx,
         upper: Vec::new(),
         lower: Vec::new(),
-        invalid: false,
     };
-    finder.visit_expr(body.value);
 
-    if finder.invalid {
+    if finder.visit_expr(body.value).is_break() {
         return None;
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs
index d62d008d480..c8828c93615 100644
--- a/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/from_raw_with_void_ptr.rs
@@ -41,7 +41,7 @@ impl LateLintPass<'_> for FromRawWithVoidPtr {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &Expr<'_>) {
         if let ExprKind::Call(box_from_raw, [arg]) = expr.kind
             && let ExprKind::Path(QPath::TypeRelative(ty, seg)) = box_from_raw.kind
-            && seg.ident.name == sym!(from_raw)
+            && seg.ident.name.as_str() == "from_raw"
             && let Some(type_str) = path_def_id(cx, ty).and_then(|id| def_id_matches_type(cx, id))
             && let arg_kind = cx.typeck_results().expr_ty(arg).kind()
             && let ty::RawPtr(ty, _) = arg_kind
diff --git a/src/tools/clippy/clippy_lints/src/functions/mod.rs b/src/tools/clippy/clippy_lints/src/functions/mod.rs
index 91d73c2a9c9..be3d0f7ad63 100644
--- a/src/tools/clippy/clippy_lints/src/functions/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/mod.rs
@@ -441,7 +441,7 @@ declare_clippy_lint! {
     /// fn bar(&self) -> Option<&String> { None }
     /// # }
     /// ```
-    #[clippy::version = "1.82.0"]
+    #[clippy::version = "1.83.0"]
     pub REF_OPTION,
     pedantic,
     "function signature uses `&Option<T>` instead of `Option<&T>`"
diff --git a/src/tools/clippy/clippy_lints/src/functions/must_use.rs b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
index c74ba088b78..175d92d2d79 100644
--- a/src/tools/clippy/clippy_lints/src/functions/must_use.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/must_use.rs
@@ -198,7 +198,7 @@ fn is_mutable_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>, tys: &mut DefIdSet)
         // primitive types are never mutable
         ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Str => false,
         ty::Adt(adt, args) => {
-            tys.insert(adt.did()) && !ty.is_freeze(cx.tcx, cx.param_env)
+            tys.insert(adt.did()) && !ty.is_freeze(cx.tcx, cx.typing_env())
                 || matches!(cx.tcx.get_diagnostic_name(adt.did()), Some(sym::Rc | sym::Arc))
                     && args.types().any(|ty| is_mutable_ty(cx, ty, tys))
         },
diff --git a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
index c2b40ae01d4..4986a311eba 100644
--- a/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/not_unsafe_ptr_arg_deref.rs
@@ -87,7 +87,7 @@ fn raw_ptr_arg(cx: &LateContext<'_>, arg: &hir::Param<'_>) -> Option<HirId> {
 }
 
 fn check_arg(cx: &LateContext<'_>, raw_ptrs: &HirIdSet, arg: &hir::Expr<'_>) {
-    if path_to_local(arg).map_or(false, |id| raw_ptrs.contains(&id)) {
+    if path_to_local(arg).is_some_and(|id| raw_ptrs.contains(&id)) {
         span_lint(
             cx,
             NOT_UNSAFE_PTR_ARG_DEREF,
diff --git a/src/tools/clippy/clippy_lints/src/functions/ref_option.rs b/src/tools/clippy/clippy_lints/src/functions/ref_option.rs
index 373ecd74cb3..aba0fbcb9fe 100644
--- a/src/tools/clippy/clippy_lints/src/functions/ref_option.rs
+++ b/src/tools/clippy/clippy_lints/src/functions/ref_option.rs
@@ -15,9 +15,9 @@ use rustc_span::{Span, sym};
 fn check_ty<'a>(cx: &LateContext<'a>, param: &rustc_hir::Ty<'a>, param_ty: Ty<'a>, fixes: &mut Vec<(Span, String)>) {
     if let ty::Ref(_, opt_ty, Mutability::Not) = param_ty.kind()
         && is_type_diagnostic_item(cx, *opt_ty, sym::Option)
-        && let ty::Adt(_, opt_gen) = opt_ty.kind()
-        && let [gen] = opt_gen.as_slice()
-        && let GenericArgKind::Type(gen_ty) = gen.unpack()
+        && let ty::Adt(_, opt_gen_args) = opt_ty.kind()
+        && let [gen_arg] = opt_gen_args.as_slice()
+        && let GenericArgKind::Type(gen_ty) = gen_arg.unpack()
         && !gen_ty.is_ref()
         // Need to gen the original spans, so first parsing mid, and hir parsing afterward
         && let hir::TyKind::Ref(lifetime, hir::MutTy { ty, .. }) = param.kind
diff --git a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
index 4c5375730b8..c370f206c8f 100644
--- a/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
+++ b/src/tools/clippy/clippy_lints/src/implicit_hasher.rs
@@ -340,7 +340,7 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> {
             if self.cx.tcx.is_diagnostic_item(sym::HashMap, ty_did) {
                 if method.ident.name == sym::new {
                     self.suggestions.insert(e.span, "HashMap::default()".to_string());
-                } else if method.ident.name == sym!(with_capacity) {
+                } else if method.ident.name.as_str() == "with_capacity" {
                     self.suggestions.insert(
                         e.span,
                         format!(
@@ -352,7 +352,7 @@ impl<'tcx> Visitor<'tcx> for ImplicitHasherConstructorVisitor<'_, '_, 'tcx> {
             } else if self.cx.tcx.is_diagnostic_item(sym::HashSet, ty_did) {
                 if method.ident.name == sym::new {
                     self.suggestions.insert(e.span, "HashSet::default()".to_string());
-                } else if method.ident.name == sym!(with_capacity) {
+                } else if method.ident.name.as_str() == "with_capacity" {
                     self.suggestions.insert(
                         e.span,
                         format!(
diff --git a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
index 65fdc93e0ed..4427edb752e 100644
--- a/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
+++ b/src/tools/clippy/clippy_lints/src/implied_bounds_in_impls.rs
@@ -4,7 +4,7 @@ use rustc_errors::{Applicability, SuggestionStyle};
 use rustc_hir::def_id::DefId;
 use rustc_hir::{
     AssocItemConstraint, GenericArg, GenericBound, GenericBounds, PredicateOrigin, TraitBoundModifiers, TyKind,
-    WherePredicate,
+    WherePredicateKind,
 };
 use rustc_hir_analysis::lower_ty;
 use rustc_lint::{LateContext, LateLintPass};
@@ -324,7 +324,7 @@ fn check<'tcx>(cx: &LateContext<'tcx>, bounds: GenericBounds<'tcx>) {
 impl<'tcx> LateLintPass<'tcx> for ImpliedBoundsInImpls {
     fn check_generics(&mut self, cx: &LateContext<'tcx>, generics: &rustc_hir::Generics<'tcx>) {
         for predicate in generics.predicates {
-            if let WherePredicate::BoundPredicate(predicate) = predicate
+            if let WherePredicateKind::BoundPredicate(predicate) = predicate.kind
                 // In theory, the origin doesn't really matter,
                 // we *could* also lint on explicit where clauses written out by the user,
                 // not just impl trait desugared ones, but that contradicts with the lint name...
diff --git a/src/tools/clippy/clippy_lints/src/infinite_iter.rs b/src/tools/clippy/clippy_lints/src/infinite_iter.rs
index 71c317552ee..d3aade31f14 100644
--- a/src/tools/clippy/clippy_lints/src/infinite_iter.rs
+++ b/src/tools/clippy/clippy_lints/src/infinite_iter.rs
@@ -157,7 +157,7 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
                     .and(cap);
                 }
             }
-            if method.ident.name == sym!(flat_map) && args.len() == 1 {
+            if method.ident.name.as_str() == "flat_map" && args.len() == 1 {
                 if let ExprKind::Closure(&Closure { body, .. }) = args[0].kind {
                     let body = cx.tcx.hir().body(body);
                     return is_infinite(cx, body.value);
@@ -171,13 +171,13 @@ fn is_infinite(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
             if let ExprKind::Path(ref qpath) = path.kind {
                 cx.qpath_res(qpath, path.hir_id)
                     .opt_def_id()
-                    .map_or(false, |id| cx.tcx.is_diagnostic_item(sym::iter_repeat, id))
+                    .is_some_and(|id| cx.tcx.is_diagnostic_item(sym::iter_repeat, id))
                     .into()
             } else {
                 Finite
             }
         },
-        ExprKind::Struct(..) => higher::Range::hir(expr).map_or(false, |r| r.end.is_none()).into(),
+        ExprKind::Struct(..) => higher::Range::hir(expr).is_some_and(|r| r.end.is_none()).into(),
         _ => Finite,
     }
 }
@@ -224,17 +224,15 @@ fn complete_infinite_iter(cx: &LateContext<'_>, expr: &Expr<'_>) -> Finiteness {
                     return MaybeInfinite.and(is_infinite(cx, receiver));
                 }
             }
-            if method.ident.name == sym!(last) && args.is_empty() {
+            if method.ident.name.as_str() == "last" && args.is_empty() {
                 let not_double_ended = cx
                     .tcx
                     .get_diagnostic_item(sym::DoubleEndedIterator)
-                    .map_or(false, |id| {
-                        !implements_trait(cx, cx.typeck_results().expr_ty(receiver), id, &[])
-                    });
+                    .is_some_and(|id| !implements_trait(cx, cx.typeck_results().expr_ty(receiver), id, &[]));
                 if not_double_ended {
                     return is_infinite(cx, receiver);
                 }
-            } else if method.ident.name == sym!(collect) {
+            } else if method.ident.name.as_str() == "collect" {
                 let ty = cx.typeck_results().expr_ty(expr);
                 if matches!(
                     get_type_diagnostic_name(cx, ty),
diff --git a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
index dd90e2a6e94..6363f717a5c 100644
--- a/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
+++ b/src/tools/clippy/clippy_lints/src/item_name_repetitions.rs
@@ -309,8 +309,8 @@ fn check_enum_start(cx: &LateContext<'_>, item_name: &str, variant: &Variant<'_>
     let item_name_chars = item_name.chars().count();
 
     if count_match_start(item_name, name).char_count == item_name_chars
-        && name.chars().nth(item_name_chars).map_or(false, |c| !c.is_lowercase())
-        && name.chars().nth(item_name_chars + 1).map_or(false, |c| !c.is_numeric())
+        && name.chars().nth(item_name_chars).is_some_and(|c| !c.is_lowercase())
+        && name.chars().nth(item_name_chars + 1).is_some_and(|c| !c.is_numeric())
     {
         span_lint_hir(
             cx,
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 b19b348c743..4bc6ad0798c 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
@@ -70,12 +70,12 @@ fn check_sig(cx: &LateContext<'_>, name: Symbol, sig: &FnSig<'_>, fn_id: LocalDe
             .instantiate_bound_regions_with_erased(cx.tcx.fn_sig(fn_id).instantiate_identity().output());
         let ret_ty = cx
             .tcx
-            .try_normalize_erasing_regions(cx.param_env, ret_ty)
+            .try_normalize_erasing_regions(cx.typing_env(), ret_ty)
             .unwrap_or(ret_ty);
         if cx
             .tcx
             .get_diagnostic_item(sym::Iterator)
-            .map_or(false, |iter_id| !implements_trait(cx, ret_ty, iter_id, &[]))
+            .is_some_and(|iter_id| !implements_trait(cx, ret_ty, iter_id, &[]))
         {
             span_lint(
                 cx,
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 1e6404190d3..906da81b183 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
@@ -142,7 +142,7 @@ impl LateLintPass<'_> for IterWithoutIntoIter {
                 ty.peel_refs().is_slice() || get_adt_inherent_method(cx, ty, expected_method_name).is_some()
             })
             && let Some(iter_assoc_span) = imp.items.iter().find_map(|item| {
-                if item.ident.name == sym!(IntoIter) {
+                if item.ident.name.as_str() == "IntoIter" {
                     Some(cx.tcx.hir().impl_item(item.id).expect_type().span)
                 } else {
                     None
@@ -215,7 +215,7 @@ impl {self_ty_without_ref} {{
             && implements_trait(cx, ret_ty, iterator_did, &[])
             && let Some(iter_ty) = make_normalized_projection(
                 cx.tcx,
-                cx.param_env,
+                cx.typing_env(),
                 iterator_did,
                 sym::Item,
                 [ret_ty],
@@ -247,8 +247,8 @@ impl {self_ty_without_ref} {{
                     let sugg = format!(
                         "
 impl IntoIterator for {self_ty_snippet} {{
-    type IntoIter = {ret_ty};
     type Item = {iter_ty};
+    type IntoIter = {ret_ty};
     fn into_iter(self) -> Self::IntoIter {{
         self.iter()
     }}
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 5d2b521b250..644365c9fe5 100644
--- a/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
+++ b/src/tools/clippy/clippy_lints/src/large_const_arrays.rs
@@ -4,7 +4,7 @@ use rustc_errors::Applicability;
 use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::layout::LayoutOf;
-use rustc_middle::ty::{self, ConstKind};
+use rustc_middle::ty;
 use rustc_session::impl_lint_pass;
 use rustc_span::{BytePos, Pos, Span};
 
@@ -56,7 +56,8 @@ impl<'tcx> LateLintPass<'tcx> for LargeConstArrays {
             && !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()
+            && let Some((ty::ValTree::Leaf(element_count), _)) = cx.tcx
+                .try_normalize_erasing_regions(cx.typing_env(), *cst).unwrap_or(*cst).try_to_valtree()
             && let element_count = element_count.to_target_usize(cx.tcx)
             && let Ok(element_size) = cx.layout_of(*element_type).map(|l| l.size.bytes())
             && u128::from(self.maximum_allowed_size) < u128::from(element_count) * u128::from(element_size)
diff --git a/src/tools/clippy/clippy_lints/src/large_futures.rs b/src/tools/clippy/clippy_lints/src/large_futures.rs
index 25f9be8b2d7..593704f206a 100644
--- a/src/tools/clippy/clippy_lints/src/large_futures.rs
+++ b/src/tools/clippy/clippy_lints/src/large_futures.rs
@@ -63,7 +63,7 @@ impl<'tcx> LateLintPass<'tcx> for LargeFuture {
             && 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 Ok(layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(ty))
             && let size = layout.layout.size()
             && size >= Size::from_bytes(self.future_size_threshold)
         {
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 f2f841dcec3..4f22931a4de 100644
--- a/src/tools/clippy/clippy_lints/src/large_include_file.rs
+++ b/src/tools/clippy/clippy_lints/src/large_include_file.rs
@@ -1,11 +1,12 @@
 use clippy_config::Conf;
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::macros::root_macro_call_first_node;
-use rustc_ast::LitKind;
+use clippy_utils::source::snippet_opt;
+use rustc_ast::{AttrArgs, AttrArgsEq, AttrKind, Attribute, LitKind};
 use rustc_hir::{Expr, ExprKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
-use rustc_span::sym;
+use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
     /// ### What it does
@@ -51,6 +52,24 @@ impl LargeIncludeFile {
 
 impl_lint_pass!(LargeIncludeFile => [LARGE_INCLUDE_FILE]);
 
+impl LargeIncludeFile {
+    fn emit_lint(&self, cx: &LateContext<'_>, span: Span) {
+        #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
+        span_lint_and_then(
+            cx,
+            LARGE_INCLUDE_FILE,
+            span,
+            "attempted to include a large file",
+            |diag| {
+                diag.note(format!(
+                    "the configuration allows a maximum size of {} bytes",
+                    self.max_file_size
+                ));
+            },
+        );
+    }
+}
+
 impl LateLintPass<'_> for LargeIncludeFile {
     fn check_expr(&mut self, cx: &LateContext<'_>, expr: &'_ Expr<'_>) {
         if let ExprKind::Lit(lit) = &expr.kind
@@ -66,19 +85,35 @@ impl LateLintPass<'_> for LargeIncludeFile {
             && (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))
         {
-            #[expect(clippy::collapsible_span_lint_calls, reason = "rust-clippy#7797")]
-            span_lint_and_then(
-                cx,
-                LARGE_INCLUDE_FILE,
-                expr.span.source_callsite(),
-                "attempted to include a large file",
-                |diag| {
-                    diag.note(format!(
-                        "the configuration allows a maximum size of {} bytes",
-                        self.max_file_size
-                    ));
-                },
-            );
+            self.emit_lint(cx, expr.span.source_callsite());
+        }
+    }
+
+    fn check_attribute(&mut self, cx: &LateContext<'_>, attr: &Attribute) {
+        if !attr.span.from_expansion()
+            // Currently, rustc limits the usage of macro at the top-level of attributes,
+            // so we don't need to recurse into each level.
+            && let AttrKind::Normal(ref normal) = attr.kind
+            && let Some(doc) = attr.doc_str()
+            && doc.as_str().len() as u64 > self.max_file_size
+            && let AttrArgs::Eq(_, AttrArgsEq::Hir(ref meta)) = normal.item.args
+            && !attr.span.contains(meta.span)
+            // Since the `include_str` is already expanded at this point, we can only take the
+            // whole attribute snippet and then modify for our suggestion.
+            && let Some(snippet) = snippet_opt(cx, attr.span)
+            // We cannot remove this because a `#[doc = include_str!("...")]` attribute can
+            // occupy several lines.
+            && let Some(start) = snippet.find('[')
+            && let Some(end) = snippet.rfind(']')
+            && let snippet = &snippet[start + 1..end]
+            // We check that the expansion actually comes from `include_str!` and not just from
+            // another macro.
+            && let Some(sub_snippet) = snippet.trim().strip_prefix("doc")
+            && let Some(sub_snippet) = sub_snippet.trim().strip_prefix("=")
+            && let sub_snippet = sub_snippet.trim()
+            && (sub_snippet.starts_with("include_str!") || sub_snippet.starts_with("include_bytes!"))
+        {
+            self.emit_lint(cx, attr.span);
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs
index d2bdf194ada..5ed948c02bb 100644
--- a/src/tools/clippy/clippy_lints/src/large_stack_frames.rs
+++ b/src/tools/clippy/clippy_lints/src/large_stack_frames.rs
@@ -150,11 +150,11 @@ impl<'tcx> LateLintPass<'tcx> for LargeStackFrames {
         }
 
         let mir = cx.tcx.optimized_mir(def_id);
-        let param_env = cx.tcx.param_env(def_id);
+        let typing_env = mir.typing_env(cx.tcx);
 
         let sizes_of_locals = || {
             mir.local_decls.iter().filter_map(|local| {
-                let layout = cx.tcx.layout_of(param_env.and(local.ty)).ok()?;
+                let layout = cx.tcx.layout_of(typing_env.as_query_input(local.ty)).ok()?;
                 Some((local, layout.size.bytes()))
             })
         };
diff --git a/src/tools/clippy/clippy_lints/src/len_zero.rs b/src/tools/clippy/clippy_lints/src/len_zero.rs
index 47c65ee6d0b..3ea758e176f 100644
--- a/src/tools/clippy/clippy_lints/src/len_zero.rs
+++ b/src/tools/clippy/clippy_lints/src/len_zero.rs
@@ -622,14 +622,14 @@ fn has_is_empty(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
 
     let ty = &cx.typeck_results().expr_ty(expr).peel_refs();
     match ty.kind() {
-        ty::Dynamic(tt, ..) => tt.principal().map_or(false, |principal| {
+        ty::Dynamic(tt, ..) => tt.principal().is_some_and(|principal| {
             let is_empty = sym!(is_empty);
             cx.tcx
                 .associated_items(principal.def_id())
                 .filter_by_name_unhygienic(is_empty)
                 .any(|item| is_is_empty(cx, item))
         }),
-        ty::Alias(ty::Projection, ref proj) => has_is_empty_impl(cx, proj.def_id),
+        ty::Alias(ty::Projection, proj) => has_is_empty_impl(cx, proj.def_id),
         ty::Adt(id, _) => has_is_empty_impl(cx, id.did()),
         ty::Array(..) | ty::Slice(..) | ty::Str => true,
         _ => false,
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 0e488cee6b7..5db28e9ae9b 100644
--- a/src/tools/clippy/clippy_lints/src/let_if_seq.rs
+++ b/src/tools/clippy/clippy_lints/src/let_if_seq.rs
@@ -80,7 +80,7 @@ impl<'tcx> LateLintPass<'tcx> for LetIfSeq {
                 let has_interior_mutability = !cx
                     .typeck_results()
                     .node_type(canonical_id)
-                    .is_freeze(cx.tcx, cx.param_env);
+                    .is_freeze(cx.tcx, cx.typing_env());
                 if has_interior_mutability {
                     return;
                 }
diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs
index 14110539709..c9064df25ac 100644
--- a/src/tools/clippy/clippy_lints/src/lib.rs
+++ b/src/tools/clippy/clippy_lints/src/lib.rs
@@ -73,6 +73,7 @@ pub mod deprecated_lints;
 mod absolute_paths;
 mod almost_complete_range;
 mod approx_const;
+mod arbitrary_source_item_ordering;
 mod arc_with_non_send_sync;
 mod as_conversions;
 mod asm_syntax;
@@ -400,6 +401,7 @@ use clippy_config::{Conf, get_configuration_metadata, sanitize_explanation};
 use clippy_utils::macros::FormatArgsStorage;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_lint::{Lint, LintId};
+use utils::attr_collector::{AttrCollector, AttrStorage};
 
 /// Register all pre expansion lints
 ///
@@ -412,6 +414,8 @@ use rustc_lint::{Lint, LintId};
 pub fn register_pre_expansion_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     // NOTE: Do not add any more pre-expansion passes. These should be removed eventually.
     store.register_pre_expansion_pass(move || Box::new(attrs::EarlyAttributes::new(conf)));
+
+    store.register_early_pass(move || Box::new(attrs::PostExpansionEarlyAttributes::new(conf)));
 }
 
 #[derive(Default)]
@@ -462,6 +466,7 @@ pub(crate) enum LintCategory {
     #[cfg(feature = "internal")]
     Internal,
 }
+
 #[allow(clippy::enum_glob_use)]
 use LintCategory::*;
 
@@ -583,6 +588,10 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
         ))
     });
 
+    let attr_storage = AttrStorage::default();
+    let attrs = attr_storage.clone();
+    store.register_early_pass(move || Box::new(AttrCollector::new(attrs.clone())));
+
     // all the internal lints
     #[cfg(feature = "internal")]
     {
@@ -604,6 +613,7 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
         store.register_late_pass(|_| {
             Box::new(utils::internal_lints::almost_standard_lint_formulation::AlmostStandardFormulation::new())
         });
+        store.register_late_pass(|_| Box::new(utils::internal_lints::slow_symbol_comparisons::SlowSymbolComparisons));
     }
 
     store.register_late_pass(|_| Box::new(ctfe::ClippyCtfe));
@@ -793,7 +803,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(|_| Box::new(unwrap_in_result::UnwrapInResult));
     store.register_late_pass(|_| Box::new(semicolon_if_nothing_returned::SemicolonIfNothingReturned));
     store.register_late_pass(|_| Box::new(async_yields_async::AsyncYieldsAsync));
-    store.register_late_pass(move |tcx| Box::new(disallowed_macros::DisallowedMacros::new(tcx, conf)));
+    let attrs = attr_storage.clone();
+    store.register_late_pass(move |tcx| Box::new(disallowed_macros::DisallowedMacros::new(tcx, conf, attrs.clone())));
     store.register_late_pass(move |tcx| Box::new(disallowed_methods::DisallowedMethods::new(tcx, conf)));
     store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86AttSyntax));
     store.register_early_pass(|| Box::new(asm_syntax::InlineAsmX86IntelSyntax));
@@ -951,5 +962,6 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) {
     store.register_late_pass(move |_| Box::new(unused_trait_names::UnusedTraitNames::new(conf)));
     store.register_late_pass(|_| Box::new(manual_ignore_case_cmp::ManualIgnoreCaseCmp));
     store.register_late_pass(|_| Box::new(unnecessary_literal_bound::UnnecessaryLiteralBound));
+    store.register_late_pass(move |_| Box::new(arbitrary_source_item_ordering::ArbitrarySourceItemOrdering::new(conf)));
     // add lints here, do not remove this comment, it's used in `new_lint`
 }
diff --git a/src/tools/clippy/clippy_lints/src/lifetimes.rs b/src/tools/clippy/clippy_lints/src/lifetimes.rs
index d55be2b036a..6ff1a1e5ec7 100644
--- a/src/tools/clippy/clippy_lints/src/lifetimes.rs
+++ b/src/tools/clippy/clippy_lints/src/lifetimes.rs
@@ -12,7 +12,7 @@ use rustc_hir::intravisit::{
 use rustc_hir::{
     BareFnTy, BodyId, FnDecl, FnSig, GenericArg, GenericArgs, GenericBound, GenericParam, GenericParamKind, Generics,
     Impl, ImplItem, ImplItemKind, Item, ItemKind, Lifetime, LifetimeName, LifetimeParamKind, Node, PolyTraitRef,
-    PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, lang_items,
+    PredicateOrigin, TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate, WherePredicateKind, lang_items,
 };
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::hir::map::Map;
@@ -442,9 +442,9 @@ impl<'tcx> Visitor<'tcx> for RefVisitor<'_, 'tcx> {
 /// reason about elision.
 fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_>) -> bool {
     for predicate in generics.predicates {
-        match *predicate {
-            WherePredicate::RegionPredicate(..) => return true,
-            WherePredicate::BoundPredicate(ref pred) => {
+        match *predicate.kind {
+            WherePredicateKind::RegionPredicate(..) => return true,
+            WherePredicateKind::BoundPredicate(ref pred) => {
                 // a predicate like F: Trait or F: for<'a> Trait<'a>
                 let mut visitor = RefVisitor::new(cx);
                 // walk the type F, it may not contain LT refs
@@ -467,7 +467,7 @@ fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_
                     }
                 }
             },
-            WherePredicate::EqPredicate(ref pred) => {
+            WherePredicateKind::EqPredicate(ref pred) => {
                 let mut visitor = RefVisitor::new(cx);
                 walk_ty(&mut visitor, pred.lhs_ty);
                 walk_ty(&mut visitor, pred.rhs_ty);
@@ -737,7 +737,7 @@ fn elision_suggestions(
     suggestions.extend(
         usages
             .iter()
-            .filter(|usage| named_lifetime(usage).map_or(false, |id| elidable_lts.contains(&id)))
+            .filter(|usage| named_lifetime(usage).is_some_and(|id| elidable_lts.contains(&id)))
             .map(|usage| {
                 match cx.tcx.parent_hir_node(usage.hir_id) {
                     Node::Ty(Ty {
diff --git a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
index 119e410b91c..d999e1a0585 100644
--- a/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/explicit_iter_loop.rs
@@ -29,11 +29,7 @@ pub(super) fn check(
             if !msrv.meets(msrvs::ARRAY_INTO_ITERATOR) {
                 return;
             }
-        } else if count
-            .try_to_target_usize(cx.tcx)
-            .map_or(true, |x| x > 32)
-            && !msrv.meets(msrvs::ARRAY_IMPL_ANY_LEN)
-        {
+        } else if count.try_to_target_usize(cx.tcx).map_or(true, |x| x > 32) && !msrv.meets(msrvs::ARRAY_IMPL_ANY_LEN) {
             return;
         }
     }
@@ -119,11 +115,11 @@ fn is_ref_iterable<'tcx>(
             .tcx
             .liberate_late_bound_regions(fn_id, cx.tcx.fn_sig(fn_id).skip_binder())
         && let &[req_self_ty, req_res_ty] = &**sig.inputs_and_output
-        && let param_env = cx.tcx.param_env(fn_id)
-        && implements_trait_with_env(cx.tcx, param_env, req_self_ty, trait_id, Some(fn_id), &[])
+        && let typing_env = ty::TypingEnv::non_body_analysis(cx.tcx, fn_id)
+        && implements_trait_with_env(cx.tcx, typing_env, req_self_ty, trait_id, Some(fn_id), &[])
         && let Some(into_iter_ty) =
-            make_normalized_projection_with_regions(cx.tcx, param_env, trait_id, sym!(IntoIter), [req_self_ty])
-        && let req_res_ty = normalize_with_regions(cx.tcx, param_env, req_res_ty)
+            make_normalized_projection_with_regions(cx.tcx, typing_env, trait_id, sym!(IntoIter), [req_self_ty])
+        && let req_res_ty = normalize_with_regions(cx.tcx, typing_env, req_res_ty)
         && into_iter_ty == req_res_ty
     {
         let adjustments = typeck.expr_adjustments(self_arg);
@@ -155,7 +151,7 @@ fn is_ref_iterable<'tcx>(
                 // Using by value won't consume anything
                 if implements_trait(cx, self_ty, trait_id, &[])
                     && let Some(ty) =
-                        make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty])
+                        make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [self_ty])
                     && ty == res_ty
                 {
                     return Some((AdjustKind::None, self_ty));
@@ -172,7 +168,7 @@ fn is_ref_iterable<'tcx>(
                 };
                 if implements_trait(cx, self_ty, trait_id, &[])
                     && let Some(ty) =
-                        make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty])
+                        make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [self_ty])
                     && ty == res_ty
                 {
                     return Some((AdjustKind::reborrow(mutbl), self_ty));
@@ -185,7 +181,7 @@ fn is_ref_iterable<'tcx>(
             // Attempt to borrow
             let self_ty = Ty::new_ref(cx.tcx, cx.tcx.lifetimes.re_erased, self_ty, mutbl);
             if implements_trait(cx, self_ty, trait_id, &[])
-                && let Some(ty) = make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [self_ty])
+                && let Some(ty) = make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [self_ty])
                 && ty == res_ty
             {
                 return Some((AdjustKind::borrow(mutbl), self_ty));
@@ -208,7 +204,7 @@ fn is_ref_iterable<'tcx>(
                     && target != self_ty
                     && implements_trait(cx, target, trait_id, &[])
                     && let Some(ty) =
-                        make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target])
+                        make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [target])
                     && ty == res_ty
                 {
                     Some((AdjustKind::auto_reborrow(mutbl), target))
@@ -226,7 +222,7 @@ fn is_ref_iterable<'tcx>(
                 if is_copy(cx, target)
                     && implements_trait(cx, target, trait_id, &[])
                     && let Some(ty) =
-                        make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target])
+                        make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [target])
                     && ty == res_ty
                 {
                     Some((AdjustKind::Deref, target))
@@ -244,7 +240,7 @@ fn is_ref_iterable<'tcx>(
                 if self_ty.is_ref()
                     && implements_trait(cx, target, trait_id, &[])
                     && let Some(ty) =
-                        make_normalized_projection(cx.tcx, cx.param_env, trait_id, sym!(IntoIter), [target])
+                        make_normalized_projection(cx.tcx, cx.typing_env(), trait_id, sym!(IntoIter), [target])
                     && ty == res_ty
                 {
                     Some((AdjustKind::auto_borrow(mutbl), target))
diff --git a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs
index e25c03db534..9f543b79bac 100644
--- a/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/infinite_loop.rs
@@ -1,12 +1,13 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use clippy_utils::{fn_def_id, is_from_proc_macro, is_lint_allowed};
 use hir::intravisit::{Visitor, walk_expr};
-use hir::{Expr, ExprKind, FnRetTy, FnSig, Node};
+use hir::{Expr, ExprKind, FnRetTy, FnSig, Node, TyKind};
 use rustc_ast::Label;
 use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
+use rustc_span::sym;
 
 use super::INFINITE_LOOP;
 
@@ -25,13 +26,7 @@ pub(super) fn check<'tcx>(
         return;
     };
     // Or, its parent function is already returning `Never`
-    if matches!(
-        parent_fn_ret,
-        FnRetTy::Return(hir::Ty {
-            kind: hir::TyKind::Never,
-            ..
-        })
-    ) {
+    if is_never_return(parent_fn_ret) {
         return;
     }
 
@@ -69,6 +64,16 @@ pub(super) fn check<'tcx>(
 fn get_parent_fn_ret_ty<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'_>) -> Option<FnRetTy<'tcx>> {
     for (_, parent_node) in cx.tcx.hir().parent_iter(expr.hir_id) {
         match parent_node {
+            // Skip `Coroutine` closures, these are the body of `async fn`, not async closures.
+            // This is because we still need to backtrack one parent node to get the `OpaqueDef` ty.
+            Node::Expr(Expr {
+                kind:
+                    ExprKind::Closure(hir::Closure {
+                        kind: hir::ClosureKind::Coroutine(_),
+                        ..
+                    }),
+                ..
+            }) => (),
             Node::Item(hir::Item {
                 kind: hir::ItemKind::Fn(FnSig { decl, .. }, _, _),
                 ..
@@ -143,3 +148,41 @@ impl<'hir> Visitor<'hir> for LoopVisitor<'hir, '_> {
         }
     }
 }
+
+/// Return `true` if the given [`FnRetTy`] is never (!).
+///
+/// Note: This function also take care of return type of async fn,
+/// as the actual type is behind an [`OpaqueDef`](TyKind::OpaqueDef).
+fn is_never_return(ret_ty: FnRetTy<'_>) -> bool {
+    let FnRetTy::Return(hir_ty) = ret_ty else { return false };
+
+    match hir_ty.kind {
+        TyKind::Never => true,
+        TyKind::OpaqueDef(hir::OpaqueTy {
+            origin: hir::OpaqueTyOrigin::AsyncFn { .. },
+            bounds,
+            ..
+        }) => {
+            if let Some(trait_ref) = bounds.iter().find_map(|b| b.trait_ref())
+                && let Some(segment) = trait_ref
+                    .path
+                    .segments
+                    .iter()
+                    .find(|seg| seg.ident.name == sym::future_trait)
+                && let Some(args) = segment.args
+                && let Some(cst_kind) = args
+                    .constraints
+                    .iter()
+                    .find_map(|cst| (cst.ident.name == sym::Output).then_some(cst.kind))
+                && let hir::AssocItemConstraintKind::Equality {
+                    term: hir::Term::Ty(ty),
+                } = cst_kind
+            {
+                matches!(ty.kind, TyKind::Never)
+            } else {
+                false
+            }
+        },
+        _ => false,
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
index bfe2e68b5d1..1721f569541 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_find.rs
@@ -58,7 +58,7 @@ pub(super) fn check<'tcx>(
             .tcx
             .lang_items()
             .copy_trait()
-            .map_or(false, |id| implements_trait(cx, ty, id, &[]))
+            .is_some_and(|id| implements_trait(cx, ty, id, &[]))
         {
             snippet.push_str(
                 &format!(
diff --git a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
index af089451759..701567a7d84 100644
--- a/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/manual_memcpy.rs
@@ -416,7 +416,7 @@ fn get_assignments<'a, 'tcx>(
         .chain(*expr)
         .filter(move |e| {
             if let ExprKind::AssignOp(_, place, _) = e.kind {
-                path_to_local(place).map_or(false, |id| {
+                path_to_local(place).is_some_and(|id| {
                     !loop_counters
                         .iter()
                         // skip the first item which should be `StartKind::Range`
diff --git a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
index 21f9a71f2c5..39e5e140b7a 100644
--- a/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/mut_range_bound.rs
@@ -80,7 +80,7 @@ impl<'tcx> Delegate<'tcx> for MutatePairDelegate<'_, 'tcx> {
     fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
 
     fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) {
-        if bk == ty::BorrowKind::MutBorrow {
+        if bk == ty::BorrowKind::Mutable {
             if let PlaceBase::Local(id) = cmt.place.base {
                 if Some(id) == self.hir_id_low && !BreakAfterExprVisitor::is_found(self.cx, diag_expr_id) {
                     self.span_low = Some(self.cx.tcx.hir().span(diag_expr_id));
@@ -126,7 +126,7 @@ impl BreakAfterExprVisitor {
             break_after_expr: false,
         };
 
-        get_enclosing_block(cx, hir_id).map_or(false, |block| {
+        get_enclosing_block(cx, hir_id).is_some_and(|block| {
             visitor.visit_block(block);
             visitor.break_after_expr
         })
diff --git a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
index d255fea3af2..951ebc9caef 100644
--- a/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/same_item_push.rs
@@ -50,7 +50,7 @@ pub(super) fn check<'tcx>(
             .tcx
             .lang_items()
             .clone_trait()
-            .map_or(false, |id| implements_trait(cx, ty, id, &[]))
+            .is_some_and(|id| implements_trait(cx, ty, id, &[]))
     {
         // Make sure that the push does not involve possibly mutating values
         match pushed_item.kind {
diff --git a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs
index 70f76ced09a..35dc8e9aa4e 100644
--- a/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/single_element_loop.rs
@@ -84,7 +84,7 @@ pub(super) fn check<'tcx>(
         if !prefix.is_empty()
             && (
                 // Precedence of internal expression is less than or equal to precedence of `&expr`.
-                arg_expression.precedence().order() <= PREC_PREFIX || is_range_literal(arg_expression)
+                arg_expression.precedence() <= PREC_PREFIX || is_range_literal(arg_expression)
             )
         {
             arg_snip = format!("({arg_snip})").into();
diff --git a/src/tools/clippy/clippy_lints/src/loops/utils.rs b/src/tools/clippy/clippy_lints/src/loops/utils.rs
index c4c504e1ae4..51fde5288ab 100644
--- a/src/tools/clippy/clippy_lints/src/loops/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/utils.rs
@@ -256,9 +256,10 @@ fn is_conditional(expr: &Expr<'_>) -> bool {
 /// If `arg` was the argument to a `for` loop, return the "cleanest" way of writing the
 /// actual `Iterator` that the loop uses.
 pub(super) fn make_iterator_snippet(cx: &LateContext<'_>, arg: &Expr<'_>, applic_ref: &mut Applicability) -> String {
-    let impls_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| {
-        implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[])
-    });
+    let impls_iterator = cx
+        .tcx
+        .get_diagnostic_item(sym::Iterator)
+        .is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(arg), id, &[]));
     if impls_iterator {
         format!(
             "{}",
diff --git a/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs b/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs
index 1a1cde3c5bd..7da4fa76e2c 100644
--- a/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/while_immutable_condition.rs
@@ -19,10 +19,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, cond: &'tcx Expr<'_>, expr: &'
         cx,
         ids: HirIdSet::default(),
         def_ids: DefIdMap::default(),
-        skip: false,
     };
-    var_visitor.visit_expr(cond);
-    if var_visitor.skip {
+    if var_visitor.visit_expr(cond).is_break() {
         return;
     }
     let used_in_condition = &var_visitor.ids;
@@ -81,7 +79,6 @@ struct VarCollectorVisitor<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
     ids: HirIdSet,
     def_ids: DefIdMap<bool>,
-    skip: bool,
 }
 
 impl<'tcx> VarCollectorVisitor<'_, 'tcx> {
@@ -104,11 +101,15 @@ impl<'tcx> VarCollectorVisitor<'_, 'tcx> {
 }
 
 impl<'tcx> Visitor<'tcx> for VarCollectorVisitor<'_, 'tcx> {
-    fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
+    type Result = ControlFlow<()>;
+    fn visit_expr(&mut self, ex: &'tcx Expr<'_>) -> Self::Result {
         match ex.kind {
-            ExprKind::Path(_) => self.insert_def_id(ex),
+            ExprKind::Path(_) => {
+                self.insert_def_id(ex);
+                ControlFlow::Continue(())
+            },
             // If there is any function/method call… we just stop analysis
-            ExprKind::Call(..) | ExprKind::MethodCall(..) => self.skip = true,
+            ExprKind::Call(..) | ExprKind::MethodCall(..) => ControlFlow::Break(()),
 
             _ => walk_expr(self, ex),
         }
diff --git a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
index 74467522619..b7e37c1a876 100644
--- a/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
+++ b/src/tools/clippy/clippy_lints/src/loops/while_let_on_iterator.rs
@@ -1,3 +1,5 @@
+use std::ops::ControlFlow;
+
 use super::WHILE_LET_ON_ITERATOR;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::source::snippet_with_applicability;
@@ -204,35 +206,32 @@ fn uses_iter<'tcx>(cx: &LateContext<'tcx>, iter_expr: &IterExpr, container: &'tc
     struct V<'a, 'b, 'tcx> {
         cx: &'a LateContext<'tcx>,
         iter_expr: &'b IterExpr,
-        uses_iter: bool,
     }
     impl<'tcx> Visitor<'tcx> for V<'_, '_, 'tcx> {
-        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
-            if self.uses_iter {
-                // return
-            } else if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) {
-                self.uses_iter = true;
+        type Result = ControlFlow<()>;
+        fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result {
+            if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) {
+                ControlFlow::Break(())
             } else if let (e, true) = skip_fields_and_path(e) {
                 if let Some(e) = e {
-                    self.visit_expr(e);
+                    self.visit_expr(e)
+                } else {
+                    ControlFlow::Continue(())
                 }
             } else if let ExprKind::Closure(&Closure { body: id, .. }) = e.kind {
                 if is_res_used(self.cx, self.iter_expr.path, id) {
-                    self.uses_iter = true;
+                    ControlFlow::Break(())
+                } else {
+                    ControlFlow::Continue(())
                 }
             } else {
-                walk_expr(self, e);
+                walk_expr(self, e)
             }
         }
     }
 
-    let mut v = V {
-        cx,
-        iter_expr,
-        uses_iter: false,
-    };
-    v.visit_expr(container);
-    v.uses_iter
+    let mut v = V { cx, iter_expr };
+    v.visit_expr(container).is_break()
 }
 
 #[expect(clippy::too_many_lines)]
@@ -242,34 +241,38 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &
         iter_expr: &'b IterExpr,
         loop_id: HirId,
         after_loop: bool,
-        used_iter: bool,
     }
     impl<'tcx> Visitor<'tcx> for AfterLoopVisitor<'_, '_, 'tcx> {
         type NestedFilter = OnlyBodies;
+        type Result = ControlFlow<()>;
         fn nested_visit_map(&mut self) -> Self::Map {
             self.cx.tcx.hir()
         }
 
-        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
-            if self.used_iter {
-                return;
-            }
+        fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result {
             if self.after_loop {
                 if is_expr_same_child_or_parent_field(self.cx, e, &self.iter_expr.fields, self.iter_expr.path) {
-                    self.used_iter = true;
+                    ControlFlow::Break(())
                 } else if let (e, true) = skip_fields_and_path(e) {
                     if let Some(e) = e {
-                        self.visit_expr(e);
+                        self.visit_expr(e)
+                    } else {
+                        ControlFlow::Continue(())
                     }
                 } else if let ExprKind::Closure(&Closure { body: id, .. }) = e.kind {
-                    self.used_iter = is_res_used(self.cx, self.iter_expr.path, id);
+                    if is_res_used(self.cx, self.iter_expr.path, id) {
+                        ControlFlow::Break(())
+                    } else {
+                        ControlFlow::Continue(())
+                    }
                 } else {
-                    walk_expr(self, e);
+                    walk_expr(self, e)
                 }
             } else if self.loop_id == e.hir_id {
                 self.after_loop = true;
+                ControlFlow::Continue(())
             } else {
-                walk_expr(self, e);
+                walk_expr(self, e)
             }
         }
     }
@@ -347,9 +350,8 @@ fn needs_mutable_borrow(cx: &LateContext<'_>, iter_expr: &IterExpr, loop_expr: &
             iter_expr,
             loop_id: loop_expr.hir_id,
             after_loop: false,
-            used_iter: false,
         };
-        v.visit_expr(cx.tcx.hir().body(cx.enclosing_body.unwrap()).value);
-        v.used_iter
+        v.visit_expr(cx.tcx.hir().body(cx.enclosing_body.unwrap()).value)
+            .is_break()
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
index c904137da1a..3c77db84a40 100644
--- a/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_async_fn.rs
@@ -106,13 +106,12 @@ impl<'tcx> LateLintPass<'tcx> for ManualAsyncFn {
 
 fn future_trait_ref<'tcx>(cx: &LateContext<'tcx>, opaque: &'tcx OpaqueTy<'tcx>) -> Option<&'tcx TraitRef<'tcx>> {
     if let Some(trait_ref) = opaque.bounds.iter().find_map(|bound| {
-            if let GenericBound::Trait(poly) = bound {
-                Some(&poly.trait_ref)
-            } else {
-                None
-            }
-        })
-        && trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait()
+        if let GenericBound::Trait(poly) = bound {
+            Some(&poly.trait_ref)
+        } else {
+            None
+        }
+    }) && trait_ref.trait_def_id() == cx.tcx.lang_items().future_trait()
     {
         return Some(trait_ref);
     }
@@ -156,16 +155,18 @@ fn captures_all_lifetimes(cx: &LateContext<'_>, fn_def_id: LocalDefId, opaque_de
         .tcx
         .opaque_captured_lifetimes(opaque_def_id)
         .iter()
-        .filter(|&(lifetime, _)| match *lifetime {
-            ResolvedArg::EarlyBound(_) | ResolvedArg::LateBound(ty::INNERMOST, _, _) => true,
-            _ => false,
+        .filter(|&(lifetime, _)| {
+            matches!(
+                *lifetime,
+                ResolvedArg::EarlyBound(_) | ResolvedArg::LateBound(ty::INNERMOST, _, _)
+            )
         })
         .count();
     num_captured_lifetimes == num_early_lifetimes + num_late_lifetimes
 }
 
 fn desugared_async_block<'tcx>(cx: &LateContext<'tcx>, block: &'tcx Block<'tcx>) -> Option<&'tcx Body<'tcx>> {
-    if let Some(Expr {
+    if let Some(&Expr {
         kind: ExprKind::Closure(&Closure { kind, body, .. }),
         ..
     }) = block.expr
diff --git a/src/tools/clippy/clippy_lints/src/manual_bits.rs b/src/tools/clippy/clippy_lints/src/manual_bits.rs
index 1bd8813e348..fd71167f814 100644
--- a/src/tools/clippy/clippy_lints/src/manual_bits.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_bits.rs
@@ -7,8 +7,7 @@ use rustc_ast::ast::LitKind;
 use rustc_data_structures::packed::Pu128;
 use rustc_errors::Applicability;
 use rustc_hir::{BinOpKind, Expr, ExprKind, GenericArg, QPath};
-use rustc_lint::{LateContext, LateLintPass, LintContext};
-use rustc_middle::lint::in_external_macro;
+use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{self, Ty};
 use rustc_session::impl_lint_pass;
 use rustc_span::sym;
@@ -53,7 +52,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualBits {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
         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)
+            && !expr.span.from_expansion()
             && self.msrv.meets(msrvs::MANUAL_BITS)
             && let ctxt = expr.span.ctxt()
             && left_expr.span.ctxt() == ctxt
diff --git a/src/tools/clippy/clippy_lints/src/manual_clamp.rs b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
index fd66cacdfe9..016ec7320a6 100644
--- a/src/tools/clippy/clippy_lints/src/manual_clamp.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_clamp.rs
@@ -226,7 +226,7 @@ impl TypeClampability {
         } else if cx
             .tcx
             .get_diagnostic_item(sym::Ord)
-            .map_or(false, |id| implements_trait(cx, ty, id, &[]))
+            .is_some_and(|id| implements_trait(cx, ty, id, &[]))
         {
             Some(TypeClampability::Ord)
         } else {
diff --git a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs
index c62bd65bea6..7a9c9963742 100644
--- a/src/tools/clippy/clippy_lints/src/manual_hash_one.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_hash_one.rs
@@ -68,7 +68,7 @@ impl LateLintPass<'_> for ManualHashOne {
             && let Some(init) = local.init
             && !init.span.from_expansion()
             && let ExprKind::MethodCall(seg, build_hasher, [], _) = init.kind
-            && seg.ident.name == sym!(build_hasher)
+            && seg.ident.name.as_str() == "build_hasher"
 
             && let Node::Stmt(local_stmt) = cx.tcx.parent_hir_node(local.hir_id)
             && let Node::Block(block) = cx.tcx.parent_hir_node(local_stmt.hir_id)
@@ -96,7 +96,7 @@ impl LateLintPass<'_> for ManualHashOne {
             && let Node::Expr(finish_expr) = cx.tcx.parent_hir_node(path_expr.hir_id)
             && !finish_expr.span.from_expansion()
             && let ExprKind::MethodCall(seg, _, [], _) = finish_expr.kind
-            && seg.ident.name == sym!(finish)
+            && seg.ident.name.as_str() == "finish"
 
             && self.msrv.meets(msrvs::BUILD_HASHER_HASH_ONE)
         {
diff --git a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
index 24207705743..dec8c5d85de 100644
--- a/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_is_ascii_check.rs
@@ -105,7 +105,7 @@ impl<'tcx> LateLintPass<'tcx> for ManualIsAsciiCheck {
                 check_is_ascii(cx, macro_call.span, recv, &range, None);
             }
         } else if let ExprKind::MethodCall(path, receiver, [arg], ..) = expr.kind
-            && path.ident.name == sym!(contains)
+            && path.ident.name.as_str() == "contains"
             && let Some(higher::Range {
                 start: Some(start),
                 end: Some(end),
diff --git a/src/tools/clippy/clippy_lints/src/manual_strip.rs b/src/tools/clippy/clippy_lints/src/manual_strip.rs
index 828c5a3f6ff..3f401eff6bd 100644
--- a/src/tools/clippy/clippy_lints/src/manual_strip.rs
+++ b/src/tools/clippy/clippy_lints/src/manual_strip.rs
@@ -162,9 +162,9 @@ fn eq_pattern_length<'tcx>(cx: &LateContext<'tcx>, pattern: &Expr<'_>, expr: &'t
         ..
     }) = expr.kind
     {
-        constant_length(cx, pattern).map_or(false, |length| *n == length)
+        constant_length(cx, pattern).is_some_and(|length| *n == length)
     } else {
-        len_arg(cx, expr).map_or(false, |arg| eq_expr_value(cx, pattern, arg))
+        len_arg(cx, expr).is_some_and(|arg| eq_expr_value(cx, pattern, arg))
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
index d38560998a5..9c6df4d8ac0 100644
--- a/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/manual_utils.rs
@@ -117,7 +117,7 @@ where
     // it's being passed by value.
     let scrutinee = peel_hir_expr_refs(scrutinee).0;
     let (scrutinee_str, _) = snippet_with_context(cx, scrutinee.span, expr_ctxt, "..", &mut app);
-    let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence().order() < PREC_UNAMBIGUOUS {
+    let scrutinee_str = if scrutinee.span.eq_ctxt(expr.span) && scrutinee.precedence() < PREC_UNAMBIGUOUS {
         format!("({scrutinee_str})")
     } else {
         scrutinee_str.into()
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs
index 64cb7a06ce9..47472ab831f 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_like_matches.rs
@@ -74,7 +74,7 @@ where
         && b0 != b1
         && (first_guard.is_none() || iter.len() == 0)
         && first_attrs.is_empty()
-        && iter.all(|arm| find_bool_lit(&arm.2.kind).map_or(false, |b| b == b0) && arm.3.is_none() && arm.0.is_empty())
+        && iter.all(|arm| find_bool_lit(&arm.2.kind).is_some_and(|b| b == b0) && arm.3.is_none() && arm.0.is_empty())
     {
         if let Some(last_pat) = last_pat_opt {
             if !is_wild(last_pat) {
@@ -148,7 +148,7 @@ fn find_bool_lit(ex: &ExprKind<'_>) -> Option<bool> {
         }) => Some(*b),
         ExprKind::Block(
             rustc_hir::Block {
-                stmts: &[],
+                stmts: [],
                 expr: Some(exp),
                 ..
             },
diff --git a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs
index 463aa602bc8..1267fc9d0a5 100644
--- a/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/match_str_case_mismatch.rs
@@ -1,3 +1,5 @@
+use std::ops::ControlFlow;
+
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::ty::is_type_lang_item;
 use rustc_ast::ast::LitKind;
@@ -23,11 +25,8 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arm
     if let ty::Ref(_, ty, _) = cx.typeck_results().expr_ty(scrutinee).kind()
         && let ty::Str = ty.kind()
     {
-        let mut visitor = MatchExprVisitor { cx, case_method: None };
-
-        visitor.visit_expr(scrutinee);
-
-        if let Some(case_method) = visitor.case_method {
+        let mut visitor = MatchExprVisitor { cx };
+        if let ControlFlow::Break(case_method) = visitor.visit_expr(scrutinee) {
             if let Some((bad_case_span, bad_case_sym)) = verify_case(&case_method, arms) {
                 lint(cx, &case_method, bad_case_span, bad_case_sym.as_str());
             }
@@ -37,30 +36,33 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, scrutinee: &'tcx Expr<'_>, arm
 
 struct MatchExprVisitor<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
-    case_method: Option<CaseMethod>,
 }
 
 impl<'tcx> Visitor<'tcx> for MatchExprVisitor<'_, 'tcx> {
-    fn visit_expr(&mut self, ex: &'tcx Expr<'_>) {
-        match ex.kind {
-            ExprKind::MethodCall(segment, receiver, [], _) if self.case_altered(segment.ident.as_str(), receiver) => {},
-            _ => walk_expr(self, ex),
+    type Result = ControlFlow<CaseMethod>;
+    fn visit_expr(&mut self, ex: &'tcx Expr<'_>) -> Self::Result {
+        if let ExprKind::MethodCall(segment, receiver, [], _) = ex.kind {
+            let result = self.case_altered(segment.ident.as_str(), receiver);
+            if result.is_break() {
+                return result;
+            }
         }
+
+        walk_expr(self, ex)
     }
 }
 
 impl MatchExprVisitor<'_, '_> {
-    fn case_altered(&mut self, segment_ident: &str, receiver: &Expr<'_>) -> bool {
+    fn case_altered(&mut self, segment_ident: &str, receiver: &Expr<'_>) -> ControlFlow<CaseMethod> {
         if let Some(case_method) = get_case_method(segment_ident) {
             let ty = self.cx.typeck_results().expr_ty(receiver).peel_refs();
 
             if is_type_lang_item(self.cx, ty, LangItem::String) || ty.kind() == &ty::Str {
-                self.case_method = Some(case_method);
-                return true;
+                return ControlFlow::Break(case_method);
             }
         }
 
-        false
+        ControlFlow::Continue(())
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
index 42d9efe4ff6..9e54475033c 100644
--- a/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/redundant_guards.rs
@@ -10,7 +10,7 @@ use rustc_hir::def::{DefKind, Res};
 use rustc_hir::{Arm, BinOpKind, Expr, ExprKind, MatchSource, Node, PatKind, UnOp};
 use rustc_lint::LateContext;
 use rustc_span::symbol::Ident;
-use rustc_span::{Span, Symbol, sym};
+use rustc_span::{Span, sym};
 use std::borrow::Cow;
 use std::ops::ControlFlow;
 
@@ -95,7 +95,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv:
         } else if let ExprKind::MethodCall(path, recv, args, ..) = guard.kind
             && let Some(binding) = get_pat_binding(cx, recv, outer_arm)
         {
-            check_method_calls(cx, outer_arm, path.ident.name, recv, args, guard, &binding);
+            check_method_calls(cx, outer_arm, path.ident.name.as_str(), recv, args, guard, &binding);
         }
     }
 }
@@ -103,7 +103,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, arms: &'tcx [Arm<'tcx>], msrv:
 fn check_method_calls<'tcx>(
     cx: &LateContext<'tcx>,
     arm: &Arm<'tcx>,
-    method: Symbol,
+    method: &str,
     recv: &Expr<'_>,
     args: &[Expr<'_>],
     if_expr: &Expr<'_>,
@@ -112,7 +112,7 @@ fn check_method_calls<'tcx>(
     let ty = cx.typeck_results().expr_ty(recv).peel_refs();
     let slice_like = ty.is_slice() || ty.is_array();
 
-    let sugg = if method == sym!(is_empty) {
+    let sugg = if method == "is_empty" {
         // `s if s.is_empty()` becomes ""
         // `arr if arr.is_empty()` becomes []
 
@@ -137,9 +137,9 @@ fn check_method_calls<'tcx>(
 
         if needles.is_empty() {
             sugg.insert_str(1, "..");
-        } else if method == sym!(starts_with) {
+        } else if method == "starts_with" {
             sugg.insert_str(sugg.len() - 1, ", ..");
-        } else if method == sym!(ends_with) {
+        } else if method == "ends_with" {
             sugg.insert_str(1, ".., ");
         } else {
             return;
diff --git a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
index 9ca75fb2615..264458a86ef 100644
--- a/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/redundant_pattern_match.rs
@@ -319,10 +319,9 @@ fn found_good_method<'tcx>(
     node: (&PatKind<'_>, &PatKind<'_>),
 ) -> Option<(&'static str, Option<&'tcx Expr<'tcx>>)> {
     match node {
-        (
-            PatKind::TupleStruct(ref path_left, patterns_left, _),
-            PatKind::TupleStruct(ref path_right, patterns_right, _),
-        ) if patterns_left.len() == 1 && patterns_right.len() == 1 => {
+        (PatKind::TupleStruct(path_left, patterns_left, _), PatKind::TupleStruct(path_right, patterns_right, _))
+            if patterns_left.len() == 1 && patterns_right.len() == 1 =>
+        {
             if let (PatKind::Wild, PatKind::Wild) = (&patterns_left[0].kind, &patterns_right[0].kind) {
                 find_good_method_for_match(
                     cx,
@@ -350,8 +349,8 @@ fn found_good_method<'tcx>(
                 None
             }
         },
-        (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Path(ref path_right))
-        | (PatKind::Path(ref path_left), PatKind::TupleStruct(ref path_right, patterns, _))
+        (PatKind::TupleStruct(path_left, patterns, _), PatKind::Path(path_right))
+        | (PatKind::Path(path_left), PatKind::TupleStruct(path_right, patterns, _))
             if patterns.len() == 1 =>
         {
             if let PatKind::Wild = patterns[0].kind {
@@ -381,14 +380,14 @@ fn found_good_method<'tcx>(
                 None
             }
         },
-        (PatKind::TupleStruct(ref path_left, patterns, _), PatKind::Wild) if patterns.len() == 1 => {
+        (PatKind::TupleStruct(path_left, patterns, _), PatKind::Wild) if patterns.len() == 1 => {
             if let PatKind::Wild = patterns[0].kind {
                 get_good_method(cx, arms, path_left)
             } else {
                 None
             }
         },
-        (PatKind::Path(ref path_left), PatKind::Wild) => get_good_method(cx, arms, path_left),
+        (PatKind::Path(path_left), PatKind::Wild) => get_good_method(cx, arms, path_left),
         _ => None,
     }
 }
@@ -448,7 +447,7 @@ fn is_pat_variant(cx: &LateContext<'_>, pat: &Pat<'_>, path: &QPath<'_>, expecte
             .tcx
             .lang_items()
             .get(expected_lang_item)
-            .map_or(false, |expected_id| cx.tcx.parent(id) == expected_id),
+            .is_some_and(|expected_id| cx.tcx.parent(id) == expected_id),
         Item::Diag(expected_ty, expected_variant) => {
             let ty = cx.typeck_results().pat_ty(pat);
 
diff --git a/src/tools/clippy/clippy_lints/src/matches/single_match.rs b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
index 047d070a131..95a4bf6f60d 100644
--- a/src/tools/clippy/clippy_lints/src/matches/single_match.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/single_match.rs
@@ -175,7 +175,7 @@ impl<'tcx> Visitor<'tcx> for PatVisitor<'tcx> {
         if matches!(pat.kind, PatKind::Binding(..)) {
             ControlFlow::Break(())
         } else {
-            self.has_enum |= self.typeck.pat_ty(pat).ty_adt_def().map_or(false, AdtDef::is_enum);
+            self.has_enum |= self.typeck.pat_ty(pat).ty_adt_def().is_some_and(AdtDef::is_enum);
             walk_pat(self, pat)
         }
     }
@@ -247,7 +247,10 @@ impl<'a> PatState<'a> {
         let states = match self {
             Self::Wild => return None,
             Self::Other => {
-                *self = Self::StdEnum(cx.arena.alloc_from_iter((0..adt.variants().len()).map(|_| Self::Other)));
+                *self = Self::StdEnum(
+                    cx.arena
+                        .alloc_from_iter(std::iter::repeat_with(|| Self::Other).take(adt.variants().len())),
+                );
                 let Self::StdEnum(x) = self else {
                     unreachable!();
                 };
diff --git a/src/tools/clippy/clippy_lints/src/matches/try_err.rs b/src/tools/clippy/clippy_lints/src/matches/try_err.rs
index c7e1b70d19e..6c02207af49 100644
--- a/src/tools/clippy/clippy_lints/src/matches/try_err.rs
+++ b/src/tools/clippy/clippy_lints/src/matches/try_err.rs
@@ -58,7 +58,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>, scrutine
                 let span = hygiene::walk_chain(err_arg.span, try_arg.span.ctxt());
                 let mut applicability = Applicability::MachineApplicable;
                 let origin_snippet = snippet_with_applicability(cx, span, "_", &mut applicability);
-                let ret_prefix = if get_parent_expr(cx, expr).map_or(false, |e| matches!(e.kind, ExprKind::Ret(_))) {
+                let ret_prefix = if get_parent_expr(cx, expr).is_some_and(|e| matches!(e.kind, ExprKind::Ret(_))) {
                     "" // already returns
                 } else {
                     "return "
diff --git a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
index c288dbdabe9..6dc48c26ba9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/expect_fun_call.rs
@@ -83,7 +83,7 @@ pub(super) fn check<'tcx>(
             hir::ExprKind::MethodCall(..) => {
                 cx.typeck_results()
                     .type_dependent_def_id(arg.hir_id)
-                    .map_or(false, |method_id| {
+                    .is_some_and(|method_id| {
                         matches!(
                             cx.tcx.fn_sig(method_id).instantiate_identity().output().skip_binder().kind(),
                             ty::Ref(re, ..) if re.is_static()
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs
index 06482a743dd..c1653b65e98 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_map.rs
@@ -21,8 +21,8 @@ fn is_method(cx: &LateContext<'_>, expr: &Expr<'_>, method_name: Symbol) -> bool
         ExprKind::Path(QPath::TypeRelative(_, mname)) => mname.ident.name == method_name,
         ExprKind::Path(QPath::Resolved(_, segments)) => segments.segments.last().unwrap().ident.name == method_name,
         ExprKind::MethodCall(segment, _, _, _) => segment.ident.name == method_name,
-        ExprKind::Closure(&Closure { body, .. }) => {
-            let body = cx.tcx.hir().body(body);
+        ExprKind::Closure(Closure { body, .. }) => {
+            let body = cx.tcx.hir().body(*body);
             let closure_expr = peel_blocks(body.value);
             match closure_expr.kind {
                 ExprKind::MethodCall(PathSegment { ident, .. }, receiver, ..) => {
@@ -234,12 +234,12 @@ impl<'tcx> OffendingFilterExpr<'tcx> {
             // the latter only calls `effect` once
             let side_effect_expr_span = receiver.can_have_side_effects().then_some(receiver.span);
 
-            if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) && path.ident.name == sym!(is_some) {
+            if cx.tcx.is_diagnostic_item(sym::Option, recv_ty.did()) && path.ident.name.as_str() == "is_some" {
                 Some(Self::IsSome {
                     receiver,
                     side_effect_expr_span,
                 })
-            } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) && path.ident.name == sym!(is_ok) {
+            } else if cx.tcx.is_diagnostic_item(sym::Result, recv_ty.did()) && path.ident.name.as_str() == "is_ok" {
                 Some(Self::IsOk {
                     receiver,
                     side_effect_expr_span,
diff --git a/src/tools/clippy/clippy_lints/src/methods/filter_next.rs b/src/tools/clippy/clippy_lints/src/methods/filter_next.rs
index e697ba656f5..6c1a14fc882 100644
--- a/src/tools/clippy/clippy_lints/src/methods/filter_next.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/filter_next.rs
@@ -32,9 +32,10 @@ pub(super) fn check<'tcx>(
     filter_arg: &'tcx hir::Expr<'_>,
 ) {
     // lint if caller of `.filter().next()` is an Iterator
-    let recv_impls_iterator = cx.tcx.get_diagnostic_item(sym::Iterator).map_or(false, |id| {
-        implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[])
-    });
+    let recv_impls_iterator = cx
+        .tcx
+        .get_diagnostic_item(sym::Iterator)
+        .is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(recv), id, &[]));
     if recv_impls_iterator {
         let msg = "called `filter(..).next()` on an `Iterator`. This is more succinctly expressed by calling \
                    `.find(..)` instead";
diff --git a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs
index cc82f6cfd63..a0c21faaa4c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/is_empty.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/is_empty.rs
@@ -1,6 +1,7 @@
 use clippy_utils::consts::ConstEvalCtxt;
 use clippy_utils::diagnostics::span_lint;
-use clippy_utils::{find_binding_init, path_to_local};
+use clippy_utils::macros::{is_assert_macro, root_macro_call};
+use clippy_utils::{find_binding_init, get_parent_expr, is_inside_always_const_context, path_to_local};
 use rustc_hir::{Expr, HirId};
 use rustc_lint::{LateContext, LintContext};
 use rustc_middle::lint::in_external_macro;
@@ -14,6 +15,16 @@ pub(super) fn check(cx: &LateContext<'_>, expr: &'_ Expr<'_>, receiver: &Expr<'_
     if in_external_macro(cx.sess(), expr.span) || !receiver.span.eq_ctxt(expr.span) {
         return;
     }
+    if let Some(parent) = get_parent_expr(cx, expr) {
+        if let Some(parent) = get_parent_expr(cx, parent) {
+            if is_inside_always_const_context(cx.tcx, expr.hir_id)
+                && let Some(macro_call) = root_macro_call(parent.span)
+                && is_assert_macro(cx, macro_call.def_id)
+            {
+                return;
+            }
+        }
+    }
     let init_expr = expr_or_init(cx, receiver);
     if !receiver.span.eq_ctxt(init_expr.span) {
         return;
diff --git a/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs b/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs
index 9d462bd1845..9a62b719a8f 100644
--- a/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/iter_out_of_bounds.rs
@@ -29,10 +29,7 @@ fn get_iterator_length<'tcx>(cx: &LateContext<'tcx>, iter: &'tcx Expr<'tcx>) ->
     if cx.tcx.is_diagnostic_item(sym::ArrayIntoIter, did) {
         // For array::IntoIter<T, const N: usize>, the length is the second generic
         // parameter.
-        substs
-            .const_at(1)
-            .try_to_target_usize(cx.tcx)
-            .map(u128::from)
+        substs.const_at(1).try_to_target_usize(cx.tcx).map(u128::from)
     } else if cx.tcx.is_diagnostic_item(sym::SliceIter, did)
         && let ExprKind::MethodCall(_, recv, ..) = iter.kind
     {
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 223b0630bfd..7aa13d8d5b6 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_inspect.rs
@@ -148,7 +148,7 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
                                 _ => {},
                             }
                         }
-                        requires_copy |= !ty.is_copy_modulo_regions(cx.tcx, cx.param_env);
+                        requires_copy |= !ty.is_copy_modulo_regions(cx.tcx, cx.typing_env());
                         break;
                     }
                 },
@@ -158,9 +158,9 @@ pub(crate) fn check(cx: &LateContext<'_>, expr: &Expr<'_>, arg: &Expr<'_>, name:
         }
 
         if can_lint
-            && (!requires_copy || arg_ty.is_copy_modulo_regions(cx.tcx, cx.param_env))
+            && (!requires_copy || arg_ty.is_copy_modulo_regions(cx.tcx, cx.typing_env()))
             // This case could be handled, but a fair bit of care would need to be taken.
-            && (!requires_deref || arg_ty.is_freeze(cx.tcx, cx.param_env))
+            && (!requires_deref || arg_ty.is_freeze(cx.tcx, cx.typing_env()))
         {
             if requires_deref {
                 edits.push((param.span.shrink_to_lo(), "&".into()));
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_next_back.rs b/src/tools/clippy/clippy_lints/src/methods/manual_next_back.rs
index 5f3fec53827..9a03559b223 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_next_back.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_next_back.rs
@@ -19,9 +19,7 @@ pub(super) fn check<'tcx>(
     if cx
         .tcx
         .get_diagnostic_item(sym::DoubleEndedIterator)
-        .map_or(false, |double_ended_iterator| {
-            implements_trait(cx, rev_recv_ty, double_ended_iterator, &[])
-        })
+        .is_some_and(|double_ended_iterator| implements_trait(cx, rev_recv_ty, double_ended_iterator, &[]))
         && is_trait_method(cx, rev_call, sym::Iterator)
         && is_trait_method(cx, expr, sym::Iterator)
     {
diff --git a/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs b/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs
index 61e74369cb0..098721dc046 100644
--- a/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/manual_str_repeat.rs
@@ -36,8 +36,8 @@ fn parse_repeat_arg(cx: &LateContext<'_>, e: &Expr<'_>) -> Option<RepeatKind> {
     } else {
         let ty = cx.typeck_results().expr_ty(e);
         if is_type_lang_item(cx, ty, LangItem::String)
-            || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).map_or(false, Ty::is_str))
-            || (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).map_or(false, Ty::is_str))
+            || (is_type_lang_item(cx, ty, LangItem::OwnedBox) && get_ty_param(ty).is_some_and(Ty::is_str))
+            || (is_type_diagnostic_item(cx, ty, sym::Cow) && get_ty_param(ty).is_some_and(Ty::is_str))
         {
             Some(RepeatKind::String)
         } else {
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs b/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs
new file mode 100644
index 00000000000..ac11baa2d54
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/map_all_any_identity.rs
@@ -0,0 +1,43 @@
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::SpanRangeExt;
+use clippy_utils::{is_expr_identity_function, is_trait_method};
+use rustc_errors::Applicability;
+use rustc_hir::Expr;
+use rustc_lint::LateContext;
+use rustc_span::{Span, sym};
+
+use super::MAP_ALL_ANY_IDENTITY;
+
+#[allow(clippy::too_many_arguments)]
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    expr: &Expr<'_>,
+    recv: &Expr<'_>,
+    map_call_span: Span,
+    map_arg: &Expr<'_>,
+    any_call_span: Span,
+    any_arg: &Expr<'_>,
+    method: &str,
+) {
+    if is_trait_method(cx, expr, sym::Iterator)
+        && is_trait_method(cx, recv, sym::Iterator)
+        && is_expr_identity_function(cx, any_arg)
+        && let map_any_call_span = map_call_span.with_hi(any_call_span.hi())
+        && let Some(map_arg) = map_arg.span.get_source_text(cx)
+    {
+        span_lint_and_then(
+            cx,
+            MAP_ALL_ANY_IDENTITY,
+            map_any_call_span,
+            format!("usage of `.map(...).{method}(identity)`"),
+            |diag| {
+                diag.span_suggestion_verbose(
+                    map_any_call_span,
+                    format!("use `.{method}(...)` instead"),
+                    format!("{method}({map_arg})"),
+                    Applicability::MachineApplicable,
+                );
+            },
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
index 515d4a11ed5..d5594b21db5 100644
--- a/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/map_clone.rs
@@ -70,7 +70,7 @@ pub(super) fn check(cx: &LateContext<'_>, e: &hir::Expr<'_>, recv: &hir::Expr<'_
                                 if ident_eq(name, obj) && method.ident.name == sym::clone
                                 && let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id)
                                 && let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
-                                && cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id)
+                                && cx.tcx.lang_items().clone_trait() == Some(trait_id)
                                 // no autoderefs
                                 && !cx.typeck_results().expr_adjustments(obj).iter()
                                     .any(|a| matches!(a.kind, Adjust::Deref(Some(..))))
diff --git a/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs
new file mode 100644
index 00000000000..fc656fd78ba
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/map_with_unused_argument_over_ranges.rs
@@ -0,0 +1,134 @@
+use crate::methods::MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES;
+use clippy_config::msrvs::{self, Msrv};
+use clippy_utils::diagnostics::span_lint_and_then;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::{eager_or_lazy, higher, usage};
+use rustc_ast::LitKind;
+use rustc_ast::ast::RangeLimits;
+use rustc_data_structures::packed::Pu128;
+use rustc_errors::Applicability;
+use rustc_hir::{Body, Closure, Expr, ExprKind};
+use rustc_lint::LateContext;
+use rustc_span::Span;
+
+fn extract_count_with_applicability(
+    cx: &LateContext<'_>,
+    range: higher::Range<'_>,
+    applicability: &mut Applicability,
+) -> Option<String> {
+    let start = range.start?;
+    let end = range.end?;
+    // TODO: This doens't handle if either the start or end are negative literals, or if the start is
+    // not a literal. In the first case, we need to be careful about how we handle computing the
+    // count to avoid overflows. In the second, we may need to add parenthesis to make the
+    // suggestion correct.
+    if let ExprKind::Lit(lit) = start.kind
+        && let LitKind::Int(Pu128(lower_bound), _) = lit.node
+    {
+        if let ExprKind::Lit(lit) = end.kind
+            && let LitKind::Int(Pu128(upper_bound), _) = lit.node
+        {
+            // Here we can explicitly calculate the number of iterations
+            let count = if upper_bound >= lower_bound {
+                match range.limits {
+                    RangeLimits::HalfOpen => upper_bound - lower_bound,
+                    RangeLimits::Closed => (upper_bound - lower_bound).checked_add(1)?,
+                }
+            } else {
+                0
+            };
+            return Some(format!("{count}"));
+        }
+        let end_snippet = Sugg::hir_with_applicability(cx, end, "...", applicability)
+            .maybe_par()
+            .into_string();
+        if lower_bound == 0 {
+            if range.limits == RangeLimits::Closed {
+                return Some(format!("{end_snippet} + 1"));
+            }
+            return Some(end_snippet);
+        }
+        if range.limits == RangeLimits::Closed {
+            return Some(format!("{end_snippet} - {}", lower_bound - 1));
+        }
+        return Some(format!("{end_snippet} - {lower_bound}"));
+    }
+    None
+}
+
+pub(super) fn check(
+    cx: &LateContext<'_>,
+    ex: &Expr<'_>,
+    receiver: &Expr<'_>,
+    arg: &Expr<'_>,
+    msrv: &Msrv,
+    method_call_span: Span,
+) {
+    let mut applicability = Applicability::MaybeIncorrect;
+    if let Some(range) = higher::Range::hir(receiver)
+        && let ExprKind::Closure(Closure { body, .. }) = arg.kind
+        && let body_hir = cx.tcx.hir().body(*body)
+        && let Body {
+            params: [param],
+            value: body_expr,
+        } = body_hir
+        && !usage::BindingUsageFinder::are_params_used(cx, body_hir)
+        && let Some(count) = extract_count_with_applicability(cx, range, &mut applicability)
+    {
+        let method_to_use_name;
+        let new_span;
+        let use_take;
+
+        if eager_or_lazy::switch_to_eager_eval(cx, body_expr) {
+            if msrv.meets(msrvs::REPEAT_N) {
+                method_to_use_name = "repeat_n";
+                let body_snippet = snippet_with_applicability(cx, body_expr.span, "..", &mut applicability);
+                new_span = (arg.span, format!("{body_snippet}, {count}"));
+                use_take = false;
+            } else {
+                method_to_use_name = "repeat";
+                let body_snippet = snippet_with_applicability(cx, body_expr.span, "..", &mut applicability);
+                new_span = (arg.span, body_snippet.to_string());
+                use_take = true;
+            }
+        } else if msrv.meets(msrvs::REPEAT_WITH) {
+            method_to_use_name = "repeat_with";
+            new_span = (param.span, String::new());
+            use_take = true;
+        } else {
+            return;
+        }
+
+        // We need to provide nonempty parts to diag.multipart_suggestion so we
+        // collate all our parts here and then remove those that are empty.
+        let mut parts = vec![
+            (
+                receiver.span.to(method_call_span),
+                format!("std::iter::{method_to_use_name}"),
+            ),
+            new_span,
+        ];
+        if use_take {
+            parts.push((ex.span.shrink_to_hi(), format!(".take({count})")));
+        }
+
+        span_lint_and_then(
+            cx,
+            MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES,
+            ex.span,
+            "map of a closure that does not depend on its parameter over a range",
+            |diag| {
+                diag.multipart_suggestion(
+                    if use_take {
+                        format!("remove the explicit range and use `{method_to_use_name}` and `take`")
+                    } else {
+                        format!("remove the explicit range and use `{method_to_use_name}`")
+                    },
+                    parts,
+                    applicability,
+                );
+            },
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/mod.rs b/src/tools/clippy/clippy_lints/src/methods/mod.rs
index 2a391870d70..795e041ffd9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/mod.rs
@@ -60,13 +60,16 @@ mod manual_ok_or;
 mod manual_saturating_arithmetic;
 mod manual_str_repeat;
 mod manual_try_fold;
+mod map_all_any_identity;
 mod map_clone;
 mod map_collect_result_unit;
 mod map_err_ignore;
 mod map_flatten;
 mod map_identity;
 mod map_unwrap_or;
+mod map_with_unused_argument_over_ranges;
 mod mut_mutex_lock;
+mod needless_as_bytes;
 mod needless_character_iteration;
 mod needless_collect;
 mod needless_option_as_deref;
@@ -118,6 +121,7 @@ mod unnecessary_iter_cloned;
 mod unnecessary_join;
 mod unnecessary_lazy_eval;
 mod unnecessary_literal_unwrap;
+mod unnecessary_map_or;
 mod unnecessary_min_or_max;
 mod unnecessary_result_map_or_else;
 mod unnecessary_sort_by;
@@ -4098,6 +4102,33 @@ declare_clippy_lint! {
 
 declare_clippy_lint! {
     /// ### What it does
+    /// Converts some constructs mapping an Enum value for equality comparison.
+    ///
+    /// ### Why is this bad?
+    /// Calls such as `opt.map_or(false, |val| val == 5)` are needlessly long and cumbersome,
+    /// and can be reduced to, for example, `opt == Some(5)` assuming `opt` implements `PartialEq`.
+    /// This lint offers readability and conciseness improvements.
+    ///
+    /// ### Example
+    /// ```no_run
+    /// pub fn a(x: Option<i32>) -> bool {
+    ///     x.map_or(false, |n| n == 5)
+    /// }
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// pub fn a(x: Option<i32>) -> bool {
+    ///     x == Some(5)
+    /// }
+    /// ```
+    #[clippy::version = "1.75.0"]
+    pub UNNECESSARY_MAP_OR,
+    style,
+    "reduce unnecessary pattern matching for constructs that implement `PartialEq`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
     /// Checks if an iterator is used to check if a string is ascii.
     ///
     /// ### Why is this bad?
@@ -4166,6 +4197,90 @@ declare_clippy_lint! {
     "calling `.first().is_some()` or `.first().is_none()` instead of `.is_empty()`"
 }
 
+declare_clippy_lint! {
+   /// ### What it does
+   /// It detects useless calls to `str::as_bytes()` before calling `len()` or `is_empty()`.
+   ///
+   /// ### Why is this bad?
+   /// The `len()` and `is_empty()` methods are also directly available on strings, and they
+   /// return identical results. In particular, `len()` on a string returns the number of
+   /// bytes.
+   ///
+   /// ### Example
+   /// ```
+   /// let len = "some string".as_bytes().len();
+   /// let b = "some string".as_bytes().is_empty();
+   /// ```
+   /// Use instead:
+   /// ```
+   /// let len = "some string".len();
+   /// let b = "some string".is_empty();
+   /// ```
+   #[clippy::version = "1.84.0"]
+   pub NEEDLESS_AS_BYTES,
+   complexity,
+   "detect useless calls to `as_bytes()`"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    /// Checks for usage of `.map(…)`, followed by `.all(identity)` or `.any(identity)`.
+    ///
+    /// ### Why is this bad?
+    /// The `.all(…)` or `.any(…)` methods can be called directly in place of `.map(…)`.
+    ///
+    /// ### Example
+    /// ```
+    /// # let mut v = [""];
+    /// let e1 = v.iter().map(|s| s.is_empty()).all(|a| a);
+    /// let e2 = v.iter().map(|s| s.is_empty()).any(std::convert::identity);
+    /// ```
+    /// Use instead:
+    /// ```
+    /// # let mut v = [""];
+    /// let e1 = v.iter().all(|s| s.is_empty());
+    /// let e2 = v.iter().any(|s| s.is_empty());
+    /// ```
+    #[clippy::version = "1.84.0"]
+    pub MAP_ALL_ANY_IDENTITY,
+    complexity,
+    "combine `.map(_)` followed by `.all(identity)`/`.any(identity)` into a single call"
+}
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Checks for `Iterator::map` over ranges without using the parameter which
+    /// could be more clearly expressed using `std::iter::repeat(...).take(...)`
+    /// or `std::iter::repeat_n`.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// It expresses the intent more clearly to `take` the correct number of times
+    /// from a generating function than to apply a closure to each number in a
+    /// range only to discard them.
+    ///
+    /// ### Example
+    ///
+    /// ```no_run
+    /// let random_numbers : Vec<_> = (0..10).map(|_| { 3 + 1 }).collect();
+    /// ```
+    /// Use instead:
+    /// ```no_run
+    /// let f : Vec<_> = std::iter::repeat( 3 + 1 ).take(10).collect();
+    /// ```
+    ///
+    /// ### Known Issues
+    ///
+    /// This lint may suggest replacing a `Map<Range>` with a `Take<RepeatWith>`.
+    /// The former implements some traits that the latter does not, such as
+    /// `DoubleEndedIterator`.
+    #[clippy::version = "1.84.0"]
+    pub MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES,
+    restriction,
+    "map of a trivial closure (not dependent on parameter) over a range"
+}
+
 pub struct Methods {
     avoid_breaking_exported_api: bool,
     msrv: Msrv,
@@ -4327,6 +4442,10 @@ impl_lint_pass!(Methods => [
     NEEDLESS_CHARACTER_ITERATION,
     MANUAL_INSPECT,
     UNNECESSARY_MIN_OR_MAX,
+    NEEDLESS_AS_BYTES,
+    MAP_ALL_ANY_IDENTITY,
+    MAP_WITH_UNUSED_ARGUMENT_OVER_RANGES,
+    UNNECESSARY_MAP_OR,
 ]);
 
 /// Extracts a method call name, args, and `Span` of the method name.
@@ -4534,15 +4653,21 @@ impl Methods {
                 ("all", [arg]) => {
                     unused_enumerate_index::check(cx, expr, recv, arg);
                     needless_character_iteration::check(cx, expr, recv, arg, true);
-                    if let Some(("cloned", recv2, [], _, _)) = method_call(recv) {
-                        iter_overeager_cloned::check(
-                            cx,
-                            expr,
-                            recv,
-                            recv2,
-                            iter_overeager_cloned::Op::NeedlessMove(arg),
-                            false,
-                        );
+                    match method_call(recv) {
+                        Some(("cloned", recv2, [], _, _)) => {
+                            iter_overeager_cloned::check(
+                                cx,
+                                expr,
+                                recv,
+                                recv2,
+                                iter_overeager_cloned::Op::NeedlessMove(arg),
+                                false,
+                            );
+                        },
+                        Some(("map", _, [map_arg], _, map_call_span)) => {
+                            map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "all");
+                        },
+                        _ => {},
                     }
                 },
                 ("and_then", [arg]) => {
@@ -4571,6 +4696,9 @@ impl Methods {
                         {
                             string_lit_chars_any::check(cx, expr, recv, param, peel_blocks(body.value), &self.msrv);
                         },
+                        Some(("map", _, [map_arg], _, map_call_span)) => {
+                            map_all_any_identity::check(cx, expr, recv, map_call_span, map_arg, call_span, arg, "any");
+                        },
                         _ => {},
                     }
                 },
@@ -4764,8 +4892,14 @@ impl Methods {
                     unit_hash::check(cx, expr, recv, arg);
                 },
                 ("is_empty", []) => {
-                    if let Some(("as_str", recv, [], as_str_span, _)) = method_call(recv) {
-                        redundant_as_str::check(cx, expr, recv, as_str_span, span);
+                    match method_call(recv) {
+                        Some(("as_bytes", prev_recv, [], _, _)) => {
+                            needless_as_bytes::check(cx, "is_empty", recv, prev_recv, expr.span);
+                        },
+                        Some(("as_str", recv, [], as_str_span, _)) => {
+                            redundant_as_str::check(cx, expr, recv, as_str_span, span);
+                        },
+                        _ => {},
                     }
                     is_empty::check(cx, expr, recv);
                 },
@@ -4795,6 +4929,11 @@ impl Methods {
                         );
                     }
                 },
+                ("len", []) => {
+                    if let Some(("as_bytes", prev_recv, [], _, _)) = method_call(recv) {
+                        needless_as_bytes::check(cx, "len", recv, prev_recv, expr.span);
+                    }
+                },
                 ("lock", []) => {
                     mut_mutex_lock::check(cx, expr, recv, span);
                 },
@@ -4802,6 +4941,7 @@ impl Methods {
                     if name == "map" {
                         unused_enumerate_index::check(cx, expr, recv, m_arg);
                         map_clone::check(cx, expr, recv, m_arg, &self.msrv);
+                        map_with_unused_argument_over_ranges::check(cx, expr, recv, m_arg, &self.msrv, span);
                         match method_call(recv) {
                             Some((map_name @ ("iter" | "into_iter"), recv2, _, _, _)) => {
                                 iter_kv_map::check(cx, map_name, expr, recv2, m_arg, &self.msrv);
@@ -4839,6 +4979,7 @@ impl Methods {
                     option_map_or_none::check(cx, expr, recv, def, map);
                     manual_ok_or::check(cx, expr, recv, def, map);
                     option_map_or_err_ok::check(cx, expr, recv, def, map);
+                    unnecessary_map_or::check(cx, expr, recv, def, map, &self.msrv);
                 },
                 ("map_or_else", [def, map]) => {
                     result_map_or_else_none::check(cx, expr, recv, def, map);
@@ -5182,7 +5323,6 @@ impl ShouldImplTraitCase {
 }
 
 #[rustfmt::skip]
-#[expect(clippy::large_const_arrays, reason = "`Span` is not sync, so this can't be static")]
 const TRAIT_METHODS: [ShouldImplTraitCase; 30] = [
     ShouldImplTraitCase::new("std::ops::Add", "add",  2,  FN_HEADER,  SelfKind::Value,  OutType::Any, true),
     ShouldImplTraitCase::new("std::convert::AsMut", "as_mut",  1,  FN_HEADER,  SelfKind::RefMut,  OutType::Ref, true),
@@ -5233,7 +5373,7 @@ impl SelfKind {
                 boxed_ty == parent_ty
             } else if is_type_diagnostic_item(cx, ty, sym::Rc) || is_type_diagnostic_item(cx, ty, sym::Arc) {
                 if let ty::Adt(_, args) = ty.kind() {
-                    args.types().next().map_or(false, |t| t == parent_ty)
+                    args.types().next() == Some(parent_ty)
                 } else {
                     false
                 }
diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs b/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs
new file mode 100644
index 00000000000..75e9f317230
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_as_bytes.rs
@@ -0,0 +1,28 @@
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::sugg::Sugg;
+use clippy_utils::ty::is_type_lang_item;
+use rustc_errors::Applicability;
+use rustc_hir::{Expr, LangItem};
+use rustc_lint::LateContext;
+use rustc_span::Span;
+
+use super::NEEDLESS_AS_BYTES;
+
+pub fn check(cx: &LateContext<'_>, method: &str, recv: &Expr<'_>, prev_recv: &Expr<'_>, span: Span) {
+    if cx.typeck_results().expr_ty_adjusted(recv).peel_refs().is_slice()
+        && let ty1 = cx.typeck_results().expr_ty_adjusted(prev_recv).peel_refs()
+        && (is_type_lang_item(cx, ty1, LangItem::String) || ty1.is_str())
+    {
+        let mut app = Applicability::MachineApplicable;
+        let sugg = Sugg::hir_with_context(cx, prev_recv, span.ctxt(), "..", &mut app);
+        span_lint_and_sugg(
+            cx,
+            NEEDLESS_AS_BYTES,
+            span,
+            "needless call to `as_bytes()`",
+            format!("`{method}()` can be called directly on strings"),
+            format!("{sugg}.{method}()"),
+            app,
+        );
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
index 96a31812ca4..c00b9b368c4 100644
--- a/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/needless_collect.rs
@@ -193,7 +193,7 @@ fn check_collect_into_intoiterator<'tcx>(
 
 /// Checks if the given method call matches the expected signature of `([&[mut]] self) -> bool`
 fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool {
-    cx.typeck_results().type_dependent_def_id(call_id).map_or(false, |id| {
+    cx.typeck_results().type_dependent_def_id(call_id).is_some_and(|id| {
         let sig = cx.tcx.fn_sig(id).instantiate_identity().skip_binder();
         sig.inputs().len() == 1 && sig.output().is_bool()
     })
@@ -203,10 +203,10 @@ fn is_is_empty_sig(cx: &LateContext<'_>, call_id: HirId) -> bool {
 fn iterates_same_ty<'tcx>(cx: &LateContext<'tcx>, iter_ty: Ty<'tcx>, collect_ty: Ty<'tcx>) -> bool {
     if let Some(iter_trait) = cx.tcx.get_diagnostic_item(sym::Iterator)
         && let Some(into_iter_trait) = cx.tcx.get_diagnostic_item(sym::IntoIterator)
-        && let Some(iter_item_ty) = make_normalized_projection(cx.tcx, cx.param_env, iter_trait, sym::Item, [iter_ty])
+        && let Some(iter_item_ty) = make_normalized_projection(cx.tcx, cx.typing_env(), iter_trait, sym::Item, [iter_ty])
         && let Some(into_iter_item_proj) = make_projection(cx.tcx, into_iter_trait, sym::Item, [collect_ty])
         && let Ok(into_iter_item_ty) = cx.tcx.try_normalize_erasing_regions(
-            cx.param_env,
+            cx.typing_env(),
             Ty::new_projection_from_args(cx.tcx, into_iter_item_proj.def_id, into_iter_item_proj.args),
         )
     {
@@ -237,7 +237,7 @@ fn is_contains_sig(cx: &LateContext<'_>, call_id: HirId, iter_expr: &Expr<'_>) -
         )
         && let args = cx.tcx.mk_args(&[GenericArg::from(typeck.expr_ty_adjusted(iter_expr))])
         && let proj_ty = Ty::new_projection_from_args(cx.tcx, iter_item.def_id, args)
-        && let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, proj_ty)
+        && let Ok(item_ty) = cx.tcx.try_normalize_erasing_regions(cx.typing_env(), proj_ty)
     {
         item_ty == EarlyBinder::bind(search_ty).instantiate(cx.tcx, cx.typeck_results().node_args(call_id))
     } else {
@@ -322,7 +322,7 @@ impl<'tcx> Visitor<'tcx> for IterFunctionVisitor<'_, 'tcx> {
         // Check function calls on our collection
         if let ExprKind::MethodCall(method_name, recv, args, _) = &expr.kind {
             if args.is_empty()
-                && method_name.ident.name == sym!(collect)
+                && method_name.ident.name.as_str() == "collect"
                 && is_trait_method(self.cx, expr, sym::Iterator)
             {
                 self.current_mutably_captured_ids = get_captured_ids(self.cx, self.cx.typeck_results().expr_ty(recv));
diff --git a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
index 389e02056b2..998bdee0157 100644
--- a/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/option_as_ref_deref.rs
@@ -45,7 +45,7 @@ pub(super) fn check(
         hir::ExprKind::Path(ref expr_qpath) => {
             cx.qpath_res(expr_qpath, map_arg.hir_id)
                 .opt_def_id()
-                .map_or(false, |fun_def_id| {
+                .is_some_and(|fun_def_id| {
                     cx.tcx.is_diagnostic_item(sym::deref_method, fun_def_id)
                         || cx.tcx.is_diagnostic_item(sym::deref_mut_method, fun_def_id)
                         || deref_aliases
diff --git a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
index b685a466b72..6b39b753885 100644
--- a/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/or_fun_call.rs
@@ -59,9 +59,7 @@ pub(super) fn check<'tcx>(
                 let output_ty = cx.tcx.fn_sig(def_id).instantiate(cx.tcx, args).skip_binder().output();
                 cx.tcx
                     .get_diagnostic_item(sym::Default)
-                    .map_or(false, |default_trait_id| {
-                        implements_trait(cx, output_ty, default_trait_id, &[])
-                    })
+                    .is_some_and(|default_trait_id| implements_trait(cx, output_ty, default_trait_id, &[]))
             } else {
                 false
             }
diff --git a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs
index 65e545ed03a..db2b9d4d92f 100644
--- a/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/read_line_without_trim.rs
@@ -44,7 +44,7 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<
             if let Some(parent) = get_parent_expr(cx, expr) {
                 let data = if let ExprKind::MethodCall(segment, recv, args, span) = parent.kind {
                     if args.is_empty()
-                        && segment.ident.name == sym!(parse)
+                        && segment.ident.name.as_str() == "parse"
                         && let parse_result_ty = cx.typeck_results().expr_ty(parent)
                         && is_type_diagnostic_item(cx, parse_result_ty, sym::Result)
                         && let ty::Adt(_, substs) = parse_result_ty.kind()
@@ -58,7 +58,7 @@ pub fn check(cx: &LateContext<'_>, call: &Expr<'_>, recv: &Expr<'_>, arg: &Expr<
                             "calling `.parse()` on a string without trimming the trailing newline character",
                             "checking",
                         ))
-                    } else if segment.ident.name == sym!(ends_with)
+                    } else if segment.ident.name.as_str() == "ends_with"
                         && recv.span == expr.span
                         && let [arg] = args
                         && expr_is_string_literal_without_trailing_newline(arg)
diff --git a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
index a2a7de905ca..1cee28e1986 100644
--- a/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/str_splitn.rs
@@ -348,7 +348,7 @@ fn parse_iter_usage<'tcx>(
                     && cx
                         .typeck_results()
                         .type_dependent_def_id(e.hir_id)
-                        .map_or(false, |id| is_diag_item_method(cx, id, sym::Option)) =>
+                        .is_some_and(|id| is_diag_item_method(cx, id, sym::Option)) =>
             {
                 (Some(UnwrapKind::Unwrap), e.span)
             },
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
index ca46da81fa0..bab439015c5 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_filter_map.rs
@@ -1,13 +1,15 @@
 use super::utils::clone_or_copy_needed;
-use clippy_utils::diagnostics::span_lint;
+use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::ty::is_copy;
 use clippy_utils::usage::mutated_variables;
 use clippy_utils::visitors::{Descend, for_each_expr_without_closures};
-use clippy_utils::{is_res_lang_ctor, is_trait_method, path_res, path_to_local_id};
+use clippy_utils::{MaybePath, is_res_lang_ctor, is_trait_method, path_res, path_to_local_id};
 use core::ops::ControlFlow;
+use rustc_errors::Applicability;
 use rustc_hir as hir;
 use rustc_hir::LangItem::{OptionNone, OptionSome};
 use rustc_lint::LateContext;
+use rustc_middle::query::Key;
 use rustc_middle::ty;
 use rustc_span::sym;
 
@@ -36,9 +38,25 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a
                 ControlFlow::Continue(Descend::Yes)
             }
         });
-
         let in_ty = cx.typeck_results().node_type(body.params[0].hir_id);
         let sugg = if !found_filtering {
+            // Check if the closure is .filter_map(|x| Some(x))
+            if name == "filter_map"
+                && let hir::ExprKind::Call(expr, args) = body.value.kind
+                && is_res_lang_ctor(cx, path_res(cx, expr), OptionSome)
+                && arg_id.ty_def_id() == args[0].hir_id().ty_def_id()
+                && let hir::ExprKind::Path(_) = args[0].kind
+            {
+                span_lint_and_sugg(
+                    cx,
+                    UNNECESSARY_FILTER_MAP,
+                    expr.span,
+                    format!("{name} is unnecessary"),
+                    "try removing the filter_map",
+                    String::new(),
+                    Applicability::MaybeIncorrect,
+                );
+            }
             if name == "filter_map" { "map" } else { "map(..).next()" }
         } else if !found_mapping && !mutates_arg && (!clone_or_copy_needed || is_copy(cx, in_ty)) {
             match cx.typeck_results().expr_ty(body.value).kind() {
@@ -52,7 +70,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a
         } else {
             return;
         };
-        span_lint(
+        span_lint_and_sugg(
             cx,
             if name == "filter_map" {
                 UNNECESSARY_FILTER_MAP
@@ -60,7 +78,10 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'tcx>, a
                 UNNECESSARY_FIND_MAP
             },
             expr.span,
-            format!("this `.{name}` can be written more simply using `.{sugg}`"),
+            format!("this `.{name}` can be written more simply"),
+            "try instead",
+            sugg.to_string(),
+            Applicability::MaybeIncorrect,
         );
     }
 }
@@ -78,7 +99,7 @@ fn check_expression<'tcx>(cx: &LateContext<'tcx>, arg_id: hir::HirId, expr: &'tc
             (true, true)
         },
         hir::ExprKind::MethodCall(segment, recv, [arg], _) => {
-            if segment.ident.name == sym!(then_some)
+            if segment.ident.name.as_str() == "then_some"
                 && cx.typeck_results().expr_ty(recv).is_bool()
                 && path_to_local_id(arg, arg_id)
             {
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs
new file mode 100644
index 00000000000..adc27cd437f
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_map_or.rs
@@ -0,0 +1,131 @@
+use std::borrow::Cow;
+
+use clippy_config::msrvs::{self, Msrv};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::eager_or_lazy::switch_to_eager_eval;
+use clippy_utils::source::snippet_opt;
+use clippy_utils::sugg::{Sugg, make_binop};
+use clippy_utils::ty::{get_type_diagnostic_name, implements_trait};
+use clippy_utils::visitors::is_local_used;
+use clippy_utils::{is_from_proc_macro, path_to_local_id};
+use rustc_ast::LitKind::Bool;
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind, PatKind};
+use rustc_lint::LateContext;
+use rustc_span::sym;
+
+use super::UNNECESSARY_MAP_OR;
+
+pub(super) enum Variant {
+    Ok,
+    Some,
+}
+impl Variant {
+    pub fn variant_name(&self) -> &'static str {
+        match self {
+            Variant::Ok => "Ok",
+            Variant::Some => "Some",
+        }
+    }
+
+    pub fn method_name(&self) -> &'static str {
+        match self {
+            Variant::Ok => "is_ok_and",
+            Variant::Some => "is_some_and",
+        }
+    }
+}
+
+pub(super) fn check<'a>(
+    cx: &LateContext<'a>,
+    expr: &Expr<'a>,
+    recv: &Expr<'_>,
+    def: &Expr<'_>,
+    map: &Expr<'_>,
+    msrv: &Msrv,
+) {
+    let ExprKind::Lit(def_kind) = def.kind else {
+        return;
+    };
+
+    let recv_ty = cx.typeck_results().expr_ty(recv);
+
+    let Bool(def_bool) = def_kind.node else {
+        return;
+    };
+
+    let variant = match get_type_diagnostic_name(cx, recv_ty) {
+        Some(sym::Option) => Variant::Some,
+        Some(sym::Result) => Variant::Ok,
+        Some(_) | None => return,
+    };
+
+    let (sugg, method) = if let ExprKind::Closure(map_closure) = map.kind
+            && let closure_body = cx.tcx.hir().body(map_closure.body)
+            && let closure_body_value = closure_body.value.peel_blocks()
+            && let ExprKind::Binary(op, l, r) = closure_body_value.kind
+            && let Some(param) = closure_body.params.first()
+            && let PatKind::Binding(_, hir_id, _, _) = param.pat.kind
+            // checking that map_or is one of the following:
+            // .map_or(false, |x| x == y)
+            // .map_or(false, |x| y == x) - swapped comparison
+            // .map_or(true, |x| x != y)
+            // .map_or(true, |x| y != x) - swapped comparison
+            && ((BinOpKind::Eq == op.node && !def_bool) || (BinOpKind::Ne == op.node && def_bool))
+            && let non_binding_location = if path_to_local_id(l, hir_id) { r } else { l }
+            && switch_to_eager_eval(cx, non_binding_location)
+            // xor, because if its both then thats a strange edge case and
+            // we can just ignore it, since by default clippy will error on this
+            && (path_to_local_id(l, hir_id) ^ path_to_local_id(r, hir_id))
+            && !is_local_used(cx, non_binding_location, hir_id)
+            && let typeck_results = cx.typeck_results()
+            && typeck_results.expr_ty(l) == typeck_results.expr_ty(r)
+            && let Some(partial_eq) = cx.tcx.get_diagnostic_item(sym::PartialEq)
+            && implements_trait(cx, recv_ty, partial_eq, &[recv_ty.into()])
+    {
+        let wrap = variant.variant_name();
+
+        // we may need to add parens around the suggestion
+        // in case the parent expression has additional method calls,
+        // since for example `Some(5).map_or(false, |x| x == 5).then(|| 1)`
+        // being converted to `Some(5) == Some(5).then(|| 1)` isnt
+        // the same thing
+
+        let inner_non_binding = Sugg::NonParen(Cow::Owned(format!(
+            "{wrap}({})",
+            Sugg::hir(cx, non_binding_location, "")
+        )));
+
+        let binop = make_binop(op.node, &Sugg::hir(cx, recv, ".."), &inner_non_binding)
+            .maybe_par()
+            .into_string();
+
+        (binop, "a standard comparison")
+    } else if !def_bool
+        && msrv.meets(msrvs::OPTION_RESULT_IS_VARIANT_AND)
+        && let Some(recv_callsite) = snippet_opt(cx, recv.span.source_callsite())
+        && let Some(span_callsite) = snippet_opt(cx, map.span.source_callsite())
+    {
+        let suggested_name = variant.method_name();
+        (
+            format!("{recv_callsite}.{suggested_name}({span_callsite})",),
+            suggested_name,
+        )
+    } else {
+        return;
+    };
+
+    if is_from_proc_macro(cx, expr) {
+        return;
+    }
+
+    span_lint_and_sugg(
+        cx,
+        UNNECESSARY_MAP_OR,
+        expr.span,
+        "this `map_or` is redundant",
+        format!("use {method} instead"),
+        sugg,
+        Applicability::MaybeIncorrect,
+    );
+}
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs
index 062d1348555..7d01bdc2269 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_min_or_max.rs
@@ -19,7 +19,7 @@ pub(super) fn check<'tcx>(
     arg: &'tcx Expr<'_>,
 ) {
     let typeck_results = cx.typeck_results();
-    let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck_results);
+    let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), typeck_results);
     if let Some(id) = typeck_results.type_dependent_def_id(expr.hir_id)
         && (cx.tcx.is_diagnostic_item(sym::cmp_ord_min, id) || cx.tcx.is_diagnostic_item(sym::cmp_ord_max, id))
     {
diff --git a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs
index 6911da69b94..603916e06c9 100644
--- a/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/unnecessary_sort_by.rs
@@ -166,9 +166,10 @@ fn detect_lint(cx: &LateContext<'_>, expr: &Expr<'_>, recv: &Expr<'_>, arg: &Exp
             },
         )) = &left_expr.kind
             && left_name == left_ident
-            && cx.tcx.get_diagnostic_item(sym::Ord).map_or(false, |id| {
-                implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[])
-            })
+            && cx
+                .tcx
+                .get_diagnostic_item(sym::Ord)
+                .is_some_and(|id| implements_trait(cx, cx.typeck_results().expr_ty(left_expr), id, &[]))
         {
             return Some(LintTrigger::Sort(SortDetection { vec_name }));
         }
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 82549413fa9..84ea3554a35 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
@@ -578,7 +578,7 @@ fn can_change_type<'a>(cx: &LateContext<'a>, mut expr: &'a Expr<'a>, mut ty: Ty<
                         if output_ty.contains(param_ty) {
                             if let Ok(new_ty) = cx.tcx.try_instantiate_and_normalize_erasing_regions(
                                 new_subst,
-                                cx.param_env,
+                                cx.typing_env(),
                                 bound_fn_sig.rebind(output_ty),
                             ) {
                                 expr = parent_expr;
diff --git a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs
index c309e778116..82313257e5c 100644
--- a/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/useless_asref.rs
@@ -124,7 +124,7 @@ fn is_calling_clone(cx: &LateContext<'_>, arg: &hir::Expr<'_>) -> bool {
                         && let Some(fn_id) = cx.typeck_results().type_dependent_def_id(closure_expr.hir_id)
                         && let Some(trait_id) = cx.tcx.trait_of_item(fn_id)
                         // We check it's the `Clone` trait.
-                        && cx.tcx.lang_items().clone_trait().map_or(false, |id| id == trait_id)
+                        && cx.tcx.lang_items().clone_trait().is_some_and(|id| id == trait_id)
                         // no autoderefs
                         && !cx.typeck_results().expr_adjustments(obj).iter()
                             .any(|a| matches!(a.kind, Adjust::Deref(Some(..))))
diff --git a/src/tools/clippy/clippy_lints/src/methods/zst_offset.rs b/src/tools/clippy/clippy_lints/src/methods/zst_offset.rs
index d33021c2a7b..102fa7bc895 100644
--- a/src/tools/clippy/clippy_lints/src/methods/zst_offset.rs
+++ b/src/tools/clippy/clippy_lints/src/methods/zst_offset.rs
@@ -7,7 +7,7 @@ use super::ZST_OFFSET;
 
 pub(super) fn check(cx: &LateContext<'_>, expr: &hir::Expr<'_>, recv: &hir::Expr<'_>) {
     if let ty::RawPtr(ty, _) = cx.typeck_results().expr_ty(recv).kind()
-        && let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(*ty))
+        && let Ok(layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(*ty))
         && layout.is_zst()
     {
         span_lint(cx, ZST_OFFSET, expr.span, "offset calculation on zero-sized value");
diff --git a/src/tools/clippy/clippy_lints/src/minmax.rs b/src/tools/clippy/clippy_lints/src/minmax.rs
index e95864c6db8..ed89b3b3438 100644
--- a/src/tools/clippy/clippy_lints/src/minmax.rs
+++ b/src/tools/clippy/clippy_lints/src/minmax.rs
@@ -79,9 +79,9 @@ fn min_max<'a, 'tcx>(cx: &LateContext<'tcx>, expr: &'a Expr<'a>) -> Option<(MinM
         },
         ExprKind::MethodCall(path, receiver, args @ [_], _) => {
             if cx.typeck_results().expr_ty(receiver).is_floating_point() || is_trait_method(cx, expr, sym::Ord) {
-                if path.ident.name == sym!(max) {
+                if path.ident.name.as_str() == "max" {
                     fetch_const(cx, Some(receiver), args, MinMax::Max)
-                } else if path.ident.name == sym!(min) {
+                } else if path.ident.name.as_str() == "min" {
                     fetch_const(cx, Some(receiver), args, MinMax::Min)
                 } else {
                     None
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 4cba13a05c2..37d7427f9a5 100644
--- a/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/misc_early/mod.rs
@@ -427,7 +427,7 @@ impl MiscEarlyLints {
         // See <https://github.com/rust-lang/rust-clippy/issues/4507> for a regression.
         // FIXME: Find a better way to detect those cases.
         let lit_snip = match snippet_opt(cx, span) {
-            Some(snip) if snip.chars().next().map_or(false, |c| c.is_ascii_digit()) => snip,
+            Some(snip) if snip.chars().next().is_some_and(|c| c.is_ascii_digit()) => snip,
             _ => return,
         };
 
diff --git a/src/tools/clippy/clippy_lints/src/missing_doc.rs b/src/tools/clippy/clippy_lints/src/missing_doc.rs
index ce508d85d63..1300c7d1062 100644
--- a/src/tools/clippy/clippy_lints/src/missing_doc.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_doc.rs
@@ -12,11 +12,13 @@ use clippy_utils::is_from_proc_macro;
 use clippy_utils::source::SpanRangeExt;
 use rustc_ast::ast::{self, MetaItem, MetaItemKind};
 use rustc_hir as hir;
+use rustc_hir::def::DefKind;
 use rustc_hir::def_id::LocalDefId;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
 use rustc_middle::ty::Visibility;
 use rustc_session::impl_lint_pass;
 use rustc_span::def_id::CRATE_DEF_ID;
+use rustc_span::symbol::kw;
 use rustc_span::{Span, sym};
 
 declare_clippy_lint! {
@@ -110,6 +112,21 @@ impl MissingDoc {
             return;
         }
 
+        if let Some(parent_def_id) = cx.tcx.opt_parent(def_id.to_def_id())
+            && let DefKind::AnonConst
+            | DefKind::AssocConst
+            | DefKind::AssocFn
+            | DefKind::Closure
+            | DefKind::Const
+            | DefKind::Fn
+            | DefKind::InlineConst
+            | DefKind::Static { .. }
+            | DefKind::SyntheticCoroutineBody = cx.tcx.def_kind(parent_def_id)
+        {
+            // Nested item has no generated documentation, so it doesn't need to be documented.
+            return;
+        }
+
         let has_doc = attrs
             .iter()
             .any(|a| a.doc_str().is_some() || Self::has_include(a.meta()))
@@ -184,8 +201,12 @@ impl<'tcx> LateLintPass<'tcx> for MissingDoc {
                     }
                 }
             },
-            hir::ItemKind::Const(..)
-            | hir::ItemKind::Enum(..)
+            hir::ItemKind::Const(..) => {
+                if it.ident.name == kw::Underscore {
+                    note_prev_span_then_ret!(self.prev_span, it.span);
+                }
+            },
+            hir::ItemKind::Enum(..)
             | hir::ItemKind::Macro(..)
             | hir::ItemKind::Mod(..)
             | hir::ItemKind::Static(..)
diff --git a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
index fc01b6753f1..f28b431ab99 100644
--- a/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_fields_in_debug.rs
@@ -116,7 +116,7 @@ fn should_lint<'tcx>(
 
             if path.ident.name == sym::debug_struct && is_type_diagnostic_item(cx, recv_ty, sym::Formatter) {
                 has_debug_struct = true;
-            } else if path.ident.name == sym!(finish_non_exhaustive)
+            } else if path.ident.name.as_str() == "finish_non_exhaustive"
                 && is_type_diagnostic_item(cx, recv_ty, sym::DebugStruct)
             {
                 has_finish_non_exhaustive = true;
diff --git a/src/tools/clippy/clippy_lints/src/missing_inline.rs b/src/tools/clippy/clippy_lints/src/missing_inline.rs
index f95a0f63fab..e587d695c84 100644
--- a/src/tools/clippy/clippy_lints/src/missing_inline.rs
+++ b/src/tools/clippy/clippy_lints/src/missing_inline.rs
@@ -2,6 +2,7 @@ use clippy_utils::diagnostics::span_lint;
 use rustc_ast::ast;
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::ty::AssocItemContainer;
 use rustc_session::declare_lint_pass;
 use rustc_span::{Span, sym};
 
@@ -138,7 +139,6 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
     }
 
     fn check_impl_item(&mut self, cx: &LateContext<'tcx>, impl_item: &'tcx hir::ImplItem<'_>) {
-        use rustc_middle::ty::{ImplContainer, TraitContainer};
         if rustc_middle::lint::in_external_macro(cx.sess(), impl_item.span) || is_executable_or_proc_macro(cx) {
             return;
         }
@@ -156,8 +156,8 @@ impl<'tcx> LateLintPass<'tcx> for MissingInline {
         let assoc_item = cx.tcx.associated_item(impl_item.owner_id);
         let container_id = assoc_item.container_id(cx.tcx);
         let trait_def_id = match assoc_item.container {
-            TraitContainer => Some(container_id),
-            ImplContainer => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id),
+            AssocItemContainer::Trait => Some(container_id),
+            AssocItemContainer::Impl => cx.tcx.impl_trait_ref(container_id).map(|t| t.skip_binder().def_id),
         };
 
         if let Some(trait_def_id) = trait_def_id {
diff --git a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs
index d276e29bace..882ab2dda7a 100644
--- a/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs
+++ b/src/tools/clippy/clippy_lints/src/multiple_bound_locations.rs
@@ -1,5 +1,5 @@
 use rustc_ast::visit::FnKind;
-use rustc_ast::{NodeId, WherePredicate};
+use rustc_ast::{NodeId, WherePredicateKind};
 use rustc_data_structures::fx::FxHashMap;
 use rustc_lint::{EarlyContext, EarlyLintPass};
 use rustc_session::declare_lint_pass;
@@ -51,8 +51,8 @@ impl EarlyLintPass for MultipleBoundLocations {
                 }
             }
             for clause in &generics.where_clause.predicates {
-                match clause {
-                    WherePredicate::BoundPredicate(pred) => {
+                match &clause.kind {
+                    WherePredicateKind::BoundPredicate(pred) => {
                         if (!pred.bound_generic_params.is_empty() || !pred.bounds.is_empty())
                             && let Some(Some(bound_span)) = pred
                                 .bounded_ty
@@ -62,14 +62,14 @@ impl EarlyLintPass for MultipleBoundLocations {
                             emit_lint(cx, *bound_span, pred.bounded_ty.span);
                         }
                     },
-                    WherePredicate::RegionPredicate(pred) => {
+                    WherePredicateKind::RegionPredicate(pred) => {
                         if !pred.bounds.is_empty()
                             && let Some(bound_span) = generic_params_with_bounds.get(&pred.lifetime.ident.name.as_str())
                         {
                             emit_lint(cx, *bound_span, pred.lifetime.ident.span);
                         }
                     },
-                    WherePredicate::EqPredicate(_) => {},
+                    WherePredicateKind::EqPredicate(_) => {},
                 }
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/mut_mut.rs b/src/tools/clippy/clippy_lints/src/mut_mut.rs
index 6cddd7ea813..e2ab5e98504 100644
--- a/src/tools/clippy/clippy_lints/src/mut_mut.rs
+++ b/src/tools/clippy/clippy_lints/src/mut_mut.rs
@@ -80,7 +80,7 @@ impl<'tcx> intravisit::Visitor<'tcx> for MutVisitor<'_, 'tcx> {
                     "generally you want to avoid `&mut &mut _` if possible",
                 );
             } else if let ty::Ref(_, ty, hir::Mutability::Mut) = self.cx.typeck_results().expr_ty(e).kind() {
-                if ty.peel_refs().is_sized(self.cx.tcx, self.cx.param_env) {
+                if ty.peel_refs().is_sized(self.cx.tcx, self.cx.typing_env()) {
                     span_lint_hir(
                         self.cx,
                         MUT_MUT,
diff --git a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
index f7fa31d83aa..9ebef531bc5 100644
--- a/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_borrows_for_generic_args.rs
@@ -85,8 +85,8 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
             && use_cx.same_ctxt
             && !use_cx.is_ty_unified
             && let use_node = use_cx.use_node(cx)
-            && let Some(DefinedTy::Mir(ty)) = use_node.defined_ty(cx)
-            && let ty::Param(ty) = *ty.value.skip_binder().kind()
+            && let Some(DefinedTy::Mir { def_site_def_id: _, ty }) = use_node.defined_ty(cx)
+            && let ty::Param(param_ty) = *ty.skip_binder().kind()
             && let Some((hir_id, fn_id, i)) = match use_node {
                 ExprUseNode::MethodArg(_, _, 0) => None,
                 ExprUseNode::MethodArg(hir_id, None, i) => cx
@@ -112,7 +112,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
                 fn_id,
                 cx.typeck_results().node_args(hir_id),
                 i,
-                ty,
+                param_ty,
                 expr,
                 &self.msrv,
             )
@@ -134,9 +134,11 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessBorrowsForGenericArgs<'tcx> {
     }
 
     fn check_body_post(&mut self, cx: &LateContext<'tcx>, body: &Body<'_>) {
-        if self.possible_borrowers.last().map_or(false, |&(local_def_id, _)| {
-            local_def_id == cx.tcx.hir().body_owner_def_id(body.id())
-        }) {
+        if self
+            .possible_borrowers
+            .last()
+            .is_some_and(|&(local_def_id, _)| local_def_id == cx.tcx.hir().body_owner_def_id(body.id()))
+        {
             self.possible_borrowers.pop();
         }
     }
@@ -232,7 +234,7 @@ fn needless_borrow_count<'tcx>(
     let mut check_reference_and_referent = |reference: &Expr<'tcx>, referent: &Expr<'tcx>| {
         if let ExprKind::Field(base, _) = &referent.kind {
             let base_ty = cx.typeck_results().expr_ty(base);
-            if drop_trait_def_id.map_or(false, |id| implements_trait(cx, base_ty, id, &[])) {
+            if drop_trait_def_id.is_some_and(|id| implements_trait(cx, base_ty, id, &[])) {
                 return false;
             }
         }
@@ -419,7 +421,7 @@ fn replace_types<'tcx>(
                         .expect_ty(cx.tcx)
                         .to_ty(cx.tcx);
 
-                    if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, projection)
+                    if let Ok(projected_ty) = cx.tcx.try_normalize_erasing_regions(cx.typing_env(), projection)
                         && args[term_param_ty.index as usize] != GenericArg::from(projected_ty)
                     {
                         deque.push_back((*term_param_ty, projected_ty));
diff --git a/src/tools/clippy/clippy_lints/src/needless_continue.rs b/src/tools/clippy/clippy_lints/src/needless_continue.rs
index ce97370d4d9..c48232f9905 100644
--- a/src/tools/clippy/clippy_lints/src/needless_continue.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_continue.rs
@@ -153,7 +153,7 @@ fn needless_continue_in_else(else_expr: &ast::Expr, label: Option<&ast::Label>)
 }
 
 fn is_first_block_stmt_continue(block: &ast::Block, label: Option<&ast::Label>) -> bool {
-    block.stmts.first().map_or(false, |stmt| match stmt.kind {
+    block.stmts.first().is_some_and(|stmt| match stmt.kind {
         ast::StmtKind::Semi(ref e) | ast::StmtKind::Expr(ref e) => {
             if let ast::ExprKind::Continue(ref l) = e.kind {
                 compare_labels(label, l.as_ref())
@@ -330,10 +330,11 @@ fn suggestion_snippet_for_continue_inside_else(cx: &EarlyContext<'_>, data: &Lin
 }
 
 fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) {
-    if let ast::ExprKind::Loop(loop_block, ..) = &expr.kind
+    if let ast::ExprKind::Loop(loop_block, loop_label, ..) = &expr.kind
         && let Some(last_stmt) = loop_block.stmts.last()
         && let ast::StmtKind::Expr(inner_expr) | ast::StmtKind::Semi(inner_expr) = &last_stmt.kind
-        && let ast::ExprKind::Continue(_) = inner_expr.kind
+        && let ast::ExprKind::Continue(continue_label) = inner_expr.kind
+        && compare_labels(loop_label.as_ref(), continue_label.as_ref())
     {
         span_lint_and_help(
             cx,
@@ -389,7 +390,7 @@ fn check_and_warn(cx: &EarlyContext<'_>, expr: &ast::Expr) {
 #[must_use]
 fn erode_from_back(s: &str) -> String {
     let mut ret = s.to_string();
-    while ret.pop().map_or(false, |c| c != '}') {}
+    while ret.pop().is_some_and(|c| c != '}') {}
     while let Some(c) = ret.pop() {
         if !c.is_whitespace() {
             ret.push(c);
diff --git a/src/tools/clippy/clippy_lints/src/needless_late_init.rs b/src/tools/clippy/clippy_lints/src/needless_late_init.rs
index 46cdb82130f..a67addea948 100644
--- a/src/tools/clippy/clippy_lints/src/needless_late_init.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_late_init.rs
@@ -347,7 +347,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit {
         if let LetStmt {
             init: None,
             pat:
-                &Pat {
+                Pat {
                     kind: PatKind::Binding(BindingMode::NONE, binding_id, _, None),
                     ..
                 },
@@ -357,7 +357,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessLateInit {
             && let Some((_, Node::Stmt(local_stmt))) = parents.next()
             && let Some((_, Node::Block(block))) = parents.next()
         {
-            check(cx, local, local_stmt, block, binding_id);
+            check(cx, local, local_stmt, block, *binding_id);
         }
     }
 }
diff --git a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
index 9a1c397b5b2..ad6313e391b 100644
--- a/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_maybe_sized.rs
@@ -1,7 +1,7 @@
 use clippy_utils::diagnostics::span_lint_and_then;
 use rustc_errors::Applicability;
 use rustc_hir::def_id::{DefId, DefIdMap};
-use rustc_hir::{GenericBound, Generics, PolyTraitRef, TraitBoundModifiers, BoundPolarity, WherePredicate};
+use rustc_hir::{BoundPolarity, GenericBound, Generics, PolyTraitRef, TraitBoundModifiers, WherePredicateKind};
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_middle::ty::{ClauseKind, PredicatePolarity};
 use rustc_session::declare_lint_pass;
@@ -52,7 +52,7 @@ fn type_param_bounds<'tcx>(generics: &'tcx Generics<'tcx>) -> impl Iterator<Item
         .iter()
         .enumerate()
         .filter_map(|(predicate_pos, predicate)| {
-            let WherePredicate::BoundPredicate(bound_predicate) = predicate else {
+            let WherePredicateKind::BoundPredicate(bound_predicate) = &predicate.kind else {
                 return None;
             };
 
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 5c631a176c4..b7dc269061c 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
@@ -183,7 +183,7 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByRefMut<'tcx> {
             .iter()
             .zip(fn_sig.inputs())
             .zip(body.params)
-            .filter(|((&input, &ty), arg)| !should_skip(cx, input, ty, arg))
+            .filter(|&((&input, &ty), arg)| !should_skip(cx, input, ty, arg))
             .peekable();
         if it.peek().is_none() {
             return;
@@ -417,8 +417,8 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
             // a closure, it'll return this variant whereas if you have just an index access, it'll
             // return `ImmBorrow`. So if there is "Unique" and it's a mutable reference, we add it
             // to the mutably used variables set.
-            if borrow == ty::BorrowKind::MutBorrow
-                || (borrow == ty::BorrowKind::UniqueImmBorrow && base_ty.ref_mutability() == Some(Mutability::Mut))
+            if borrow == ty::BorrowKind::Mutable
+                || (borrow == ty::BorrowKind::UniqueImmutable && base_ty.ref_mutability() == Some(Mutability::Mut))
             {
                 self.add_mutably_used_var(*vid);
             } else if self.is_in_unsafe_block(id) {
@@ -426,7 +426,7 @@ impl<'tcx> euv::Delegate<'tcx> for MutablyUsedVariablesCtxt<'tcx> {
                 // upon!
                 self.add_mutably_used_var(*vid);
             }
-        } else if borrow == ty::ImmBorrow {
+        } else if borrow == ty::BorrowKind::Immutable {
             // If there is an `async block`, it'll contain a call to a closure which we need to
             // go into to ensure all "mutate" checks are found.
             if let Node::Expr(Expr {
diff --git a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
index 0775d7abdbb..b65ec8c3c48 100644
--- a/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
+++ b/src/tools/clippy/clippy_lints/src/needless_pass_by_value.rs
@@ -180,11 +180,16 @@ impl<'tcx> LateLintPass<'tcx> for NeedlessPassByValue {
             if !is_self(arg)
                 && !ty.is_mutable_ptr()
                 && !is_copy(cx, ty)
-                && ty.is_sized(cx.tcx, cx.param_env)
+                && ty.is_sized(cx.tcx, cx.typing_env())
                 && !allowed_traits.iter().any(|&t| {
-                    implements_trait_with_env_from_iter(cx.tcx, cx.param_env, ty, t, None, [Option::<
-                        ty::GenericArg<'tcx>,
-                    >::None])
+                    implements_trait_with_env_from_iter(
+                        cx.tcx,
+                        cx.typing_env(),
+                        ty, 
+                        t,
+                        None,
+                        [None::<ty::GenericArg<'tcx>>]
+                    )
                 })
                 && !implements_borrow_trait
                 && !all_borrowable_trait
diff --git a/src/tools/clippy/clippy_lints/src/neg_multiply.rs b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
index f84d9fadb85..a0ba2aaf552 100644
--- a/src/tools/clippy/clippy_lints/src/neg_multiply.rs
+++ b/src/tools/clippy/clippy_lints/src/neg_multiply.rs
@@ -58,7 +58,7 @@ fn check_mul(cx: &LateContext<'_>, span: Span, lit: &Expr<'_>, exp: &Expr<'_>) {
     {
         let mut applicability = Applicability::MachineApplicable;
         let (snip, from_macro) = snippet_with_context(cx, exp.span, span.ctxt(), "..", &mut applicability);
-        let suggestion = if !from_macro && exp.precedence().order() < PREC_PREFIX && !has_enclosing_paren(&snip) {
+        let suggestion = if !from_macro && exp.precedence() < PREC_PREFIX && !has_enclosing_paren(&snip) {
             format!("-({snip})")
         } else {
             format!("-{snip}")
diff --git a/src/tools/clippy/clippy_lints/src/no_effect.rs b/src/tools/clippy/clippy_lints/src/no_effect.rs
index 2e5195d459f..74536028b5d 100644
--- a/src/tools/clippy/clippy_lints/src/no_effect.rs
+++ b/src/tools/clippy/clippy_lints/src/no_effect.rs
@@ -159,8 +159,12 @@ impl NoEffect {
 
                                 // Remove `impl Future<Output = T>` to get `T`
                                 if cx.tcx.ty_is_opaque_future(ret_ty)
-                                    && let Some(true_ret_ty) =
-                                        cx.tcx.infer_ctxt().build(cx.typing_mode()).err_ctxt().get_impl_future_output_ty(ret_ty)
+                                    && let Some(true_ret_ty) = cx
+                                        .tcx
+                                        .infer_ctxt()
+                                        .build(cx.typing_mode())
+                                        .err_ctxt()
+                                        .get_impl_future_output_ty(ret_ty)
                                 {
                                     ret_ty = true_ret_ty;
                                 }
diff --git a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs
index 8d5a523fd8f..64587e08ddc 100644
--- a/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs
+++ b/src/tools/clippy/clippy_lints/src/no_mangle_with_rust_abi.rs
@@ -1,5 +1,5 @@
 use clippy_utils::diagnostics::span_lint_and_then;
-use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::source::{snippet, snippet_with_applicability};
 use rustc_errors::Applicability;
 use rustc_hir::{Item, ItemKind};
 use rustc_lint::{LateContext, LateLintPass};
@@ -18,13 +18,13 @@ declare_clippy_lint! {
     /// Rust ABI can break this at any point.
     ///
     /// ### Example
-    /// ```no_run
+    /// ```rust,ignore
     ///  #[no_mangle]
     ///  fn example(arg_one: u32, arg_two: usize) {}
     /// ```
     ///
     /// Use instead:
-    /// ```no_run
+    /// ```rust,ignore
     ///  #[no_mangle]
     ///  extern "C" fn example(arg_one: u32, arg_two: usize) {}
     /// ```
@@ -40,24 +40,25 @@ impl<'tcx> LateLintPass<'tcx> for NoMangleWithRustAbi {
         if let ItemKind::Fn(fn_sig, _, _) = &item.kind {
             let attrs = cx.tcx.hir().attrs(item.hir_id());
             let mut app = Applicability::MaybeIncorrect;
-            let snippet = snippet_with_applicability(cx, fn_sig.span, "..", &mut app);
+            let fn_snippet = snippet_with_applicability(cx, fn_sig.span.with_hi(item.ident.span.lo()), "..", &mut app);
             for attr in attrs {
                 if let Some(ident) = attr.ident()
                     && ident.name == rustc_span::sym::no_mangle
                     && fn_sig.header.abi == Abi::Rust
-                    && let Some((fn_attrs, _)) = snippet.split_once("fn")
+                    && let Some((fn_attrs, _)) = fn_snippet.rsplit_once("fn")
                     && !fn_attrs.contains("extern")
                 {
                     let sugg_span = fn_sig
                         .span
                         .with_lo(fn_sig.span.lo() + BytePos::from_usize(fn_attrs.len()))
                         .shrink_to_lo();
+                    let attr_snippet = snippet(cx, attr.span, "..");
 
                     span_lint_and_then(
                         cx,
                         NO_MANGLE_WITH_RUST_ABI,
                         fn_sig.span,
-                        "`#[no_mangle]` set on a function with the default (`Rust`) ABI",
+                        format!("`{attr_snippet}` set on a function with the default (`Rust`) ABI"),
                         |diag| {
                             diag.span_suggestion(sugg_span, "set an ABI", "extern \"C\" ", app)
                                 .span_suggestion(sugg_span, "or explicitly set the default", "extern \"Rust\" ", app);
diff --git a/src/tools/clippy/clippy_lints/src/non_copy_const.rs b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
index 5e20b406426..5416e00fe0c 100644
--- a/src/tools/clippy/clippy_lints/src/non_copy_const.rs
+++ b/src/tools/clippy/clippy_lints/src/non_copy_const.rs
@@ -270,31 +270,31 @@ impl<'tcx> NonCopyConst<'tcx> {
             instance,
             promoted: None,
         };
-        let param_env = cx.tcx.param_env(def_id).with_reveal_all_normalized(cx.tcx);
-        let result = cx.tcx.const_eval_global_id_for_typeck(param_env, cid, DUMMY_SP);
+        let typing_env = ty::TypingEnv::post_analysis(cx.tcx, def_id);
+        let result = cx.tcx.const_eval_global_id_for_typeck(typing_env, cid, DUMMY_SP);
         Self::is_value_unfrozen_raw(cx, result, ty)
     }
 
     fn is_value_unfrozen_expr(cx: &LateContext<'tcx>, hir_id: HirId, def_id: DefId, ty: Ty<'tcx>) -> bool {
         let args = cx.typeck_results().node_args(hir_id);
 
-        let result = Self::const_eval_resolve(cx.tcx, cx.param_env, ty::UnevaluatedConst::new(def_id, args), DUMMY_SP);
+        let result = Self::const_eval_resolve(cx.tcx, cx.typing_env(), ty::UnevaluatedConst::new(def_id, args), DUMMY_SP);
         Self::is_value_unfrozen_raw(cx, result, ty)
     }
 
     pub fn const_eval_resolve(
         tcx: TyCtxt<'tcx>,
-        param_env: ty::ParamEnv<'tcx>,
+        typing_env: ty::TypingEnv<'tcx>,
         ct: ty::UnevaluatedConst<'tcx>,
         span: Span,
     ) -> EvalToValTreeResult<'tcx> {
-        match ty::Instance::try_resolve(tcx, param_env, ct.def, ct.args) {
+        match ty::Instance::try_resolve(tcx, typing_env, ct.def, ct.args) {
             Ok(Some(instance)) => {
                 let cid = GlobalId {
                     instance,
                     promoted: None,
                 };
-                tcx.const_eval_global_id_for_typeck(param_env, cid, span)
+                tcx.const_eval_global_id_for_typeck(typing_env, cid, span)
             },
             Ok(None) => Err(ErrorHandled::TooGeneric(span)),
             Err(err) => Err(ErrorHandled::Reported(err.into(), span)),
@@ -321,7 +321,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
 
             // Normalize assoc types because ones originated from generic params
             // bounded other traits could have their bound.
-            let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
+            let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty);
             if self.interior_mut.is_interior_mut_ty(cx, normalized)
                 // When there's no default value, lint it only according to its type;
                 // in other words, lint consts whose value *could* be unfrozen, not definitely is.
@@ -361,12 +361,12 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
                             .trait_item_def_id
                         && cx
                             .tcx
-                            .layout_of(cx.tcx.param_env(of_trait_def_id).and(
+                            .layout_of(ty::TypingEnv::post_analysis(cx.tcx, of_trait_def_id).as_query_input(
                                 // Normalize assoc types because ones originated from generic params
                                 // bounded other traits could have their bound at the trait defs;
                                 // and, in that case, the definition is *not* generic.
                                 cx.tcx.normalize_erasing_regions(
-                                    cx.tcx.param_env(of_trait_def_id),
+                                    ty::TypingEnv::post_analysis(cx.tcx, of_trait_def_id),
                                     cx.tcx.type_of(of_assoc_item).instantiate_identity(),
                                 ),
                             ))
@@ -376,7 +376,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
                             // similar to unknown layouts.
                             // e.g. `layout_of(...).is_err() || has_frozen_variant(...);`
                         && let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity()
-                        && let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty)
+                        && let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty)
                         && self.interior_mut.is_interior_mut_ty(cx, normalized)
                         && Self::is_value_unfrozen_poly(cx, *body_id, normalized)
                     {
@@ -386,7 +386,7 @@ impl<'tcx> LateLintPass<'tcx> for NonCopyConst<'tcx> {
                 ItemKind::Impl(Impl { of_trait: None, .. }) => {
                     let ty = cx.tcx.type_of(impl_item.owner_id).instantiate_identity();
                     // Normalize assoc types originated from generic params.
-                    let normalized = cx.tcx.normalize_erasing_regions(cx.param_env, ty);
+                    let normalized = cx.tcx.normalize_erasing_regions(cx.typing_env(), ty);
 
                     if self.interior_mut.is_interior_mut_ty(cx, normalized)
                         && Self::is_value_unfrozen_poly(cx, *body_id, normalized)
diff --git a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
index 3f156aa5510..0caa19cd844 100644
--- a/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
+++ b/src/tools/clippy/clippy_lints/src/non_octal_unix_permissions.rs
@@ -43,12 +43,12 @@ impl<'tcx> LateLintPass<'tcx> for NonOctalUnixPermissions {
         match &expr.kind {
             ExprKind::MethodCall(path, func, [param], _) => {
                 if let Some(adt) = cx.typeck_results().expr_ty(func).peel_refs().ty_adt_def()
-                    && ((path.ident.name == sym!(mode)
+                    && ((path.ident.name.as_str() == "mode"
                         && matches!(
                             cx.tcx.get_diagnostic_name(adt.did()),
                             Some(sym::FsOpenOptions | sym::DirBuilder)
                         ))
-                        || (path.ident.name == sym!(set_mode)
+                        || (path.ident.name.as_str() == "set_mode"
                             && cx.tcx.is_diagnostic_item(sym::FsPermissions, adt.did())))
                     && let ExprKind::Lit(_) = param.kind
                     && param.span.eq_ctxt(expr.span)
diff --git a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
index a13391a5945..83f7d931969 100644
--- a/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
+++ b/src/tools/clippy/clippy_lints/src/nonstandard_macro_braces.rs
@@ -88,7 +88,7 @@ fn is_offending_macro(cx: &EarlyContext<'_>, span: Span, mac_braces: &MacroBrace
             || span
                 .macro_backtrace()
                 .last()
-                .map_or(false, |e| e.macro_def_id.map_or(false, DefId::is_local))
+                .is_some_and(|e| e.macro_def_id.is_some_and(DefId::is_local))
     };
     let span_call_site = span.ctxt().outer_expn_data().call_site;
     if let ExpnKind::Macro(MacroKind::Bang, mac_name) = span.ctxt().outer_expn_data().kind
diff --git a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
index 372128ac16f..13b3d240700 100644
--- a/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
+++ b/src/tools/clippy/clippy_lints/src/only_used_in_recursion.rs
@@ -290,7 +290,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
                     Some((Node::Expr(parent), child_id)) => match parent.kind {
                         // Recursive call. Track which index the parameter is used in.
                         ExprKind::Call(callee, args)
-                            if path_def_id(cx, callee).map_or(false, |id| {
+                            if path_def_id(cx, callee).is_some_and(|id| {
                                 id == param.fn_id && has_matching_args(param.fn_kind, typeck.node_args(callee.hir_id))
                             }) =>
                         {
@@ -300,7 +300,7 @@ impl<'tcx> LateLintPass<'tcx> for OnlyUsedInRecursion {
                             return;
                         },
                         ExprKind::MethodCall(_, receiver, args, _)
-                            if typeck.type_dependent_def_id(parent.hir_id).map_or(false, |id| {
+                            if typeck.type_dependent_def_id(parent.hir_id).is_some_and(|id| {
                                 id == param.fn_id && has_matching_args(param.fn_kind, typeck.node_args(parent.hir_id))
                             }) =>
                         {
diff --git a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
index 8b9f899d82d..65ef56fd211 100644
--- a/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/arithmetic_side_effects.rs
@@ -325,7 +325,7 @@ impl ArithmeticSideEffects {
     fn should_skip_expr<'tcx>(&mut self, cx: &LateContext<'tcx>, expr: &hir::Expr<'tcx>) -> bool {
         is_lint_allowed(cx, ARITHMETIC_SIDE_EFFECTS, expr.hir_id)
             || self.expr_span.is_some()
-            || self.const_span.map_or(false, |sp| sp.contains(expr.span))
+            || self.const_span.is_some_and(|sp| sp.contains(expr.span))
     }
 }
 
diff --git a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs
index 11c97b4ef00..0dcaec1c9a7 100644
--- a/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/assign_op_pattern.rs
@@ -102,7 +102,7 @@ fn imm_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> HirIdSet {
     struct S(HirIdSet);
     impl Delegate<'_> for S {
         fn borrow(&mut self, place: &PlaceWithHirId<'_>, _: HirId, kind: BorrowKind) {
-            if matches!(kind, BorrowKind::ImmBorrow | BorrowKind::UniqueImmBorrow) {
+            if matches!(kind, BorrowKind::Immutable | BorrowKind::UniqueImmutable) {
                 self.0.insert(match place.place.base {
                     PlaceBase::Local(id) => id,
                     PlaceBase::Upvar(id) => id.var_path.hir_id,
@@ -127,7 +127,7 @@ fn mut_borrows_in_expr(cx: &LateContext<'_>, e: &hir::Expr<'_>) -> HirIdSet {
     struct S(HirIdSet);
     impl Delegate<'_> for S {
         fn borrow(&mut self, place: &PlaceWithHirId<'_>, _: HirId, kind: BorrowKind) {
-            if matches!(kind, BorrowKind::MutBorrow) {
+            if matches!(kind, BorrowKind::Mutable) {
                 self.0.insert(match place.place.base {
                     PlaceBase::Local(id) => id,
                     PlaceBase::Upvar(id) => id.var_path.hir_id,
diff --git a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs
index 208b20a7a06..b0d872e98fd 100644
--- a/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/cmp_owned.rs
@@ -42,14 +42,12 @@ fn check_op(cx: &LateContext<'_>, expr: &Expr<'_>, other: &Expr<'_>, left: bool)
             if typeck
                 .type_dependent_def_id(expr.hir_id)
                 .and_then(|id| cx.tcx.trait_of_item(id))
-                .map_or(false, |id| {
-                    matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned))
-                }) =>
+                .is_some_and(|id| matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ToString | sym::ToOwned))) =>
         {
             (arg, arg.span)
         },
         ExprKind::Call(path, [arg])
-            if path_def_id(cx, path).map_or(false, |did| {
+            if path_def_id(cx, path).is_some_and(|did| {
                 if cx.tcx.is_diagnostic_item(sym::from_str_method, did) {
                     true
                 } else if cx.tcx.is_diagnostic_item(sym::from_fn, did) {
diff --git a/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs
index 5d94cfab3b0..1a0bfd8b997 100644
--- a/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/const_comparisons.rs
@@ -26,7 +26,7 @@ fn comparison_to_const<'tcx>(
     if let ExprKind::Binary(operator, left, right) = expr.kind
         && let Ok(cmp_op) = CmpOp::try_from(operator.node)
     {
-        let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck);
+        let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), typeck);
         match (ecx.eval(left), ecx.eval(right)) {
             (Some(_), Some(_)) => None,
             (_, Some(con)) => Some((cmp_op, left, right, con, typeck.expr_ty(right))),
diff --git a/src/tools/clippy/clippy_lints/src/operators/erasing_op.rs b/src/tools/clippy/clippy_lints/src/operators/erasing_op.rs
index 24bfe2b050b..e3fc8d8fea7 100644
--- a/src/tools/clippy/clippy_lints/src/operators/erasing_op.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/erasing_op.rs
@@ -39,7 +39,7 @@ fn check_op<'tcx>(
     other: &Expr<'tcx>,
     parent: &Expr<'tcx>,
 ) {
-    if ConstEvalCtxt::with_env(cx.tcx, cx.param_env, tck).eval_simple(op) == Some(Constant::Int(0)) {
+    if ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), tck).eval_simple(op) == Some(Constant::Int(0)) {
         if different_types(tck, other, parent) {
             return;
         }
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 8e394944c21..8272d3643d4 100644
--- a/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/float_cmp.rs
@@ -17,8 +17,7 @@ pub(crate) fn check<'tcx>(
     right: &'tcx Expr<'_>,
 ) {
     if (op == BinOpKind::Eq || op == BinOpKind::Ne) && is_float(cx, left) {
-        let typeck = cx.typeck_results();
-        let ecx = ConstEvalCtxt::with_env(cx.tcx, cx.param_env, typeck);
+        let ecx = ConstEvalCtxt::new(cx);
         let left_is_local = match ecx.eval_with_source(left) {
             Some((c, s)) if !is_allowed(&c) => s.is_local(),
             Some(_) => return,
@@ -107,7 +106,7 @@ fn is_signum(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     }
 
     if let ExprKind::MethodCall(method_name, self_arg, [], _) = expr.kind
-        && sym!(signum) == method_name.ident.name
+        && method_name.ident.name.as_str() == "signum"
     // Check that the receiver of the signum() is a float (expressions[0] is the receiver of
     // the method call)
     {
diff --git a/src/tools/clippy/clippy_lints/src/operators/identity_op.rs b/src/tools/clippy/clippy_lints/src/operators/identity_op.rs
index 830be50c8ba..1c2d6e90fc9 100644
--- a/src/tools/clippy/clippy_lints/src/operators/identity_op.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/identity_op.rs
@@ -42,83 +42,43 @@ pub(crate) fn check<'tcx>(
 
     match op {
         BinOpKind::Add | BinOpKind::BitOr | BinOpKind::BitXor => {
-            let _ = check_op(
-                cx,
-                left,
-                0,
-                expr.span,
-                peeled_right_span,
-                needs_parenthesis(cx, expr, right),
-                right_is_coerced_to_value,
-            ) || check_op(
-                cx,
-                right,
-                0,
-                expr.span,
-                peeled_left_span,
-                Parens::Unneeded,
-                left_is_coerced_to_value,
-            );
+            if is_redundant_op(cx, left, 0) {
+                let paren = needs_parenthesis(cx, expr, right);
+                span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value);
+            } else if is_redundant_op(cx, right, 0) {
+                let paren = needs_parenthesis(cx, expr, left);
+                span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
+            }
         },
         BinOpKind::Shl | BinOpKind::Shr | BinOpKind::Sub => {
-            let _ = check_op(
-                cx,
-                right,
-                0,
-                expr.span,
-                peeled_left_span,
-                Parens::Unneeded,
-                left_is_coerced_to_value,
-            );
+            if is_redundant_op(cx, right, 0) {
+                let paren = needs_parenthesis(cx, expr, left);
+                span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
+            }
         },
         BinOpKind::Mul => {
-            let _ = check_op(
-                cx,
-                left,
-                1,
-                expr.span,
-                peeled_right_span,
-                needs_parenthesis(cx, expr, right),
-                right_is_coerced_to_value,
-            ) || check_op(
-                cx,
-                right,
-                1,
-                expr.span,
-                peeled_left_span,
-                Parens::Unneeded,
-                left_is_coerced_to_value,
-            );
+            if is_redundant_op(cx, left, 1) {
+                let paren = needs_parenthesis(cx, expr, right);
+                span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value);
+            } else if is_redundant_op(cx, right, 1) {
+                let paren = needs_parenthesis(cx, expr, left);
+                span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
+            }
         },
         BinOpKind::Div => {
-            let _ = check_op(
-                cx,
-                right,
-                1,
-                expr.span,
-                peeled_left_span,
-                Parens::Unneeded,
-                left_is_coerced_to_value,
-            );
+            if is_redundant_op(cx, right, 1) {
+                let paren = needs_parenthesis(cx, expr, left);
+                span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
+            }
         },
         BinOpKind::BitAnd => {
-            let _ = check_op(
-                cx,
-                left,
-                -1,
-                expr.span,
-                peeled_right_span,
-                needs_parenthesis(cx, expr, right),
-                right_is_coerced_to_value,
-            ) || check_op(
-                cx,
-                right,
-                -1,
-                expr.span,
-                peeled_left_span,
-                Parens::Unneeded,
-                left_is_coerced_to_value,
-            );
+            if is_redundant_op(cx, left, -1) {
+                let paren = needs_parenthesis(cx, expr, right);
+                span_ineffective_operation(cx, expr.span, peeled_right_span, paren, right_is_coerced_to_value);
+            } else if is_redundant_op(cx, right, -1) {
+                let paren = needs_parenthesis(cx, expr, left);
+                span_ineffective_operation(cx, expr.span, peeled_left_span, paren, left_is_coerced_to_value);
+            }
         },
         BinOpKind::Rem => check_remainder(cx, left, right, expr.span, left.span),
         _ => (),
@@ -138,43 +98,70 @@ enum Parens {
     Unneeded,
 }
 
-/// Checks if `left op right` needs parenthesis when reduced to `right`
+/// Checks if a binary expression needs parenthesis when reduced to just its
+/// right or left child.
+///
+/// e.g. `-(x + y + 0)` cannot be reduced to `-x + y`, as the behavior changes silently.
+/// e.g. `1u64 + ((x + y + 0i32) as u64)` cannot be reduced to `1u64 + x + y as u64`, since
+/// the the cast expression will not apply to the same expression.
 /// e.g. `0 + if b { 1 } else { 2 } + if b { 3 } else { 4 }` cannot be reduced
 /// to `if b { 1 } else { 2 } + if b { 3 } else { 4 }` where the `if` could be
-/// interpreted as a statement
+/// interpreted as a statement. The same behavior happens for `match`, `loop`,
+/// and blocks.
+/// e.g.  `2 * (0 + { a })` can be reduced to `2 * { a }` without the need for parenthesis,
+/// but `1 * ({ a } + 4)` cannot be reduced to `{ a } + 4`, as a block at the start of a line
+/// will be interpreted as a statement instead of an expression.
 ///
-/// See #8724
-fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, right: &Expr<'_>) -> Parens {
-    match right.kind {
+/// See #8724, #13470
+fn needs_parenthesis(cx: &LateContext<'_>, binary: &Expr<'_>, child: &Expr<'_>) -> Parens {
+    match child.kind {
         ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) => {
-            // ensure we're checking against the leftmost expression of `right`
-            //
-            //     ~~~ `lhs`
-            // 0 + {4} * 2
-            //     ~~~~~~~ `right`
-            return needs_parenthesis(cx, binary, lhs);
+            // For casts and binary expressions, we want to add parenthesis if
+            // the parent HIR node is an expression, or if the parent HIR node
+            // is a Block or Stmt, and the new left hand side would need
+            // parenthesis be treated as a statement rather than an expression.
+            if let Some((_, parent)) = cx.tcx.hir().parent_iter(binary.hir_id).next() {
+                match parent {
+                    Node::Expr(_) => return Parens::Needed,
+                    Node::Block(_) | Node::Stmt(_) => {
+                        // ensure we're checking against the leftmost expression of `child`
+                        //
+                        // ~~~~~~~~~~~ `binary`
+                        //     ~~~ `lhs`
+                        // 0 + {4} * 2
+                        //     ~~~~~~~ `child`
+                        return needs_parenthesis(cx, binary, lhs);
+                    },
+                    _ => return Parens::Unneeded,
+                }
+            }
+        },
+        ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) => {
+            // For if, match, block, and loop expressions, we want to add parenthesis if
+            // the closest ancestor node that is not an expression is a block or statement.
+            // This would mean that the rustfix suggestion will appear at the start of a line, which causes
+            // these expressions to be interpreted as statements if they do not have parenthesis.
+            let mut prev_id = binary.hir_id;
+            for (_, parent) in cx.tcx.hir().parent_iter(binary.hir_id) {
+                if let Node::Expr(expr) = parent
+                    && let ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) | ExprKind::Unary(_, lhs) = expr.kind
+                    && lhs.hir_id == prev_id
+                {
+                    // keep going until we find a node that encompasses left of `binary`
+                    prev_id = expr.hir_id;
+                    continue;
+                }
+
+                match parent {
+                    Node::Block(_) | Node::Stmt(_) => return Parens::Needed,
+                    _ => return Parens::Unneeded,
+                };
+            }
+        },
+        _ => {
+            return Parens::Unneeded;
         },
-        ExprKind::If(..) | ExprKind::Match(..) | ExprKind::Block(..) | ExprKind::Loop(..) => {},
-        _ => return Parens::Unneeded,
-    }
-
-    let mut prev_id = binary.hir_id;
-    for (_, node) in cx.tcx.hir().parent_iter(binary.hir_id) {
-        if let Node::Expr(expr) = node
-            && let ExprKind::Binary(_, lhs, _) | ExprKind::Cast(lhs, _) = expr.kind
-            && lhs.hir_id == prev_id
-        {
-            // keep going until we find a node that encompasses left of `binary`
-            prev_id = expr.hir_id;
-            continue;
-        }
-
-        match node {
-            Node::Block(_) | Node::Stmt(_) => break,
-            _ => return Parens::Unneeded,
-        };
     }
-
     Parens::Needed
 }
 
@@ -199,7 +186,7 @@ fn check_remainder(cx: &LateContext<'_>, left: &Expr<'_>, right: &Expr<'_>, span
     }
 }
 
-fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, parens: Parens, is_erased: bool) -> bool {
+fn is_redundant_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8) -> bool {
     if let Some(Constant::Int(v)) = ConstEvalCtxt::new(cx).eval_simple(e).map(Constant::peel_refs) {
         let check = match *cx.typeck_results().expr_ty(e).peel_refs().kind() {
             ty::Int(ity) => unsext(cx.tcx, -1_i128, ity),
@@ -212,7 +199,6 @@ fn check_op(cx: &LateContext<'_>, e: &Expr<'_>, m: i8, span: Span, arg: Span, pa
             1 => v == 1,
             _ => unreachable!(),
         } {
-            span_ineffective_operation(cx, span, arg, parens, is_erased);
             return true;
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs
index 565294bb40a..d369978b8be 100644
--- a/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs
+++ b/src/tools/clippy/clippy_lints/src/operators/numeric_arithmetic.rs
@@ -14,7 +14,7 @@ pub struct Context {
 }
 impl Context {
     fn skip_expr(&mut self, e: &hir::Expr<'_>) -> bool {
-        self.expr_id.is_some() || self.const_span.map_or(false, |span| span.contains(e.span))
+        self.expr_id.is_some() || self.const_span.is_some_and(|span| span.contains(e.span))
     }
 
     pub fn check_binary<'tcx>(
diff --git a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
index 18e6aad9c9a..794bef7b321 100644
--- a/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/partialeq_ne_impl.rs
@@ -34,7 +34,7 @@ declare_lint_pass!(PartialEqNeImpl => [PARTIALEQ_NE_IMPL]);
 impl<'tcx> LateLintPass<'tcx> for PartialEqNeImpl {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
         if let ItemKind::Impl(Impl {
-            of_trait: Some(ref trait_ref),
+            of_trait: Some(trait_ref),
             items: impl_items,
             ..
         }) = item.kind
diff --git a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
index 1bddfab39c6..b2089487a9f 100644
--- a/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
+++ b/src/tools/clippy/clippy_lints/src/pass_by_ref_or_value.rs
@@ -192,7 +192,7 @@ impl PassByRefOrValue {
                                 continue;
                             }
                         }
-                        let value_type = if fn_body.and_then(|body| body.params.get(index)).map_or(false, is_self) {
+                        let value_type = if fn_body.and_then(|body| body.params.get(index)).is_some_and(is_self) {
                             "self".into()
                         } else {
                             snippet(cx, decl_ty.span, "_").into()
diff --git a/src/tools/clippy/clippy_lints/src/ptr.rs b/src/tools/clippy/clippy_lints/src/ptr.rs
index a548c6ef3b1..ecc095f3859 100644
--- a/src/tools/clippy/clippy_lints/src/ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr.rs
@@ -727,9 +727,8 @@ fn get_ref_lm<'tcx>(ty: &'tcx hir::Ty<'tcx>) -> Option<(&'tcx Lifetime, Mutabili
 
 fn is_null_path(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     if let ExprKind::Call(pathexp, []) = expr.kind {
-        path_def_id(cx, pathexp).map_or(false, |id| {
-            matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut))
-        })
+        path_def_id(cx, pathexp)
+            .is_some_and(|id| matches!(cx.tcx.get_diagnostic_name(id), Some(sym::ptr_null | sym::ptr_null_mut)))
     } else {
         false
     }
diff --git a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
index 56d07aeae17..808a7e005c6 100644
--- a/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
+++ b/src/tools/clippy/clippy_lints/src/ptr_offset_with_cast.rs
@@ -96,7 +96,7 @@ fn expr_as_ptr_offset_call<'tcx>(
             if path_segment.ident.name == sym::offset {
                 return Some((arg_0, arg_1, Method::Offset));
             }
-            if path_segment.ident.name == sym!(wrapping_offset) {
+            if path_segment.ident.name.as_str() == "wrapping_offset" {
                 return Some((arg_0, arg_1, Method::WrappingOffset));
             }
         }
diff --git a/src/tools/clippy/clippy_lints/src/question_mark.rs b/src/tools/clippy/clippy_lints/src/question_mark.rs
index 9344cb41993..f69cb9be4ca 100644
--- a/src/tools/clippy/clippy_lints/src/question_mark.rs
+++ b/src/tools/clippy/clippy_lints/src/question_mark.rs
@@ -8,18 +8,18 @@ use clippy_utils::source::snippet_with_applicability;
 use clippy_utils::ty::{implements_trait, is_type_diagnostic_item};
 use clippy_utils::{
     eq_expr_value, higher, is_else_clause, is_in_const_context, is_lint_allowed, is_path_lang_item, is_res_lang_ctor,
-    pat_and_expr_can_be_question_mark, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt,
-    span_contains_comment,
+    pat_and_expr_can_be_question_mark, path_res, path_to_local, path_to_local_id, peel_blocks, peel_blocks_with_stmt,
+    span_contains_cfg, span_contains_comment,
 };
 use rustc_errors::Applicability;
 use rustc_hir::LangItem::{self, OptionNone, OptionSome, ResultErr, ResultOk};
 use rustc_hir::def::Res;
 use rustc_hir::{
-    BindingMode, Block, Body, ByRef, Expr, ExprKind, LetStmt, Mutability, Node, PatKind, PathSegment, QPath, Stmt,
-    StmtKind,
+    Arm, BindingMode, Block, Body, ByRef, Expr, ExprKind, FnRetTy, HirId, LetStmt, MatchSource, Mutability, Node, Pat,
+    PatKind, PathSegment, QPath, Stmt, StmtKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
-use rustc_middle::ty::Ty;
+use rustc_middle::ty::{self, Ty};
 use rustc_session::impl_lint_pass;
 use rustc_span::sym;
 use rustc_span::symbol::Symbol;
@@ -58,6 +58,9 @@ pub struct QuestionMark {
     /// if it is greater than zero.
     /// As for why we need this in the first place: <https://github.com/rust-lang/rust-clippy/issues/8628>
     try_block_depth_stack: Vec<u32>,
+    /// Keeps track of the number of inferred return type closures we are inside, to avoid problems
+    /// with the `Err(x.into())` expansion being ambiguious.
+    inferred_ret_closure_stack: u16,
 }
 
 impl_lint_pass!(QuestionMark => [QUESTION_MARK, MANUAL_LET_ELSE]);
@@ -68,6 +71,7 @@ impl QuestionMark {
             msrv: conf.msrv.clone(),
             matches_behaviour: conf.matches_for_let_else,
             try_block_depth_stack: Vec::new(),
+            inferred_ret_closure_stack: 0,
         }
     }
 }
@@ -93,7 +97,7 @@ enum IfBlockType<'hir> {
 
 fn find_let_else_ret_expression<'hir>(block: &'hir Block<'hir>) -> Option<&'hir Expr<'hir>> {
     if let Block {
-        stmts: &[],
+        stmts: [],
         expr: Some(els),
         ..
     } = block
@@ -125,7 +129,7 @@ fn check_let_some_else_return_none(cx: &LateContext<'_>, stmt: &Stmt<'_>) {
         cx.tcx
             .lang_items()
             .try_trait()
-            .map_or(false, |did| implements_trait(cx, init_ty, did, &[]))
+            .is_some_and(|did| implements_trait(cx, init_ty, did, &[]))
     }
 
     if let StmtKind::Let(LetStmt {
@@ -163,8 +167,8 @@ fn is_early_return(smbl: Symbol, cx: &LateContext<'_>, if_block: &IfBlockType<'_
             is_type_diagnostic_item(cx, caller_ty, smbl)
                 && expr_return_none_or_err(smbl, cx, if_then, caller, None)
                 && match smbl {
-                    sym::Option => call_sym == sym!(is_none),
-                    sym::Result => call_sym == sym!(is_err),
+                    sym::Option => call_sym.as_str() == "is_none",
+                    sym::Result => call_sym.as_str() == "is_err",
                     _ => false,
                 }
         },
@@ -247,7 +251,7 @@ fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Ex
     {
         let mut applicability = Applicability::MachineApplicable;
         let receiver_str = snippet_with_applicability(cx, caller.span, "..", &mut applicability);
-        let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.param_env)
+        let by_ref = !caller_ty.is_copy_modulo_regions(cx.tcx, cx.typing_env())
             && !matches!(caller.kind, ExprKind::Call(..) | ExprKind::MethodCall(..));
         let sugg = if let Some(else_inner) = r#else {
             if eq_expr_value(cx, caller, peel_blocks(else_inner)) {
@@ -271,6 +275,135 @@ fn check_is_none_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Ex
     }
 }
 
+#[derive(Clone, Copy, Debug)]
+enum TryMode {
+    Result,
+    Option,
+}
+
+fn find_try_mode<'tcx>(cx: &LateContext<'tcx>, scrutinee: &Expr<'tcx>) -> Option<TryMode> {
+    let scrutinee_ty = cx.typeck_results().expr_ty_adjusted(scrutinee);
+    let ty::Adt(scrutinee_adt_def, _) = scrutinee_ty.kind() else {
+        return None;
+    };
+
+    match cx.tcx.get_diagnostic_name(scrutinee_adt_def.did())? {
+        sym::Result => Some(TryMode::Result),
+        sym::Option => Some(TryMode::Option),
+        _ => None,
+    }
+}
+
+// Check that `pat` is `{ctor_lang_item}(val)`, returning `val`.
+fn extract_ctor_call<'a, 'tcx>(
+    cx: &LateContext<'tcx>,
+    expected_ctor: LangItem,
+    pat: &'a Pat<'tcx>,
+) -> Option<&'a Pat<'tcx>> {
+    if let PatKind::TupleStruct(variant_path, [val_binding], _) = &pat.kind
+        && is_res_lang_ctor(cx, cx.qpath_res(variant_path, pat.hir_id), expected_ctor)
+    {
+        Some(val_binding)
+    } else {
+        None
+    }
+}
+
+// Extracts the local ID of a plain `val` pattern.
+fn extract_binding_pat(pat: &Pat<'_>) -> Option<HirId> {
+    if let PatKind::Binding(BindingMode::NONE, binding, _, None) = pat.kind {
+        Some(binding)
+    } else {
+        None
+    }
+}
+
+fn check_arm_is_some_or_ok<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &Arm<'tcx>) -> bool {
+    let happy_ctor = match mode {
+        TryMode::Result => ResultOk,
+        TryMode::Option => OptionSome,
+    };
+
+    // Check for `Ok(val)` or `Some(val)`
+    if arm.guard.is_none()
+        && let Some(val_binding) = extract_ctor_call(cx, happy_ctor, arm.pat)
+        // Extract out `val`
+        && let Some(binding) = extract_binding_pat(val_binding)
+        // Check body is just `=> val`
+        && path_to_local_id(peel_blocks(arm.body), binding)
+    {
+        true
+    } else {
+        false
+    }
+}
+
+fn check_arm_is_none_or_err<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm: &Arm<'tcx>) -> bool {
+    if arm.guard.is_some() {
+        return false;
+    }
+
+    let arm_body = peel_blocks(arm.body);
+    match mode {
+        TryMode::Result => {
+            // Check that pat is Err(val)
+            if let Some(ok_pat) = extract_ctor_call(cx, ResultErr, arm.pat)
+                && let Some(ok_val) = extract_binding_pat(ok_pat)
+                // check `=> return Err(...)`
+                && let ExprKind::Ret(Some(wrapped_ret_expr)) = arm_body.kind
+                && let ExprKind::Call(ok_ctor, [ret_expr]) = wrapped_ret_expr.kind
+                && is_res_lang_ctor(cx, path_res(cx, ok_ctor), ResultErr)
+                // check `...` is `val` from binding
+                && path_to_local_id(ret_expr, ok_val)
+            {
+                true
+            } else {
+                false
+            }
+        },
+        TryMode::Option => {
+            // Check the pat is `None`
+            if is_res_lang_ctor(cx, path_res(cx, arm.pat), OptionNone)
+                // Check `=> return None`
+                && let ExprKind::Ret(Some(ret_expr)) = arm_body.kind
+                && is_res_lang_ctor(cx, path_res(cx, ret_expr), OptionNone)
+                && !ret_expr.span.from_expansion()
+            {
+                true
+            } else {
+                false
+            }
+        },
+    }
+}
+
+fn check_arms_are_try<'tcx>(cx: &LateContext<'tcx>, mode: TryMode, arm1: &Arm<'tcx>, arm2: &Arm<'tcx>) -> bool {
+    (check_arm_is_some_or_ok(cx, mode, arm1) && check_arm_is_none_or_err(cx, mode, arm2))
+        || (check_arm_is_some_or_ok(cx, mode, arm2) && check_arm_is_none_or_err(cx, mode, arm1))
+}
+
+fn check_if_try_match<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
+    if let ExprKind::Match(scrutinee, [arm1, arm2], MatchSource::Normal | MatchSource::Postfix) = expr.kind
+        && !expr.span.from_expansion()
+        && let Some(mode) = find_try_mode(cx, scrutinee)
+        && !span_contains_cfg(cx, expr.span)
+        && check_arms_are_try(cx, mode, arm1, arm2)
+    {
+        let mut applicability = Applicability::MachineApplicable;
+        let snippet = snippet_with_applicability(cx, scrutinee.span.source_callsite(), "..", &mut applicability);
+
+        span_lint_and_sugg(
+            cx,
+            QUESTION_MARK,
+            expr.span,
+            "this `match` expression can be replaced with `?`",
+            "try instead",
+            snippet.into_owned() + "?",
+            applicability,
+        );
+    }
+}
+
 fn check_if_let_some_or_err_and_early_return<'tcx>(cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
     if let Some(higher::IfLet {
         let_pat,
@@ -339,6 +472,17 @@ fn is_try_block(cx: &LateContext<'_>, bl: &Block<'_>) -> bool {
     }
 }
 
+fn is_inferred_ret_closure(expr: &Expr<'_>) -> bool {
+    let ExprKind::Closure(closure) = expr.kind else {
+        return false;
+    };
+
+    match closure.fn_decl.output {
+        FnRetTy::Return(ret_ty) => ret_ty.is_suggestable_infer_ty(),
+        FnRetTy::DefaultReturn(_) => true,
+    }
+}
+
 impl<'tcx> LateLintPass<'tcx> for QuestionMark {
     fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx Stmt<'_>) {
         if !is_lint_allowed(cx, QUESTION_MARK_USED, stmt.hir_id) {
@@ -350,11 +494,27 @@ impl<'tcx> LateLintPass<'tcx> for QuestionMark {
         }
         self.check_manual_let_else(cx, stmt);
     }
+
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx Expr<'_>) {
+        if is_inferred_ret_closure(expr) {
+            self.inferred_ret_closure_stack += 1;
+            return;
+        }
+
         if !self.inside_try_block() && !is_in_const_context(cx) && is_lint_allowed(cx, QUESTION_MARK_USED, expr.hir_id)
         {
             check_is_none_or_err_and_early_return(cx, expr);
             check_if_let_some_or_err_and_early_return(cx, expr);
+
+            if self.inferred_ret_closure_stack == 0 {
+                check_if_try_match(cx, expr);
+            }
+        }
+    }
+
+    fn check_expr_post(&mut self, _: &LateContext<'tcx>, expr: &'tcx Expr<'tcx>) {
+        if is_inferred_ret_closure(expr) {
+            self.inferred_ret_closure_stack -= 1;
         }
     }
 
diff --git a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
index 030df535c35..79baa914b03 100644
--- a/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
+++ b/src/tools/clippy/clippy_lints/src/redundant_slicing.rs
@@ -85,7 +85,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
             let (expr_ty, expr_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(expr));
             let (indexed_ty, indexed_ref_count) = peel_middle_ty_refs(cx.typeck_results().expr_ty(indexed));
             let parent_expr = get_parent_expr(cx, expr);
-            let needs_parens_for_prefix = parent_expr.map_or(false, |parent| parent.precedence().order() > PREC_PREFIX);
+            let needs_parens_for_prefix = parent_expr.is_some_and(|parent| parent.precedence() > PREC_PREFIX);
 
             if expr_ty == indexed_ty {
                 if expr_ref_count > indexed_ref_count {
@@ -107,7 +107,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
                         kind: ExprKind::AddrOf(BorrowKind::Ref, Mutability::Mut, _),
                         ..
                     })
-                ) || cx.typeck_results().expr_adjustments(expr).first().map_or(false, |a| {
+                ) || cx.typeck_results().expr_adjustments(expr).first().is_some_and(|a| {
                     matches!(
                         a.kind,
                         Adjust::Borrow(AutoBorrow::Ref(AutoBorrowMutability::Mut { .. }))
@@ -136,7 +136,7 @@ impl<'tcx> LateLintPass<'tcx> for RedundantSlicing {
                 });
             } else if let Some(target_id) = cx.tcx.lang_items().deref_target() {
                 if let Ok(deref_ty) = cx.tcx.try_normalize_erasing_regions(
-                    cx.param_env,
+                    cx.typing_env(),
                     Ty::new_projection_from_args(cx.tcx, target_id, cx.tcx.mk_args(&[GenericArg::from(indexed_ty)])),
                 ) {
                     if deref_ty == expr_ty {
diff --git a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs
index 19ce08bde10..7d59bf24d93 100644
--- a/src/tools/clippy/clippy_lints/src/ref_option_ref.rs
+++ b/src/tools/clippy/clippy_lints/src/ref_option_ref.rs
@@ -39,7 +39,7 @@ impl<'tcx> LateLintPass<'tcx> for RefOptionRef {
     fn check_ty(&mut self, cx: &LateContext<'tcx>, ty: &'tcx Ty<'tcx>) {
         if let TyKind::Ref(_, ref mut_ty) = ty.kind
             && mut_ty.mutbl == Mutability::Not
-            && let TyKind::Path(ref qpath) = &mut_ty.ty.kind
+            && let TyKind::Path(qpath) = &mut_ty.ty.kind
             && let last = last_path_segment(qpath)
             && let Some(def_id) = last.res.opt_def_id()
             && cx.tcx.is_diagnostic_item(sym::Option, def_id)
diff --git a/src/tools/clippy/clippy_lints/src/returns.rs b/src/tools/clippy/clippy_lints/src/returns.rs
index 110dea8fb8e..aeff31d02d2 100644
--- a/src/tools/clippy/clippy_lints/src/returns.rs
+++ b/src/tools/clippy/clippy_lints/src/returns.rs
@@ -347,7 +347,7 @@ fn check_final_expr<'tcx>(
     let peeled_drop_expr = expr.peel_drop_temps();
     match &peeled_drop_expr.kind {
         // simple return is always "bad"
-        ExprKind::Ret(ref inner) => {
+        ExprKind::Ret(inner) => {
             // check if expr return nothing
             let ret_span = if inner.is_none() && replacement == RetReplacement::Empty {
                 extend_span_to_previous_non_ws(cx, peeled_drop_expr.span)
@@ -391,7 +391,7 @@ fn check_final_expr<'tcx>(
 
             if let Some(inner) = inner {
                 if for_each_unconsumed_temporary(cx, inner, |temporary_ty| {
-                    if temporary_ty.has_significant_drop(cx.tcx, cx.param_env)
+                    if temporary_ty.has_significant_drop(cx.tcx, cx.typing_env())
                         && temporary_ty
                             .walk()
                             .any(|arg| matches!(arg.unpack(), GenericArgKind::Lifetime(re) if !re.is_static()))
diff --git a/src/tools/clippy/clippy_lints/src/semicolon_block.rs b/src/tools/clippy/clippy_lints/src/semicolon_block.rs
index 09f1c112352..d85f4a8fa01 100644
--- a/src/tools/clippy/clippy_lints/src/semicolon_block.rs
+++ b/src/tools/clippy/clippy_lints/src/semicolon_block.rs
@@ -101,17 +101,21 @@ impl SemicolonBlock {
         );
     }
 
-    fn semicolon_outside_block(
-        &self,
-        cx: &LateContext<'_>,
-        block: &Block<'_>,
-        tail_stmt_expr: &Expr<'_>,
-        semi_span: Span,
-    ) {
+    fn semicolon_outside_block(&self, cx: &LateContext<'_>, block: &Block<'_>, tail_stmt_expr: &Expr<'_>) {
         let insert_span = block.span.with_lo(block.span.hi());
-        // account for macro calls
-        let semi_span = cx.sess().source_map().stmt_span(semi_span, block.span);
-        let remove_span = semi_span.with_lo(tail_stmt_expr.span.source_callsite().hi());
+
+        // For macro call semicolon statements (`mac!();`), the statement's span does not actually
+        // include the semicolon itself, so use `mac_call_stmt_semi_span`, which finds the semicolon
+        // based on a source snippet.
+        // (Does not use `stmt_span` as that requires `.from_expansion()` to return true,
+        // which is not the case for e.g. `line!();` and `asm!();`)
+        let Some(remove_span) = cx
+            .sess()
+            .source_map()
+            .mac_call_stmt_semi_span(tail_stmt_expr.span.source_callsite())
+        else {
+            return;
+        };
 
         if self.semicolon_outside_block_ignore_multiline && get_line(cx, remove_span) != get_line(cx, insert_span) {
             return;
@@ -150,13 +154,12 @@ impl LateLintPass<'_> for SemicolonBlock {
                 };
                 let &Stmt {
                     kind: StmtKind::Semi(expr),
-                    span,
                     ..
                 } = stmt
                 else {
                     return;
                 };
-                self.semicolon_outside_block(cx, block, expr, span);
+                self.semicolon_outside_block(cx, block, expr);
             },
             StmtKind::Semi(Expr {
                 kind: ExprKind::Block(block @ Block { expr: Some(tail), .. }, _),
diff --git a/src/tools/clippy/clippy_lints/src/serde_api.rs b/src/tools/clippy/clippy_lints/src/serde_api.rs
index fcf1c4cc5c2..6a0dfde2d9c 100644
--- a/src/tools/clippy/clippy_lints/src/serde_api.rs
+++ b/src/tools/clippy/clippy_lints/src/serde_api.rs
@@ -26,7 +26,7 @@ declare_lint_pass!(SerdeApi => [SERDE_API_MISUSE]);
 impl<'tcx> LateLintPass<'tcx> for SerdeApi {
     fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) {
         if let ItemKind::Impl(Impl {
-            of_trait: Some(ref trait_ref),
+            of_trait: Some(trait_ref),
             items,
             ..
         }) = item.kind
diff --git a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
index abd8363456d..1a5b958e6a6 100644
--- a/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
+++ b/src/tools/clippy/clippy_lints/src/significant_drop_tightening.rs
@@ -154,7 +154,7 @@ impl<'cx, 'others, 'tcx> AttrChecker<'cx, 'others, 'tcx> {
         let ty = self
             .cx
             .tcx
-            .try_normalize_erasing_regions(self.cx.param_env, ty)
+            .try_normalize_erasing_regions(self.cx.typing_env(), ty)
             .unwrap_or(ty);
         match self.type_cache.entry(ty) {
             Entry::Occupied(e) => return *e.get(),
diff --git a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
index 9fdee8543a8..fa082453504 100644
--- a/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
+++ b/src/tools/clippy/clippy_lints/src/single_component_path_imports.rs
@@ -174,7 +174,7 @@ impl SingleComponentPathImports {
         }
 
         match &item.kind {
-            ItemKind::Mod(_, ModKind::Loaded(ref items, ..)) => {
+            ItemKind::Mod(_, ModKind::Loaded(items, ..)) => {
                 self.check_mod(items);
             },
             ItemKind::MacroDef(MacroDef { macro_rules: true, .. }) => {
diff --git a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
index ec6e45256d1..8c8f11569c8 100644
--- a/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
+++ b/src/tools/clippy/clippy_lints/src/slow_vector_initialization.rs
@@ -235,7 +235,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> {
         if self.initialization_found
             && let ExprKind::MethodCall(path, self_arg, [extend_arg], _) = expr.kind
             && path_to_local_id(self_arg, self.vec_alloc.local_id)
-            && path.ident.name == sym!(extend)
+            && path.ident.name.as_str() == "extend"
             && self.is_repeat_take(extend_arg)
         {
             self.slow_expression = Some(InitializationType::Extend(expr));
@@ -247,7 +247,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> {
         if self.initialization_found
             && let ExprKind::MethodCall(path, self_arg, [len_arg, fill_arg], _) = expr.kind
             && path_to_local_id(self_arg, self.vec_alloc.local_id)
-            && path.ident.name == sym!(resize)
+            && path.ident.name.as_str() == "resize"
             // Check that is filled with 0
             && is_integer_literal(fill_arg, 0)
         {
@@ -269,7 +269,7 @@ impl<'tcx> VectorInitializationVisitor<'_, 'tcx> {
     /// Returns `true` if give expression is `repeat(0).take(...)`
     fn is_repeat_take(&mut self, expr: &'tcx Expr<'tcx>) -> bool {
         if let ExprKind::MethodCall(take_path, recv, [len_arg], _) = expr.kind
-            && take_path.ident.name == sym!(take)
+            && take_path.ident.name.as_str() == "take"
             // Check that take is applied to `repeat(0)`
             && self.is_repeat_zero(recv)
         {
diff --git a/src/tools/clippy/clippy_lints/src/strings.rs b/src/tools/clippy/clippy_lints/src/strings.rs
index bf49bb60162..e09c0706006 100644
--- a/src/tools/clippy/clippy_lints/src/strings.rs
+++ b/src/tools/clippy/clippy_lints/src/strings.rs
@@ -286,7 +286,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
 
         if !in_external_macro(cx.sess(), e.span)
             && let ExprKind::MethodCall(path, receiver, ..) = &e.kind
-            && path.ident.name == sym!(as_bytes)
+            && path.ident.name.as_str() == "as_bytes"
             && let ExprKind::Lit(lit) = &receiver.kind
             && let LitKind::Str(lit_content, _) = &lit.node
         {
@@ -332,7 +332,7 @@ impl<'tcx> LateLintPass<'tcx> for StringLitAsBytes {
         }
 
         if let ExprKind::MethodCall(path, recv, [], _) = &e.kind
-            && path.ident.name == sym!(into_bytes)
+            && path.ident.name.as_str() == "into_bytes"
             && let ExprKind::MethodCall(path, recv, [], _) = &recv.kind
             && matches!(path.ident.name.as_str(), "to_owned" | "to_string")
             && let ExprKind::Lit(lit) = &recv.kind
@@ -493,7 +493,7 @@ impl<'tcx> LateLintPass<'tcx> for TrimSplitWhitespace {
     fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'_>) {
         let tyckres = cx.typeck_results();
         if let ExprKind::MethodCall(path, split_recv, [], split_ws_span) = expr.kind
-            && path.ident.name == sym!(split_whitespace)
+            && path.ident.name.as_str() == "split_whitespace"
             && let Some(split_ws_def_id) = tyckres.type_dependent_def_id(expr.hir_id)
             && cx.tcx.is_diagnostic_item(sym::str_split_whitespace, split_ws_def_id)
             && let ExprKind::MethodCall(path, _trim_recv, [], trim_span) = split_recv.kind
diff --git a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
index be32dc0aab0..0d809c17989 100644
--- a/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
+++ b/src/tools/clippy/clippy_lints/src/suspicious_operation_groupings.rs
@@ -334,7 +334,7 @@ fn strip_non_ident_wrappers(expr: &Expr) -> &Expr {
     let mut output = expr;
     loop {
         output = match &output.kind {
-            ExprKind::Paren(ref inner) | ExprKind::Unary(_, ref inner) => inner,
+            ExprKind::Paren(inner) | ExprKind::Unary(_, inner) => inner,
             _ => {
                 return output;
             },
@@ -348,13 +348,13 @@ fn extract_related_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> {
 
 fn if_statement_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> {
     match kind {
-        ExprKind::If(ref condition, _, _) => chained_binops(&condition.kind),
-        ExprKind::Paren(ref e) => if_statement_binops(&e.kind),
-        ExprKind::Block(ref block, _) => {
+        ExprKind::If(condition, _, _) => chained_binops(&condition.kind),
+        ExprKind::Paren(e) => if_statement_binops(&e.kind),
+        ExprKind::Block(block, _) => {
             let mut output = None;
             for stmt in &block.stmts {
-                match stmt.kind {
-                    StmtKind::Expr(ref e) | StmtKind::Semi(ref e) => {
+                match &stmt.kind {
+                    StmtKind::Expr(e) | StmtKind::Semi(e) => {
                         output = append_opt_vecs(output, if_statement_binops(&e.kind));
                     },
                     _ => {},
@@ -383,7 +383,7 @@ fn append_opt_vecs<A>(target_opt: Option<Vec<A>>, source_opt: Option<Vec<A>>) ->
 fn chained_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> {
     match kind {
         ExprKind::Binary(_, left_outer, right_outer) => chained_binops_helper(left_outer, right_outer),
-        ExprKind::Paren(ref e) | ExprKind::Unary(_, ref e) => chained_binops(&e.kind),
+        ExprKind::Paren(e) | ExprKind::Unary(_, e) => chained_binops(&e.kind),
         _ => None,
     }
 }
@@ -391,16 +391,14 @@ fn chained_binops(kind: &ExprKind) -> Option<Vec<BinaryOp<'_>>> {
 fn chained_binops_helper<'expr>(left_outer: &'expr Expr, right_outer: &'expr Expr) -> Option<Vec<BinaryOp<'expr>>> {
     match (&left_outer.kind, &right_outer.kind) {
         (
-            ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e),
-            ExprKind::Paren(ref right_e) | ExprKind::Unary(_, ref right_e),
+            ExprKind::Paren(left_e) | ExprKind::Unary(_, left_e),
+            ExprKind::Paren(right_e) | ExprKind::Unary(_, right_e),
         ) => chained_binops_helper(left_e, right_e),
-        (ExprKind::Paren(ref left_e) | ExprKind::Unary(_, ref left_e), _) => chained_binops_helper(left_e, right_outer),
-        (_, ExprKind::Paren(ref right_e) | ExprKind::Unary(_, ref right_e)) => {
-            chained_binops_helper(left_outer, right_e)
-        },
+        (ExprKind::Paren(left_e) | ExprKind::Unary(_, left_e), _) => chained_binops_helper(left_e, right_outer),
+        (_, ExprKind::Paren(right_e) | ExprKind::Unary(_, right_e)) => chained_binops_helper(left_outer, right_e),
         (
-            ExprKind::Binary(Spanned { node: left_op, .. }, ref left_left, ref left_right),
-            ExprKind::Binary(Spanned { node: right_op, .. }, ref right_left, ref right_right),
+            ExprKind::Binary(Spanned { node: left_op, .. }, left_left, left_right),
+            ExprKind::Binary(Spanned { node: right_op, .. }, right_left, right_right),
         ) => match (
             chained_binops_helper(left_left, left_right),
             chained_binops_helper(right_left, right_right),
diff --git a/src/tools/clippy/clippy_lints/src/swap.rs b/src/tools/clippy/clippy_lints/src/swap.rs
index 6cba560393d..78f0b7d121c 100644
--- a/src/tools/clippy/clippy_lints/src/swap.rs
+++ b/src/tools/clippy/clippy_lints/src/swap.rs
@@ -392,7 +392,7 @@ impl<'tcx> IndexBinding<'_, 'tcx> {
         for stmt in self.block.stmts {
             match stmt.kind {
                 StmtKind::Expr(expr) | StmtKind::Semi(expr) => v.visit_expr(expr),
-                StmtKind::Let(LetStmt { ref init, .. }) => {
+                StmtKind::Let(LetStmt { init, .. }) => {
                     if let Some(init) = init.as_ref() {
                         v.visit_expr(init);
                     }
@@ -412,9 +412,7 @@ impl<'tcx> IndexBinding<'_, 'tcx> {
                 }
                 Self::is_used_slice_indexed(lhs, idx_ident) || Self::is_used_slice_indexed(rhs, idx_ident)
             },
-            ExprKind::Path(QPath::Resolved(_, path)) => {
-                path.segments.first().map_or(false, |idx| idx.ident == idx_ident)
-            },
+            ExprKind::Path(QPath::Resolved(_, path)) => path.segments.first().is_some_and(|idx| idx.ident == idx_ident),
             _ => false,
         }
     }
diff --git a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
index 52bb7c4bd68..50a1577b288 100644
--- a/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
+++ b/src/tools/clippy/clippy_lints/src/trailing_empty_array.rs
@@ -58,7 +58,7 @@ fn is_struct_with_trailing_zero_sized_array<'tcx>(cx: &LateContext<'tcx>, item:
         && let Some(last_field) = data.fields().last()
         && let field_ty = cx
             .tcx
-            .normalize_erasing_regions(cx.param_env, cx.tcx.type_of(last_field.def_id).instantiate_identity())
+            .normalize_erasing_regions(cx.typing_env(), cx.tcx.type_of(last_field.def_id).instantiate_identity())
         && let ty::Array(_, array_len) = *field_ty.kind()
         && let Some(0) = array_len.try_to_target_usize(cx.tcx)
     {
diff --git a/src/tools/clippy/clippy_lints/src/trait_bounds.rs b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
index 3da4bf67558..6f3c6682305 100644
--- a/src/tools/clippy/clippy_lints/src/trait_bounds.rs
+++ b/src/tools/clippy/clippy_lints/src/trait_bounds.rs
@@ -10,8 +10,8 @@ use rustc_data_structures::unhash::UnhashMap;
 use rustc_errors::Applicability;
 use rustc_hir::def::Res;
 use rustc_hir::{
-    GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment, PredicateOrigin, QPath,
-    TraitBoundModifiers, TraitItem, TraitRef, Ty, TyKind, WherePredicate, BoundPolarity,
+    BoundPolarity, GenericBound, Generics, Item, ItemKind, LangItem, Node, Path, PathSegment, PredicateOrigin, QPath,
+    TraitBoundModifiers, TraitItem, TraitRef, Ty, TyKind, WherePredicateKind,
 };
 use rustc_lint::{LateContext, LateLintPass};
 use rustc_session::impl_lint_pass;
@@ -124,9 +124,9 @@ impl<'tcx> LateLintPass<'tcx> for TraitBounds {
         let mut self_bounds_map = FxHashMap::default();
 
         for predicate in item.generics.predicates {
-            if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate
+            if let WherePredicateKind::BoundPredicate(bound_predicate) = predicate.kind
                 && bound_predicate.origin != PredicateOrigin::ImplTrait
-                && !bound_predicate.span.from_expansion()
+                && !predicate.span.from_expansion()
                 && let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind
                 && let Some(PathSegment {
                     res: Res::SelfTyParam { trait_: def_id },
@@ -268,10 +268,10 @@ impl TraitBounds {
         let mut map: UnhashMap<SpanlessTy<'_, '_>, Vec<&GenericBound<'_>>> = UnhashMap::default();
         let mut applicability = Applicability::MaybeIncorrect;
         for bound in generics.predicates {
-            if let WherePredicate::BoundPredicate(ref p) = bound
+            if let WherePredicateKind::BoundPredicate(p) = bound.kind
                 && p.origin != PredicateOrigin::ImplTrait
                 && p.bounds.len() as u64 <= self.max_trait_bounds
-                && !p.span.from_expansion()
+                && !bound.span.from_expansion()
                 && let bounds = p
                     .bounds
                     .iter()
@@ -295,7 +295,7 @@ impl TraitBounds {
                 span_lint_and_help(
                     cx,
                     TYPE_REPETITION_IN_BOUNDS,
-                    p.span,
+                    bound.span,
                     "this type has already been used as a bound predicate",
                     None,
                     hint_string,
@@ -322,8 +322,8 @@ fn check_trait_bound_duplication<'tcx>(cx: &LateContext<'tcx>, generics: &'_ Gen
         .predicates
         .iter()
         .filter_map(|pred| {
-            if pred.in_where_clause()
-                && let WherePredicate::BoundPredicate(bound_predicate) = pred
+            if pred.kind.in_where_clause()
+                && let WherePredicateKind::BoundPredicate(bound_predicate) = pred.kind
                 && let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind
             {
                 return Some(
@@ -347,10 +347,10 @@ fn check_trait_bound_duplication<'tcx>(cx: &LateContext<'tcx>, generics: &'_ Gen
     //            |
     // compare trait bounds keyed by generic name and comparable trait to collected where
     // predicates eg. (T, Clone)
-    for predicate in generics.predicates.iter().filter(|pred| !pred.in_where_clause()) {
-        if let WherePredicate::BoundPredicate(bound_predicate) = predicate
+    for predicate in generics.predicates.iter().filter(|pred| !pred.kind.in_where_clause()) {
+        if let WherePredicateKind::BoundPredicate(bound_predicate) = predicate.kind
             && bound_predicate.origin != PredicateOrigin::ImplTrait
-            && !bound_predicate.span.from_expansion()
+            && !predicate.span.from_expansion()
             && let TyKind::Path(QPath::Resolved(_, path)) = bound_predicate.bounded_ty.kind
         {
             let traits = rollup_traits(cx, bound_predicate.bounds, "these bounds contain repeated elements");
@@ -379,7 +379,7 @@ struct ComparableTraitRef<'a, 'tcx> {
 
 impl PartialEq for ComparableTraitRef<'_, '_> {
     fn eq(&self, other: &Self) -> bool {
-        SpanlessEq::new(self.cx).eq_modifiers(self.modifiers, other.modifiers)
+        SpanlessEq::eq_modifiers(self.modifiers, other.modifiers)
             && SpanlessEq::new(self.cx)
                 .paths_by_resolution()
                 .eq_path(self.trait_ref.path, other.trait_ref.path)
diff --git a/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs b/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs
index 1dfc9f7091e..1209bd5b34f 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/eager_transmute.rs
@@ -43,7 +43,7 @@ fn binops_with_local(cx: &LateContext<'_>, local_expr: &Expr<'_>, expr: &Expr<'_
             binops_with_local(cx, local_expr, lhs) || binops_with_local(cx, local_expr, rhs)
         },
         ExprKind::MethodCall(path, receiver, [arg], _)
-            if path.ident.name == sym!(contains)
+            if path.ident.name.as_str() == "contains"
                 // ... `contains` called on some kind of range
                 && let Some(receiver_adt) = cx.typeck_results().expr_ty(receiver).peel_refs().ty_adt_def()
                 && let lang_items = cx.tcx.lang_items()
@@ -81,15 +81,15 @@ pub(super) fn check<'tcx>(
     if let Some(then_some_call) = peel_parent_unsafe_blocks(cx, expr)
         && let ExprKind::MethodCall(path, receiver, [arg], _) = then_some_call.kind
         && cx.typeck_results().expr_ty(receiver).is_bool()
-        && path.ident.name == sym!(then_some)
+        && path.ident.name.as_str() == "then_some"
         && is_local_with_projections(transmutable)
         && binops_with_local(cx, transmutable, receiver)
         && is_normalizable(cx, cx.param_env, from_ty)
         && is_normalizable(cx, cx.param_env, to_ty)
         // we only want to lint if the target type has a niche that is larger than the one of the source type
         // e.g. `u8` to `NonZero<u8>` should lint, but `NonZero<u8>` to `u8` should not
-        && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from_ty))
-        && let Ok(to_layout) = cx.tcx.layout_of(cx.param_env.and(to_ty))
+        && let Ok(from_layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(from_ty))
+        && let Ok(to_layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(to_ty))
         && match (from_layout.largest_niche, to_layout.largest_niche) {
             (Some(from_niche), Some(to_niche)) => !range_fully_contained(from_niche.valid_range, to_niche.valid_range),
             (None, Some(_)) => true,
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs
index 0772b284968..bf6700b1b6b 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_ptr_to_ptr.rs
@@ -27,7 +27,7 @@ pub(super) fn check<'tcx>(
                 |diag| {
                     if let Some(arg) = sugg::Sugg::hir_opt(cx, arg) {
                         if from_mutbl == to_mutbl
-                            && to_pointee_ty.is_sized(cx.tcx, cx.param_env)
+                            && to_pointee_ty.is_sized(cx.tcx, cx.typing_env())
                             && msrv.meets(msrvs::POINTER_CAST)
                         {
                             diag.span_suggestion_verbose(
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs
index 3b32e4396b9..48d65eb15d9 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmute_undefined_repr.rs
@@ -206,12 +206,12 @@ fn reduce_refs<'tcx>(cx: &LateContext<'tcx>, mut from_ty: Ty<'tcx>, mut to_ty: T
                 continue;
             },
             (&(ty::Ref(_, unsized_ty, _) | ty::RawPtr(unsized_ty, _)), _)
-                if !unsized_ty.is_sized(cx.tcx, cx.param_env) =>
+                if !unsized_ty.is_sized(cx.tcx, cx.typing_env()) =>
             {
                 (true, false)
             },
             (_, &(ty::Ref(_, unsized_ty, _) | ty::RawPtr(unsized_ty, _)))
-                if !unsized_ty.is_sized(cx.tcx, cx.param_env) =>
+                if !unsized_ty.is_sized(cx.tcx, cx.typing_env()) =>
             {
                 (false, true)
             },
@@ -244,7 +244,7 @@ enum ReducedTy<'tcx> {
 /// Reduce structs containing a single non-zero sized field to it's contained type.
 fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx> {
     loop {
-        ty = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty).unwrap_or(ty);
+        ty = cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty).unwrap_or(ty);
         return match *ty.kind() {
             ty::Array(sub_ty, _) if matches!(sub_ty.kind(), ty::Int(_) | ty::Uint(_)) => {
                 ReducedTy::TypeErasure { raw_ptr_only: false }
@@ -297,8 +297,8 @@ fn reduce_ty<'tcx>(cx: &LateContext<'tcx>, mut ty: Ty<'tcx>) -> ReducedTy<'tcx>
 }
 
 fn is_zero_sized_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
-    if let Ok(ty) = cx.tcx.try_normalize_erasing_regions(cx.param_env, ty)
-        && let Ok(layout) = cx.tcx.layout_of(cx.param_env.and(ty))
+    if let Ok(ty) = cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty)
+        && let Ok(layout) = cx.tcx.layout_of(cx.typing_env().as_query_input(ty))
     {
         layout.layout.size().bytes() == 0
     } else {
diff --git a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
index fca332dba40..cad15b1e982 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/transmutes_expressible_as_ptr_casts.rs
@@ -1,7 +1,7 @@
 use super::TRANSMUTES_EXPRESSIBLE_AS_PTR_CASTS;
 use clippy_utils::diagnostics::span_lint_and_sugg;
 use clippy_utils::sugg::Sugg;
-use rustc_ast::ExprPrecedence;
+use rustc_ast::util::parser::AssocOp;
 use rustc_errors::Applicability;
 use rustc_hir::{Expr, Node};
 use rustc_hir_typeck::cast::check_cast;
@@ -44,7 +44,7 @@ pub(super) fn check<'tcx>(
     };
 
     if let Node::Expr(parent) = cx.tcx.parent_hir_node(e.hir_id)
-        && parent.precedence().order() > ExprPrecedence::Cast.order()
+        && parent.precedence() > AssocOp::As.precedence() as i8
     {
         sugg = format!("({sugg})");
     }
diff --git a/src/tools/clippy/clippy_lints/src/transmute/utils.rs b/src/tools/clippy/clippy_lints/src/transmute/utils.rs
index e8ccd35b4da..5baa67b1f3e 100644
--- a/src/tools/clippy/clippy_lints/src/transmute/utils.rs
+++ b/src/tools/clippy/clippy_lints/src/transmute/utils.rs
@@ -4,10 +4,11 @@ use rustc_middle::ty::Ty;
 // check if the component types of the transmuted collection and the result have different ABI,
 // size or alignment
 pub(super) fn is_layout_incompatible<'tcx>(cx: &LateContext<'tcx>, from: Ty<'tcx>, to: Ty<'tcx>) -> bool {
-    if let Ok(from) = cx.tcx.try_normalize_erasing_regions(cx.param_env, from)
-        && let Ok(to) = cx.tcx.try_normalize_erasing_regions(cx.param_env, to)
-        && let Ok(from_layout) = cx.tcx.layout_of(cx.param_env.and(from))
-        && let Ok(to_layout) = cx.tcx.layout_of(cx.param_env.and(to))
+    let typing_env = cx.typing_env();
+    if let Ok(from) = cx.tcx.try_normalize_erasing_regions(typing_env, from)
+        && let Ok(to) = cx.tcx.try_normalize_erasing_regions(typing_env, to)
+        && let Ok(from_layout) = cx.tcx.layout_of(typing_env.as_query_input(from))
+        && let Ok(to_layout) = cx.tcx.layout_of(typing_env.as_query_input(to))
     {
         from_layout.size != to_layout.size || from_layout.align.abi != to_layout.align.abi
     } else {
diff --git a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
index eb7ffbbe360..bde88ab61ad 100644
--- a/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
+++ b/src/tools/clippy/clippy_lints/src/types/borrowed_box.rs
@@ -51,8 +51,7 @@ pub(super) fn check(cx: &LateContext<'_>, hir_ty: &hir::Ty<'_>, lt: &Lifetime, m
                         format!("&{ltopt}({inner_snippet})")
                     },
                     TyKind::Path(qpath)
-                        if get_bounds_if_impl_trait(cx, qpath, inner.hir_id)
-                            .map_or(false, |bounds| bounds.len() > 1) =>
+                        if get_bounds_if_impl_trait(cx, qpath, inner.hir_id).is_some_and(|bounds| bounds.len() > 1) =>
                     {
                         format!("&{ltopt}({inner_snippet})")
                     },
diff --git a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
index 1a656088b17..de3456a8ba5 100644
--- a/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
+++ b/src/tools/clippy/clippy_lints/src/types/redundant_allocation.rs
@@ -60,7 +60,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, hir_ty: &hir::Ty<'tcx>, qpath:
             // here because `mod.rs` guarantees this lint is only run on types outside of bodies and
             // is not run on locals.
             let ty = lower_ty(cx.tcx, hir_ty);
-            if ty.has_escaping_bound_vars() || !ty.is_sized(cx.tcx, cx.param_env) {
+            if ty.has_escaping_bound_vars() || !ty.is_sized(cx.tcx, cx.typing_env()) {
                 return false;
             }
             hir_ty.span
diff --git a/src/tools/clippy/clippy_lints/src/types/vec_box.rs b/src/tools/clippy/clippy_lints/src/types/vec_box.rs
index 230239335c6..9b236d3bda5 100644
--- a/src/tools/clippy/clippy_lints/src/types/vec_box.rs
+++ b/src/tools/clippy/clippy_lints/src/types/vec_box.rs
@@ -37,7 +37,7 @@ pub(super) fn check<'tcx>(
             && let boxed_alloc_ty = last.args.get(1)
             && let ty_ty = lower_ty(cx.tcx, boxed_ty)
             && !ty_ty.has_escaping_bound_vars()
-            && ty_ty.is_sized(cx.tcx, cx.param_env)
+            && ty_ty.is_sized(cx.tcx, cx.typing_env())
             && let Ok(ty_ty_size) = cx.layout_of(ty_ty).map(|l| l.size.bytes())
             && ty_ty_size < box_size_threshold
             // https://github.com/rust-lang/rust-clippy/issues/7114
diff --git a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
index 44eb0a6c593..0bba611116b 100644
--- a/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
+++ b/src/tools/clippy/clippy_lints/src/undocumented_unsafe_blocks.rs
@@ -295,7 +295,7 @@ fn expr_has_unnecessary_safety_comment<'tcx>(
     if cx.tcx.hir().parent_iter(expr.hir_id).any(|(_, ref node)| {
         matches!(
             node,
-            Node::Block(&Block {
+            Node::Block(Block {
                 rules: BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided),
                 ..
             }),
diff --git a/src/tools/clippy/clippy_lints/src/uninhabited_references.rs b/src/tools/clippy/clippy_lints/src/uninhabited_references.rs
index cfa565cf803..ee9ef017253 100644
--- a/src/tools/clippy/clippy_lints/src/uninhabited_references.rs
+++ b/src/tools/clippy/clippy_lints/src/uninhabited_references.rs
@@ -46,7 +46,7 @@ impl LateLintPass<'_> for UninhabitedReferences {
 
         if let ExprKind::Unary(UnOp::Deref, _) = expr.kind {
             let ty = cx.typeck_results().expr_ty_adjusted(expr);
-            if ty.is_privately_uninhabited(cx.tcx, cx.param_env) {
+            if ty.is_privately_uninhabited(cx.tcx, cx.typing_env()) {
                 span_lint(
                     cx,
                     UNINHABITED_REFERENCES,
@@ -71,7 +71,7 @@ impl LateLintPass<'_> for UninhabitedReferences {
         }
         if let FnRetTy::Return(hir_ty) = fndecl.output
             && let TyKind::Ref(_, mut_ty) = hir_ty.kind
-            && lower_ty(cx.tcx, mut_ty.ty).is_privately_uninhabited(cx.tcx, cx.param_env)
+            && lower_ty(cx.tcx, mut_ty.ty).is_privately_uninhabited(cx.tcx, cx.typing_env())
         {
             span_lint(
                 cx,
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
index bf2d75ea1a9..6eef582b4b2 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/let_unit_value.rs
@@ -38,7 +38,7 @@ pub(super) fn check<'tcx>(cx: &LateContext<'tcx>, local: &'tcx LetStmt<'_>) {
             return;
         }
 
-        if (local.ty.map_or(false, |ty| !matches!(ty.kind, TyKind::Infer))
+        if (local.ty.is_some_and(|ty| !matches!(ty.kind, TyKind::Infer))
             || matches!(local.pat.kind, PatKind::Tuple([], ddpos) if ddpos.as_opt_usize().is_none()))
             && expr_needs_inferred_result(cx, init)
         {
@@ -194,7 +194,7 @@ fn needs_inferred_result_ty(
     let (id, receiver, args) = match e.kind {
         ExprKind::Call(
             Expr {
-                kind: ExprKind::Path(ref path),
+                kind: ExprKind::Path(path),
                 hir_id,
                 ..
             },
diff --git a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
index e7d26fa238e..0f4bd286145 100644
--- a/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
+++ b/src/tools/clippy/clippy_lints/src/unit_types/unit_arg.rs
@@ -149,7 +149,7 @@ fn is_empty_block(expr: &Expr<'_>) -> bool {
         expr.kind,
         ExprKind::Block(
             Block {
-                stmts: &[],
+                stmts: [],
                 expr: None,
                 ..
             },
diff --git a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
index 14f4aa6676b..34df1d5560a 100644
--- a/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
+++ b/src/tools/clippy/clippy_lints/src/unnecessary_box_returns.rs
@@ -82,7 +82,7 @@ impl UnnecessaryBoxReturns {
         // It's sometimes useful to return Box<T> if T is unsized, so don't lint those.
         // Also, don't lint if we know that T is very large, in which case returning
         // a Box<T> may be beneficial.
-        if boxed_ty.is_sized(cx.tcx, cx.param_env) && approx_ty_size(cx, boxed_ty) <= self.maximum_size {
+        if boxed_ty.is_sized(cx.tcx, cx.typing_env()) && approx_ty_size(cx, boxed_ty) <= self.maximum_size {
             span_lint_and_then(
                 cx,
                 UNNECESSARY_BOX_RETURNS,
diff --git a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
index cf406b817da..7c9455bf8ab 100644
--- a/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
+++ b/src/tools/clippy/clippy_lints/src/unused_io_amount.rs
@@ -1,6 +1,6 @@
 use clippy_utils::diagnostics::span_lint_hir_and_then;
 use clippy_utils::macros::{is_panic, root_macro_call_first_node};
-use clippy_utils::{is_res_lang_ctor, is_trait_method, match_trait_method, paths, peel_blocks};
+use clippy_utils::{is_res_lang_ctor, is_trait_method, match_def_path, match_trait_method, paths, peel_blocks};
 use hir::{ExprKind, HirId, PatKind};
 use rustc_hir as hir;
 use rustc_lint::{LateContext, LateLintPass};
@@ -83,6 +83,28 @@ impl<'tcx> LateLintPass<'tcx> for UnusedIoAmount {
     /// to consider the arms, and we want to avoid breaking the logic for situations where things
     /// get desugared to match.
     fn check_block(&mut self, cx: &LateContext<'tcx>, block: &'tcx hir::Block<'tcx>) {
+        let fn_def_id = block.hir_id.owner.to_def_id();
+        if let Some(impl_id) = cx.tcx.impl_of_method(fn_def_id)
+            && let Some(trait_id) = cx.tcx.trait_id_of_impl(impl_id)
+        {
+            // We don't want to lint inside io::Read or io::Write implementations, as the author has more
+            // information about their trait implementation than our lint, see https://github.com/rust-lang/rust-clippy/issues/4836
+            if cx.tcx.is_diagnostic_item(sym::IoRead, trait_id) || cx.tcx.is_diagnostic_item(sym::IoWrite, trait_id) {
+                return;
+            }
+
+            let async_paths: [&[&str]; 4] = [
+                &paths::TOKIO_IO_ASYNCREADEXT,
+                &paths::TOKIO_IO_ASYNCWRITEEXT,
+                &paths::FUTURES_IO_ASYNCREADEXT,
+                &paths::FUTURES_IO_ASYNCWRITEEXT,
+            ];
+
+            if async_paths.into_iter().any(|path| match_def_path(cx, trait_id, path)) {
+                return;
+            }
+        }
+
         for stmt in block.stmts {
             if let hir::StmtKind::Semi(exp) = stmt.kind {
                 check_expr(cx, exp);
@@ -222,7 +244,7 @@ fn unpack_call_chain<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
 }
 
 fn unpack_try<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
-    while let ExprKind::Call(func, [ref arg_0]) = expr.kind
+    while let ExprKind::Call(func, [arg_0]) = expr.kind
         && matches!(
             func.kind,
             ExprKind::Path(hir::QPath::LangItem(hir::LangItem::TryTraitBranch, ..))
@@ -244,7 +266,7 @@ fn unpack_match<'a>(mut expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
 /// waited on.  Otherwise return None.
 fn unpack_await<'a>(expr: &'a hir::Expr<'a>) -> &'a hir::Expr<'a> {
     if let ExprKind::Match(expr, _, hir::MatchSource::AwaitDesugar) = expr.kind {
-        if let ExprKind::Call(func, [ref arg_0]) = expr.kind {
+        if let ExprKind::Call(func, [arg_0]) = expr.kind {
             if matches!(
                 func.kind,
                 ExprKind::Path(hir::QPath::LangItem(hir::LangItem::IntoFutureIntoFuture, ..))
diff --git a/src/tools/clippy/clippy_lints/src/unwrap.rs b/src/tools/clippy/clippy_lints/src/unwrap.rs
index 096b3ff9a2e..89bb429e265 100644
--- a/src/tools/clippy/clippy_lints/src/unwrap.rs
+++ b/src/tools/clippy/clippy_lints/src/unwrap.rs
@@ -217,7 +217,7 @@ fn is_option_as_mut_use(tcx: TyCtxt<'_>, expr_id: HirId) -> bool {
 
 impl<'tcx> Delegate<'tcx> for MutationVisitor<'tcx> {
     fn borrow(&mut self, cat: &PlaceWithHirId<'tcx>, diag_expr_id: HirId, bk: ty::BorrowKind) {
-        if let ty::BorrowKind::MutBorrow = bk
+        if let ty::BorrowKind::Mutable = bk
             && is_potentially_local_place(self.local_id, &cat.place)
             && !is_option_as_mut_use(self.tcx, diag_expr_id)
         {
diff --git a/src/tools/clippy/clippy_lints/src/utils/attr_collector.rs b/src/tools/clippy/clippy_lints/src/utils/attr_collector.rs
new file mode 100644
index 00000000000..1522553bbf5
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/utils/attr_collector.rs
@@ -0,0 +1,40 @@
+use std::mem;
+use std::sync::OnceLock;
+
+use rustc_ast::{Attribute, Crate};
+use rustc_data_structures::sync::Lrc;
+use rustc_lint::{EarlyContext, EarlyLintPass};
+use rustc_session::impl_lint_pass;
+use rustc_span::Span;
+
+#[derive(Clone, Default)]
+pub struct AttrStorage(pub Lrc<OnceLock<Vec<Span>>>);
+
+pub struct AttrCollector {
+    storage: AttrStorage,
+    attrs: Vec<Span>,
+}
+
+impl AttrCollector {
+    pub fn new(storage: AttrStorage) -> Self {
+        Self {
+            storage,
+            attrs: Vec::new(),
+        }
+    }
+}
+
+impl_lint_pass!(AttrCollector => []);
+
+impl EarlyLintPass for AttrCollector {
+    fn check_attribute(&mut self, _cx: &EarlyContext<'_>, attr: &Attribute) {
+        self.attrs.push(attr.span);
+    }
+
+    fn check_crate_post(&mut self, _: &EarlyContext<'_>, _: &Crate) {
+        self.storage
+            .0
+            .set(mem::take(&mut self.attrs))
+            .expect("should only be called once");
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs
index 31f9d84f5e4..1bf24083665 100644
--- a/src/tools/clippy/clippy_lints/src/utils/author.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/author.rs
@@ -396,7 +396,7 @@ impl<'a, 'tcx> PrintVisitor<'a, 'tcx> {
                 self.pat(field!(let_expr.pat));
                 // Does what ExprKind::Cast does, only adds a clause for the type
                 // if it's a path
-                if let Some(TyKind::Path(ref qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) {
+                if let Some(TyKind::Path(qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) {
                     bind!(self, qpath);
                     chain!(self, "let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind");
                     self.qpath(qpath);
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 f662c7651f6..deb983b6971 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints.rs
@@ -6,5 +6,6 @@ pub mod lint_without_lint_pass;
 pub mod msrv_attr_impl;
 pub mod outer_expn_data_pass;
 pub mod produce_ice;
+pub mod slow_symbol_comparisons;
 pub mod unnecessary_def_path;
 pub mod unsorted_clippy_utils_paths;
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs
index a3f9abe4f96..eaeb754a23f 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/collapsible_calls.rs
@@ -79,7 +79,7 @@ impl<'tcx> LateLintPass<'tcx> for CollapsibleCalls {
 
         if let ExprKind::Call(func, [call_cx, call_lint, call_sp, call_msg, call_f]) = expr.kind
             && is_expr_path_def_path(cx, func, &["clippy_utils", "diagnostics", "span_lint_and_then"])
-            && let ExprKind::Closure(&Closure { body, .. }) = &call_f.kind
+            && let ExprKind::Closure(&Closure { body, .. }) = call_f.kind
             && let body = cx.tcx.hir().body(body)
             && let only_expr = peel_blocks_with_stmt(body.value)
             && let ExprKind::MethodCall(ps, recv, span_call_args, _) = &only_expr.kind
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
index 51235de9f29..af38e066559 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/lint_without_lint_pass.rs
@@ -21,7 +21,7 @@ declare_clippy_lint! {
     ///
     /// ### Why is this bad?
     /// The compiler only knows lints via a `LintPass`. Without
-    /// putting a lint to a `LintPass::get_lints()`'s return, the compiler will not
+    /// putting a lint to a `LintPass::lint_vec()`'s return, the compiler will not
     /// know the name of the lint.
     ///
     /// ### Known problems
@@ -123,7 +123,7 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
                     .expect("lints must have a description field");
 
                 if let ExprKind::Lit(Spanned {
-                    node: LitKind::Str(ref sym, _),
+                    node: LitKind::Str(sym, _),
                     ..
                 }) = field.expr.kind
                 {
@@ -159,8 +159,8 @@ impl<'tcx> LateLintPass<'tcx> for LintWithoutLintPass {
                 let body = cx.tcx.hir().body_owned_by(
                     impl_item_refs
                         .iter()
-                        .find(|iiref| iiref.ident.as_str() == "get_lints")
-                        .expect("LintPass needs to implement get_lints")
+                        .find(|iiref| iiref.ident.as_str() == "lint_vec")
+                        .expect("LintPass needs to implement lint_vec")
                         .id
                         .owner_id
                         .def_id,
@@ -218,9 +218,7 @@ pub(super) fn is_lint_ref_type(cx: &LateContext<'_>, ty: &hir::Ty<'_>) -> bool {
 
 fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'_>) {
     if let Some(value) = extract_clippy_version_value(cx, item) {
-        // The `sym!` macro doesn't work as it only expects a single token.
-        // It's better to keep it this way and have a direct `Symbol::intern` call here.
-        if value == Symbol::intern("pre 1.29.0") {
+        if value.as_str() == "pre 1.29.0" {
             return;
         }
 
@@ -251,7 +249,7 @@ fn check_invalid_clippy_version_attribute(cx: &LateContext<'_>, item: &'_ Item<'
 pub(super) fn extract_clippy_version_value(cx: &LateContext<'_>, item: &'_ Item<'_>) -> Option<Symbol> {
     let attrs = cx.tcx.hir().attrs(item.hir_id());
     attrs.iter().find_map(|attr| {
-        if let ast::AttrKind::Normal(ref attr_kind) = &attr.kind
+        if let ast::AttrKind::Normal(attr_kind) = &attr.kind
             // Identify attribute
             && let [tool_name, attr_name] = &attr_kind.item.path.segments[..]
             && tool_name.ident.name == sym::clippy
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs
index 63fcbd61528..68692246153 100644
--- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/msrv_attr_impl.rs
@@ -42,7 +42,7 @@ impl LateLintPass<'_> for MsrvAttrImpl {
                     .filter(|t| matches!(t.unpack(), GenericArgKind::Type(_)))
                     .any(|t| match_type(cx, t.expect_ty(), &paths::MSRV))
             })
-            && !items.iter().any(|item| item.ident.name == sym!(check_attributes))
+            && !items.iter().any(|item| item.ident.name.as_str() == "check_attributes")
         {
             let context = if is_late_pass { "LateContext" } else { "EarlyContext" };
             let lint_pass = if is_late_pass { "LateLintPass" } else { "EarlyLintPass" };
diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs
new file mode 100644
index 00000000000..3742be0e103
--- /dev/null
+++ b/src/tools/clippy/clippy_lints/src/utils/internal_lints/slow_symbol_comparisons.rs
@@ -0,0 +1,69 @@
+use clippy_utils::consts::{ConstEvalCtxt, Constant};
+use clippy_utils::diagnostics::span_lint_and_sugg;
+use clippy_utils::source::snippet_with_applicability;
+use clippy_utils::ty::match_type;
+use clippy_utils::{match_function_call, paths};
+use rustc_errors::Applicability;
+use rustc_hir::{BinOpKind, Expr, ExprKind};
+use rustc_lint::{LateContext, LateLintPass};
+use rustc_session::declare_lint_pass;
+use rustc_span::Span;
+
+declare_clippy_lint! {
+    /// ### What it does
+    ///
+    /// Detects symbol comparision using `Symbol::intern`.
+    ///
+    /// ### Why is this bad?
+    ///
+    /// Comparision via `Symbol::as_str()` is faster if the interned symbols are not reused.
+    ///
+    /// ### Example
+    ///
+    /// None, see suggestion.
+    pub SLOW_SYMBOL_COMPARISONS,
+    internal,
+    "detects slow comparisions of symbol"
+}
+
+declare_lint_pass!(SlowSymbolComparisons => [SLOW_SYMBOL_COMPARISONS]);
+
+fn check_slow_comparison<'tcx>(
+    cx: &LateContext<'tcx>,
+    op1: &'tcx Expr<'tcx>,
+    op2: &'tcx Expr<'tcx>,
+) -> Option<(Span, String)> {
+    if match_type(cx, cx.typeck_results().expr_ty(op1), &paths::SYMBOL)
+        && let Some([symbol_name_expr]) = match_function_call(cx, op2, &paths::SYMBOL_INTERN)
+        && let Some(Constant::Str(symbol_name)) = ConstEvalCtxt::new(cx).eval_simple(symbol_name_expr)
+    {
+        Some((op1.span, symbol_name))
+    } else {
+        None
+    }
+}
+
+impl<'tcx> LateLintPass<'tcx> for SlowSymbolComparisons {
+    fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &Expr<'tcx>) {
+        if let ExprKind::Binary(op, left, right) = expr.kind
+            && (op.node == BinOpKind::Eq || op.node == BinOpKind::Ne)
+            && let Some((symbol_span, symbol_name)) =
+                check_slow_comparison(cx, left, right).or_else(|| check_slow_comparison(cx, right, left))
+        {
+            let mut applicability = Applicability::MachineApplicable;
+            span_lint_and_sugg(
+                cx,
+                SLOW_SYMBOL_COMPARISONS,
+                expr.span,
+                "comparing `Symbol` via `Symbol::intern`",
+                "use `Symbol::as_str` and check the string instead",
+                format!(
+                    "{}.as_str() {} \"{symbol_name}\"",
+                    snippet_with_applicability(cx, symbol_span, "symbol", &mut applicability),
+                    op.node.as_str()
+                ),
+                applicability,
+            );
+        };
+    }
+}
diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs
index 13e9ead9a57..4476cd1005e 100644
--- a/src/tools/clippy/clippy_lints/src/utils/mod.rs
+++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs
@@ -1,5 +1,7 @@
+pub mod attr_collector;
 pub mod author;
 pub mod dump_hir;
 pub mod format_args_collector;
+
 #[cfg(feature = "internal")]
 pub mod internal_lints;
diff --git a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
index 91dff5a9523..cbc6885ae5d 100644
--- a/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
+++ b/src/tools/clippy/clippy_lints/src/vec_init_then_push.rs
@@ -98,7 +98,7 @@ impl VecPushSearcher {
                     needs_mut |= cx.typeck_results().expr_ty_adjusted(last_place).ref_mutability()
                         == Some(Mutability::Mut)
                         || get_parent_expr(cx, last_place)
-                            .map_or(false, |e| matches!(e.kind, ExprKind::AddrOf(_, Mutability::Mut, _)));
+                            .is_some_and(|e| matches!(e.kind, ExprKind::AddrOf(_, Mutability::Mut, _)));
                 },
                 ExprKind::MethodCall(_, recv, ..)
                     if recv.hir_id == e.hir_id
diff --git a/src/tools/clippy/clippy_lints/src/write.rs b/src/tools/clippy/clippy_lints/src/write.rs
index 1dd46ed5a89..a42ddcdae35 100644
--- a/src/tools/clippy/clippy_lints/src/write.rs
+++ b/src/tools/clippy/clippy_lints/src/write.rs
@@ -295,7 +295,7 @@ impl<'tcx> LateLintPass<'tcx> for Write {
             .opts
             .crate_name
             .as_ref()
-            .map_or(false, |crate_name| crate_name == "build_script_build");
+            .is_some_and(|crate_name| crate_name == "build_script_build");
 
         let allowed_in_tests = self.allow_print_in_tests && is_in_test(cx.tcx, expr.hir_id);
         match diag_name {
diff --git a/src/tools/clippy/clippy_utils/src/ast_utils.rs b/src/tools/clippy/clippy_utils/src/ast_utils.rs
index 0be6dc15a8e..c90f4a6ebfe 100644
--- a/src/tools/clippy/clippy_utils/src/ast_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/ast_utils.rs
@@ -661,8 +661,8 @@ pub fn eq_generics(l: &Generics, r: &Generics) -> bool {
 }
 
 pub fn eq_where_predicate(l: &WherePredicate, r: &WherePredicate) -> bool {
-    use WherePredicate::*;
-    match (l, r) {
+    use WherePredicateKind::*;
+    match (&l.kind, &r.kind) {
         (BoundPredicate(l), BoundPredicate(r)) => {
             over(&l.bound_generic_params, &r.bound_generic_params, |l, r| {
                 eq_generic_param(l, r)
diff --git a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
index bfb3a76ad25..3269bf758ac 100644
--- a/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
+++ b/src/tools/clippy/clippy_utils/src/check_proc_macro.rs
@@ -21,7 +21,7 @@ use rustc_hir::{
     ImplItem, ImplItemKind, IsAuto, Item, ItemKind, Lit, LoopSource, MatchSource, MutTy, Node, Path, QPath, Safety,
     TraitItem, TraitItemKind, Ty, TyKind, UnOp, UnsafeSource, Variant, VariantData, YieldSource,
 };
-use rustc_lint::{LateContext, LintContext};
+use rustc_lint::{EarlyContext, LateContext, LintContext};
 use rustc_middle::ty::TyCtxt;
 use rustc_session::Session;
 use rustc_span::symbol::{Ident, kw};
@@ -51,7 +51,7 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) ->
         return false;
     };
     let end = span.hi() - pos.sf.start_pos;
-    src.get(pos.pos.0 as usize..end.0 as usize).map_or(false, |s| {
+    src.get(pos.pos.0 as usize..end.0 as usize).is_some_and(|s| {
         // Spans can be wrapped in a mixture or parenthesis, whitespace, and trailing commas.
         let start_str = s.trim_start_matches(|c: char| c.is_whitespace() || c == '(');
         let end_str = s.trim_end_matches(|c: char| c.is_whitespace() || c == ')' || c == ',');
@@ -60,13 +60,13 @@ fn span_matches_pat(sess: &Session, span: Span, start_pat: Pat, end_pat: Pat) ->
             Pat::MultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
             Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
             Pat::Sym(sym) => start_str.starts_with(sym.as_str()),
-            Pat::Num => start_str.as_bytes().first().map_or(false, u8::is_ascii_digit),
+            Pat::Num => start_str.as_bytes().first().is_some_and(u8::is_ascii_digit),
         } && match end_pat {
             Pat::Str(text) => end_str.ends_with(text),
-            Pat::MultiStr(texts) => texts.iter().any(|s| start_str.ends_with(s)),
-            Pat::OwnedMultiStr(texts) => texts.iter().any(|s| start_str.starts_with(s)),
+            Pat::MultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)),
+            Pat::OwnedMultiStr(texts) => texts.iter().any(|s| end_str.ends_with(s)),
             Pat::Sym(sym) => end_str.ends_with(sym.as_str()),
-            Pat::Num => end_str.as_bytes().last().map_or(false, u8::is_ascii_hexdigit),
+            Pat::Num => end_str.as_bytes().last().is_some_and(u8::is_ascii_hexdigit),
         })
     })
 }
@@ -333,26 +333,32 @@ fn attr_search_pat(attr: &Attribute) -> (Pat, Pat) {
     match attr.kind {
         AttrKind::Normal(..) => {
             if let Some(ident) = attr.ident() {
-                // TODO: I feel like it's likely we can use `Cow` instead but this will require quite a bit of
-                // refactoring
                 // NOTE: This will likely have false positives, like `allow = 1`
-                (
-                    Pat::OwnedMultiStr(vec![ident.to_string(), "#".to_owned()]),
-                    Pat::Str(""),
-                )
+                let ident_string = ident.to_string();
+                if attr.style == AttrStyle::Outer {
+                    (
+                        Pat::OwnedMultiStr(vec!["#[".to_owned() + &ident_string, ident_string]),
+                        Pat::Str(""),
+                    )
+                } else {
+                    (
+                        Pat::OwnedMultiStr(vec!["#![".to_owned() + &ident_string, ident_string]),
+                        Pat::Str(""),
+                    )
+                }
             } else {
                 (Pat::Str("#"), Pat::Str("]"))
             }
         },
         AttrKind::DocComment(_kind @ CommentKind::Line, ..) => {
-            if matches!(attr.style, AttrStyle::Outer) {
+            if attr.style == AttrStyle::Outer {
                 (Pat::Str("///"), Pat::Str(""))
             } else {
                 (Pat::Str("//!"), Pat::Str(""))
             }
         },
         AttrKind::DocComment(_kind @ CommentKind::Block, ..) => {
-            if matches!(attr.style, AttrStyle::Outer) {
+            if attr.style == AttrStyle::Outer {
                 (Pat::Str("/**"), Pat::Str("*/"))
             } else {
                 (Pat::Str("/*!"), Pat::Str("*/"))
@@ -429,11 +435,12 @@ impl_with_search_pat!((_cx: LateContext<'tcx>, self: ImplItem<'_>) => impl_item_
 impl_with_search_pat!((_cx: LateContext<'tcx>, self: FieldDef<'_>) => field_def_search_pat(self));
 impl_with_search_pat!((_cx: LateContext<'tcx>, self: Variant<'_>) => variant_search_pat(self));
 impl_with_search_pat!((_cx: LateContext<'tcx>, self: Ty<'_>) => ty_search_pat(self));
-impl_with_search_pat!((_cx: LateContext<'tcx>, self: Attribute) => attr_search_pat(self));
 impl_with_search_pat!((_cx: LateContext<'tcx>, self: Ident) => ident_search_pat(*self));
 impl_with_search_pat!((_cx: LateContext<'tcx>, self: Lit) => lit_search_pat(&self.node));
 impl_with_search_pat!((_cx: LateContext<'tcx>, self: Path<'_>) => path_search_pat(self));
 
+impl_with_search_pat!((_cx: EarlyContext<'tcx>, self: Attribute) => attr_search_pat(self));
+
 impl<'cx> WithSearchPat<'cx> for (&FnKind<'cx>, &Body<'cx>, HirId, Span) {
     type Context = LateContext<'cx>;
 
diff --git a/src/tools/clippy/clippy_utils/src/consts.rs b/src/tools/clippy/clippy_utils/src/consts.rs
index 67c31abbdda..52c98646289 100644
--- a/src/tools/clippy/clippy_utils/src/consts.rs
+++ b/src/tools/clippy/clippy_utils/src/consts.rs
@@ -1,3 +1,7 @@
+//! A simple const eval API, for use on arbitrary HIR expressions.
+//!
+//! This cannot use rustc's const eval, aka miri, as arbitrary HIR expressions cannot be lowered to
+//! executable MIR bodies, so we have to do this instead.
 #![allow(clippy::float_cmp)]
 
 use crate::macros::HirNode;
@@ -14,7 +18,7 @@ use rustc_lexer::tokenize;
 use rustc_lint::LateContext;
 use rustc_middle::mir::ConstValue;
 use rustc_middle::mir::interpret::{Scalar, alloc_range};
-use rustc_middle::ty::{self, FloatTy, IntTy, ParamEnv, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy};
+use rustc_middle::ty::{self, FloatTy, IntTy, ScalarInt, Ty, TyCtxt, TypeckResults, UintTy};
 use rustc_middle::{bug, mir, span_bug};
 use rustc_span::def_id::DefId;
 use rustc_span::symbol::Ident;
@@ -379,9 +383,11 @@ impl Ord for FullInt {
 /// The context required to evaluate a constant expression.
 ///
 /// This is currently limited to constant folding and reading the value of named constants.
+///
+/// See the module level documentation for some context.
 pub struct ConstEvalCtxt<'tcx> {
     tcx: TyCtxt<'tcx>,
-    param_env: ParamEnv<'tcx>,
+    typing_env: ty::TypingEnv<'tcx>,
     typeck: &'tcx TypeckResults<'tcx>,
     source: Cell<ConstantSource>,
 }
@@ -392,17 +398,17 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
     pub fn new(cx: &LateContext<'tcx>) -> Self {
         Self {
             tcx: cx.tcx,
-            param_env: cx.param_env,
+            typing_env: cx.typing_env(),
             typeck: cx.typeck_results(),
             source: Cell::new(ConstantSource::Local),
         }
     }
 
     /// Creates an evaluation context.
-    pub fn with_env(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, typeck: &'tcx TypeckResults<'tcx>) -> Self {
+    pub fn with_env(tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, typeck: &'tcx TypeckResults<'tcx>) -> Self {
         Self {
             tcx,
-            param_env,
+            typing_env,
             typeck,
             source: Cell::new(ConstantSource::Local),
         }
@@ -637,7 +643,7 @@ impl<'tcx> ConstEvalCtxt<'tcx> {
                 let args = self.typeck.node_args(id);
                 let result = self
                     .tcx
-                    .const_eval_resolve(self.param_env, mir::UnevaluatedConst::new(def_id, args), qpath.span())
+                    .const_eval_resolve(self.typing_env, mir::UnevaluatedConst::new(def_id, args), qpath.span())
                     .ok()
                     .map(|val| mir::Const::from_value(val, ty))?;
                 f(self, result)
diff --git a/src/tools/clippy/clippy_utils/src/diagnostics.rs b/src/tools/clippy/clippy_utils/src/diagnostics.rs
index 4877fb65d37..ddb7a6635e0 100644
--- a/src/tools/clippy/clippy_utils/src/diagnostics.rs
+++ b/src/tools/clippy/clippy_utils/src/diagnostics.rs
@@ -9,6 +9,8 @@
 //! ~The `INTERNAL_METADATA_COLLECTOR` lint
 
 use rustc_errors::{Applicability, Diag, DiagMessage, MultiSpan, SubdiagMessage};
+#[cfg(debug_assertions)]
+use rustc_errors::{EmissionGuarantee, SubstitutionPart, Suggestions};
 use rustc_hir::HirId;
 use rustc_lint::{LateContext, Lint, LintContext};
 use rustc_span::Span;
@@ -28,6 +30,43 @@ fn docs_link(diag: &mut Diag<'_, ()>, lint: &'static Lint) {
     }
 }
 
+/// Makes sure that a diagnostic is well formed.
+///
+/// rustc debug asserts a few properties about spans,
+/// but the clippy repo uses a distributed rustc build with debug assertions disabled,
+/// so this has historically led to problems during subtree syncs where those debug assertions
+/// only started triggered there.
+///
+/// This function makes sure we also validate them in debug clippy builds.
+#[cfg(debug_assertions)]
+fn validate_diag(diag: &Diag<'_, impl EmissionGuarantee>) {
+    let suggestions = match &diag.suggestions {
+        Suggestions::Enabled(suggs) => &**suggs,
+        Suggestions::Sealed(suggs) => &**suggs,
+        Suggestions::Disabled => return,
+    };
+
+    for substitution in suggestions.iter().flat_map(|s| &s.substitutions) {
+        assert_eq!(
+            substitution
+                .parts
+                .iter()
+                .find(|SubstitutionPart { snippet, span }| snippet.is_empty() && span.is_empty()),
+            None,
+            "span must not be empty and have no suggestion"
+        );
+
+        assert_eq!(
+            substitution
+                .parts
+                .array_windows()
+                .find(|[a, b]| a.span.overlaps(b.span)),
+            None,
+            "suggestion must not have overlapping parts"
+        );
+    }
+}
+
 /// Emit a basic lint message with a `msg` and a `span`.
 ///
 /// This is the most primitive of our lint emission methods and can
@@ -64,6 +103,9 @@ pub fn span_lint<T: LintContext>(cx: &T, lint: &'static Lint, sp: impl Into<Mult
     cx.span_lint(lint, sp, |diag| {
         diag.primary_message(msg);
         docs_link(diag, lint);
+
+        #[cfg(debug_assertions)]
+        validate_diag(diag);
     });
 }
 
@@ -118,6 +160,9 @@ pub fn span_lint_and_help<T: LintContext>(
             diag.help(help.into());
         }
         docs_link(diag, lint);
+
+        #[cfg(debug_assertions)]
+        validate_diag(diag);
     });
 }
 
@@ -175,6 +220,9 @@ pub fn span_lint_and_note<T: LintContext>(
             diag.note(note.into());
         }
         docs_link(diag, lint);
+
+        #[cfg(debug_assertions)]
+        validate_diag(diag);
     });
 }
 
@@ -208,6 +256,9 @@ where
         diag.primary_message(msg);
         f(diag);
         docs_link(diag, lint);
+
+        #[cfg(debug_assertions)]
+        validate_diag(diag);
     });
 }
 
@@ -240,6 +291,9 @@ pub fn span_lint_hir(cx: &LateContext<'_>, lint: &'static Lint, hir_id: HirId, s
     cx.tcx.node_span_lint(lint, hir_id, sp, |diag| {
         diag.primary_message(msg);
         docs_link(diag, lint);
+
+        #[cfg(debug_assertions)]
+        validate_diag(diag);
     });
 }
 
@@ -280,6 +334,9 @@ pub fn span_lint_hir_and_then(
         diag.primary_message(msg);
         f(diag);
         docs_link(diag, lint);
+
+        #[cfg(debug_assertions)]
+        validate_diag(diag);
     });
 }
 
@@ -316,7 +373,7 @@ pub fn span_lint_hir_and_then(
 ///     |
 ///     = note: `-D fold-any` implied by `-D warnings`
 /// ```
-#[expect(clippy::collapsible_span_lint_calls)]
+#[cfg_attr(not(debug_assertions), expect(clippy::collapsible_span_lint_calls))]
 pub fn span_lint_and_sugg<T: LintContext>(
     cx: &T,
     lint: &'static Lint,
@@ -328,5 +385,8 @@ pub fn span_lint_and_sugg<T: LintContext>(
 ) {
     span_lint_and_then(cx, lint, sp, msg.into(), |diag| {
         diag.span_suggestion(sp, help.into(), sugg, applicability);
+
+        #[cfg(debug_assertions)]
+        validate_diag(diag);
     });
 }
diff --git a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
index a2e97919d04..7f0363ac942 100644
--- a/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
+++ b/src/tools/clippy/clippy_utils/src/eager_or_lazy.rs
@@ -105,7 +105,7 @@ fn res_has_significant_drop(res: Res, cx: &LateContext<'_>, e: &Expr<'_>) -> boo
     {
         cx.typeck_results()
             .expr_ty(e)
-            .has_significant_drop(cx.tcx, cx.param_env)
+            .has_significant_drop(cx.tcx, cx.typing_env())
     } else {
         false
     }
diff --git a/src/tools/clippy/clippy_utils/src/higher.rs b/src/tools/clippy/clippy_utils/src/higher.rs
index 3175a9a1dd3..11bbe734844 100644
--- a/src/tools/clippy/clippy_utils/src/higher.rs
+++ b/src/tools/clippy/clippy_utils/src/higher.rs
@@ -103,7 +103,7 @@ impl<'hir> IfLet<'hir> {
     /// Parses an `if let` expression
     pub fn hir(cx: &LateContext<'_>, expr: &Expr<'hir>) -> Option<Self> {
         if let ExprKind::If(
-            Expr {
+            &Expr {
                 kind:
                     ExprKind::Let(&hir::LetExpr {
                         pat: let_pat,
@@ -381,12 +381,12 @@ impl<'hir> WhileLet<'hir> {
     /// Parses a desugared `while let` loop
     pub const fn hir(expr: &Expr<'hir>) -> Option<Self> {
         if let ExprKind::Loop(
-            Block {
+            &Block {
                 expr:
-                    Some(Expr {
+                    Some(&Expr {
                         kind:
                             ExprKind::If(
-                                Expr {
+                                &Expr {
                                     kind:
                                         ExprKind::Let(&hir::LetExpr {
                                             pat: let_pat,
diff --git a/src/tools/clippy/clippy_utils/src/hir_utils.rs b/src/tools/clippy/clippy_utils/src/hir_utils.rs
index 8004bc68b2e..ea866a78d87 100644
--- a/src/tools/clippy/clippy_utils/src/hir_utils.rs
+++ b/src/tools/clippy/clippy_utils/src/hir_utils.rs
@@ -128,7 +128,7 @@ impl<'a, 'tcx> SpanlessEq<'a, 'tcx> {
         self.inter_expr().eq_path_segments(left, right)
     }
 
-    pub fn eq_modifiers(&mut self, left: TraitBoundModifiers, right: TraitBoundModifiers) -> bool {
+    pub fn eq_modifiers(left: TraitBoundModifiers, right: TraitBoundModifiers) -> bool {
         std::mem::discriminant(&left.constness) == std::mem::discriminant(&right.constness)
             && std::mem::discriminant(&left.polarity) == std::mem::discriminant(&right.polarity)
     }
@@ -258,7 +258,7 @@ impl HirEqInterExpr<'_, '_, '_> {
     }
 
     fn should_ignore(&mut self, expr: &Expr<'_>) -> bool {
-        macro_backtrace(expr.span).last().map_or(false, |macro_call| {
+        macro_backtrace(expr.span).last().is_some_and(|macro_call| {
             matches!(
                 &self.inner.cx.tcx.get_diagnostic_name(macro_call.def_id),
                 Some(sym::todo_macro | sym::unimplemented_macro)
@@ -297,8 +297,8 @@ impl HirEqInterExpr<'_, '_, '_> {
         if let Some((typeck_lhs, typeck_rhs)) = self.inner.maybe_typeck_results
             && typeck_lhs.expr_ty(left) == typeck_rhs.expr_ty(right)
             && let (Some(l), Some(r)) = (
-                ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.param_env, typeck_lhs).eval_simple(left),
-                ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.param_env, typeck_rhs).eval_simple(right),
+                ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_lhs).eval_simple(left),
+                ConstEvalCtxt::with_env(self.inner.cx.tcx, self.inner.cx.typing_env(), typeck_rhs).eval_simple(right),
             )
             && l == r
         {
@@ -322,7 +322,7 @@ impl HirEqInterExpr<'_, '_, '_> {
             (&ExprKind::Block(l, _), &ExprKind::Block(r, _)) => self.eq_block(l, r),
             (&ExprKind::Binary(l_op, ll, lr), &ExprKind::Binary(r_op, rl, rr)) => {
                 l_op.node == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
-                    || swap_binop(l_op.node, ll, lr).map_or(false, |(l_op, ll, lr)| {
+                    || swap_binop(l_op.node, ll, lr).is_some_and(|(l_op, ll, lr)| {
                         l_op == r_op.node && self.eq_expr(ll, rl) && self.eq_expr(lr, rr)
                     })
             },
@@ -444,7 +444,7 @@ impl HirEqInterExpr<'_, '_, '_> {
             ) => false,
         };
         (is_eq && (!self.should_ignore(left) || !self.should_ignore(right)))
-            || self.inner.expr_fallback.as_mut().map_or(false, |f| f(left, right))
+            || self.inner.expr_fallback.as_mut().is_some_and(|f| f(left, right))
     }
 
     fn eq_exprs(&mut self, left: &[Expr<'_>], right: &[Expr<'_>]) -> bool {
@@ -724,7 +724,7 @@ fn swap_binop<'a>(
 /// `eq_fn`.
 pub fn both<X>(l: Option<&X>, r: Option<&X>, mut eq_fn: impl FnMut(&X, &X) -> bool) -> bool {
     l.as_ref()
-        .map_or_else(|| r.is_none(), |x| r.as_ref().map_or(false, |y| eq_fn(x, y)))
+        .map_or_else(|| r.is_none(), |x| r.as_ref().is_some_and(|y| eq_fn(x, y)))
 }
 
 /// Checks if two slices are equal as per `eq_fn`.
@@ -813,7 +813,7 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
     #[expect(clippy::too_many_lines)]
     pub fn hash_expr(&mut self, e: &Expr<'_>) {
         let simple_const = self.maybe_typeck_results.and_then(|typeck_results| {
-            ConstEvalCtxt::with_env(self.cx.tcx, self.cx.param_env, typeck_results).eval_simple(e)
+            ConstEvalCtxt::with_env(self.cx.tcx, self.cx.typing_env(), typeck_results).eval_simple(e)
         });
 
         // const hashing may result in the same hash as some unrelated node, so add a sort of
@@ -1201,11 +1201,11 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                 self.hash_ty(ty);
                 self.hash_pat(pat);
             },
-            TyKind::Ptr(ref mut_ty) => {
+            TyKind::Ptr(mut_ty) => {
                 self.hash_ty(mut_ty.ty);
                 mut_ty.mutbl.hash(&mut self.s);
             },
-            TyKind::Ref(lifetime, ref mut_ty) => {
+            TyKind::Ref(lifetime, mut_ty) => {
                 self.hash_lifetime(lifetime);
                 self.hash_ty(mut_ty.ty);
                 mut_ty.mutbl.hash(&mut self.s);
@@ -1230,14 +1230,19 @@ impl<'a, 'tcx> SpanlessHash<'a, 'tcx> {
                     self.hash_ty(ty);
                 }
             },
-            TyKind::Path(ref qpath) => self.hash_qpath(qpath),
+            TyKind::Path(qpath) => self.hash_qpath(qpath),
             TyKind::TraitObject(_, lifetime, _) => {
                 self.hash_lifetime(lifetime);
             },
             TyKind::Typeof(anon_const) => {
                 self.hash_body(anon_const.body);
             },
-            TyKind::Err(_) | TyKind::Infer | TyKind::Never | TyKind::InferDelegation(..) | TyKind::OpaqueDef(_) | TyKind::AnonAdt(_) => {},
+            TyKind::Err(_)
+            | TyKind::Infer
+            | TyKind::Never
+            | TyKind::InferDelegation(..)
+            | TyKind::OpaqueDef(_)
+            | TyKind::AnonAdt(_) => {},
         }
     }
 
diff --git a/src/tools/clippy/clippy_utils/src/lib.rs b/src/tools/clippy/clippy_utils/src/lib.rs
index ad85dfa2d1e..6408cc938cb 100644
--- a/src/tools/clippy/clippy_utils/src/lib.rs
+++ b/src/tools/clippy/clippy_utils/src/lib.rs
@@ -9,6 +9,7 @@
 #![feature(rustc_private)]
 #![feature(assert_matches)]
 #![feature(unwrap_infallible)]
+#![feature(array_windows)]
 #![recursion_limit = "512"]
 #![allow(
     clippy::missing_errors_doc,
@@ -115,8 +116,8 @@ use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow};
 use rustc_middle::ty::fast_reject::SimplifiedType;
 use rustc_middle::ty::layout::IntegerExt;
 use rustc_middle::ty::{
-    self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgsRef, IntTy, ParamEnv,
-    ParamEnvAnd, Ty, TyCtxt, TypeVisitableExt, UintTy, UpvarCapture,
+    self as rustc_ty, Binder, BorrowKind, ClosureKind, EarlyBinder, FloatTy, GenericArgsRef, IntTy,
+    Ty, TyCtxt, TypeVisitableExt, UintTy, UpvarCapture,
 };
 use rustc_span::hygiene::{ExpnKind, MacroKind};
 use rustc_span::source_map::SourceMap;
@@ -341,10 +342,9 @@ pub fn is_ty_alias(qpath: &QPath<'_>) -> bool {
 /// Checks if the method call given in `expr` belongs to the given trait.
 /// This is a deprecated function, consider using [`is_trait_method`].
 pub fn match_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, path: &[&str]) -> bool {
-    cx.typeck_results()
-        .type_dependent_def_id(expr.hir_id)
-        .and_then(|defid| cx.tcx.trait_of_item(defid))
-        .map_or(false, |trt_id| match_def_path(cx, trt_id, path))
+    let def_id = cx.typeck_results().type_dependent_def_id(expr.hir_id).unwrap();
+    let trt_id = cx.tcx.trait_of_item(def_id);
+    trt_id.is_some_and(|trt_id| match_def_path(cx, trt_id, path))
 }
 
 /// Checks if the given method call expression calls an inherent method.
@@ -378,7 +378,7 @@ pub fn is_diag_trait_item(cx: &LateContext<'_>, def_id: DefId, diag_item: Symbol
 pub fn is_trait_method(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -> bool {
     cx.typeck_results()
         .type_dependent_def_id(expr.hir_id)
-        .map_or(false, |did| is_diag_trait_item(cx, did, diag_item))
+        .is_some_and(|did| is_diag_trait_item(cx, did, diag_item))
 }
 
 /// Checks if the `def_id` belongs to a function that is part of a trait impl.
@@ -405,7 +405,7 @@ pub fn is_trait_item(cx: &LateContext<'_>, expr: &Expr<'_>, diag_item: Symbol) -
     if let ExprKind::Path(ref qpath) = expr.kind {
         cx.qpath_res(qpath, expr.hir_id)
             .opt_def_id()
-            .map_or(false, |def_id| is_diag_trait_item(cx, def_id, diag_item))
+            .is_some_and(|def_id| is_diag_trait_item(cx, def_id, diag_item))
     } else {
         false
     }
@@ -465,13 +465,13 @@ pub fn match_qpath(path: &QPath<'_>, segments: &[&str]) -> bool {
 ///
 /// Please use `is_path_diagnostic_item` if the target is a diagnostic item.
 pub fn is_expr_path_def_path(cx: &LateContext<'_>, expr: &Expr<'_>, segments: &[&str]) -> bool {
-    path_def_id(cx, expr).map_or(false, |id| match_def_path(cx, id, segments))
+    path_def_id(cx, expr).is_some_and(|id| match_def_path(cx, id, segments))
 }
 
 /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
 /// it matches the given lang item.
 pub fn is_path_lang_item<'tcx>(cx: &LateContext<'_>, maybe_path: &impl MaybePath<'tcx>, lang_item: LangItem) -> bool {
-    path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.lang_items().get(lang_item) == Some(id))
+    path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.lang_items().get(lang_item) == Some(id))
 }
 
 /// If `maybe_path` is a path node which resolves to an item, resolves it to a `DefId` and checks if
@@ -481,7 +481,7 @@ pub fn is_path_diagnostic_item<'tcx>(
     maybe_path: &impl MaybePath<'tcx>,
     diag_item: Symbol,
 ) -> bool {
-    path_def_id(cx, maybe_path).map_or(false, |id| cx.tcx.is_diagnostic_item(diag_item, id))
+    path_def_id(cx, maybe_path).is_some_and(|id| cx.tcx.is_diagnostic_item(diag_item, id))
 }
 
 /// THIS METHOD IS DEPRECATED. Matches a `Path` against a slice of segment string literals.
@@ -688,11 +688,11 @@ pub fn find_crates(tcx: TyCtxt<'_>, name: Symbol) -> Vec<Res> {
 ///
 /// This function is expensive and should be used sparingly.
 pub fn def_path_res(tcx: TyCtxt<'_>, path: &[&str]) -> Vec<Res> {
-    let (base, path) = match *path {
+    let (base, path) = match path {
         [primitive] => {
             return vec![PrimTy::from_name(Symbol::intern(primitive)).map_or(Res::Err, Res::PrimTy)];
         },
-        [base, ref path @ ..] => (base, path),
+        [base, path @ ..] => (base, path),
         _ => return Vec::new(),
     };
 
@@ -744,7 +744,7 @@ pub fn def_path_res_with_base(tcx: TyCtxt<'_>, mut base: Vec<Res>, mut path: &[&
 }
 
 /// Resolves a def path like `std::vec::Vec` to its [`DefId`]s, see [`def_path_res`].
-pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator<Item = DefId> {
+pub fn def_path_def_ids(tcx: TyCtxt<'_>, path: &[&str]) -> impl Iterator<Item = DefId> + use<> {
     def_path_res(tcx, path).into_iter().filter_map(|res| res.opt_def_id())
 }
 
@@ -934,7 +934,7 @@ pub fn is_default_equivalent(cx: &LateContext<'_>, e: &Expr<'_>) -> bool {
             }
         },
         ExprKind::Call(repl_func, []) => is_default_equivalent_call(cx, repl_func),
-        ExprKind::Call(from_func, [ref arg]) => is_default_equivalent_from(cx, from_func, arg),
+        ExprKind::Call(from_func, [arg]) => is_default_equivalent_from(cx, from_func, arg),
         ExprKind::Path(qpath) => is_res_lang_ctor(cx, cx.qpath_res(qpath, e.hir_id), OptionNone),
         ExprKind::AddrOf(rustc_hir::BorrowKind::Ref, _, expr) => matches!(expr.kind, ExprKind::Array([])),
         _ => false,
@@ -947,7 +947,7 @@ fn is_default_equivalent_from(cx: &LateContext<'_>, from_func: &Expr<'_>, arg: &
     {
         match arg.kind {
             ExprKind::Lit(hir::Lit {
-                node: LitKind::Str(ref sym, _),
+                node: LitKind::Str(sym, _),
                 ..
             }) => return sym.is_empty() && is_path_lang_item(cx, ty, LangItem::String),
             ExprKind::Array([]) => return is_path_diagnostic_item(cx, ty, sym::Vec),
@@ -1209,8 +1209,8 @@ pub fn can_move_expr_to_closure<'tcx>(cx: &LateContext<'tcx>, expr: &'tcx Expr<'
                             let capture = match capture.info.capture_kind {
                                 UpvarCapture::ByValue => CaptureKind::Value,
                                 UpvarCapture::ByRef(kind) => match kind {
-                                    BorrowKind::ImmBorrow => CaptureKind::Ref(Mutability::Not),
-                                    BorrowKind::UniqueImmBorrow | BorrowKind::MutBorrow => {
+                                    BorrowKind::Immutable => CaptureKind::Ref(Mutability::Not),
+                                    BorrowKind::UniqueImmutable | BorrowKind::Mutable => {
                                         CaptureKind::Ref(Mutability::Mut)
                                     },
                                 },
@@ -1314,7 +1314,7 @@ pub fn method_chain_args<'a>(expr: &'a Expr<'_>, methods: &[&str]) -> Option<Vec
 pub fn is_entrypoint_fn(cx: &LateContext<'_>, def_id: DefId) -> bool {
     cx.tcx
         .entry_fn(())
-        .map_or(false, |(entry_fn_def_id, _)| def_id == entry_fn_def_id)
+        .is_some_and(|(entry_fn_def_id, _)| def_id == entry_fn_def_id)
 }
 
 /// Returns `true` if the expression is in the program's `#[panic_handler]`.
@@ -1337,15 +1337,17 @@ pub fn get_item_name(cx: &LateContext<'_>, expr: &Expr<'_>) -> Option<Symbol> {
 pub struct ContainsName<'a, 'tcx> {
     pub cx: &'a LateContext<'tcx>,
     pub name: Symbol,
-    pub result: bool,
 }
 
 impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
+    type Result = ControlFlow<()>;
     type NestedFilter = nested_filter::OnlyBodies;
 
-    fn visit_name(&mut self, name: Symbol) {
+    fn visit_name(&mut self, name: Symbol) -> Self::Result {
         if self.name == name {
-            self.result = true;
+            ControlFlow::Break(())
+        } else {
+            ControlFlow::Continue(())
         }
     }
 
@@ -1356,13 +1358,8 @@ impl<'tcx> Visitor<'tcx> for ContainsName<'_, 'tcx> {
 
 /// Checks if an `Expr` contains a certain name.
 pub fn contains_name<'tcx>(name: Symbol, expr: &'tcx Expr<'_>, cx: &LateContext<'tcx>) -> bool {
-    let mut cn = ContainsName {
-        name,
-        result: false,
-        cx,
-    };
-    cn.visit_expr(expr);
-    cn.result
+    let mut cn = ContainsName { cx, name };
+    cn.visit_expr(expr).is_break()
 }
 
 /// Returns `true` if `expr` contains a return expression
@@ -1634,7 +1631,7 @@ pub fn is_integer_const(cx: &LateContext<'_>, e: &Expr<'_>, value: u128) -> bool
     }
     let enclosing_body = cx.tcx.hir().enclosing_body_owner(e.hir_id);
     if let Some(Constant::Int(v)) =
-        ConstEvalCtxt::with_env(cx.tcx, cx.tcx.param_env(enclosing_body), cx.tcx.typeck(enclosing_body)).eval(e)
+        ConstEvalCtxt::with_env(cx.tcx, cx.typing_env(), cx.tcx.typeck(enclosing_body)).eval(e)
     {
         return value == v;
     }
@@ -1755,8 +1752,8 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
 
     match pat.kind {
         PatKind::Wild | PatKind::Never => false, // If `!` typechecked then the type is empty, so not refutable.
-        PatKind::Binding(_, _, _, pat) => pat.map_or(false, |pat| is_refutable(cx, pat)),
-        PatKind::Box(pat) | PatKind::Deref(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
+        PatKind::Binding(_, _, _, pat) => pat.is_some_and(|pat| is_refutable(cx, pat)),
+        PatKind::Box(pat) | PatKind::Ref(pat, _) => is_refutable(cx, pat),
         PatKind::Path(ref qpath) => is_enum_variant(cx, qpath, pat.hir_id),
         PatKind::Or(pats) => {
             // TODO: should be the honest check, that pats is exhaustive set
@@ -1780,7 +1777,7 @@ pub fn is_refutable(cx: &LateContext<'_>, pat: &Pat<'_>) -> bool {
                 },
             }
         },
-        PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) => true,
+        PatKind::Lit(..) | PatKind::Range(..) | PatKind::Err(_) | PatKind::Deref(_) => true,
     }
 }
 
@@ -2023,7 +2020,7 @@ pub fn match_libc_symbol(cx: &LateContext<'_>, did: DefId, name: &str) -> bool {
     let path = cx.get_def_path(did);
     // libc is meant to be used as a flat list of names, but they're all actually defined in different
     // modules based on the target platform. Ignore everything but crate name and the item name.
-    path.first().map_or(false, |s| s.as_str() == "libc") && path.last().map_or(false, |s| s.as_str() == name)
+    path.first().is_some_and(|s| s.as_str() == "libc") && path.last().is_some_and(|s| s.as_str() == name)
 }
 
 /// Returns the list of condition expressions and the list of blocks in a
@@ -2106,7 +2103,7 @@ pub fn is_must_use_func_call(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
         _ => None,
     };
 
-    did.map_or(false, |did| cx.tcx.has_attr(did, sym::must_use))
+    did.is_some_and(|did| cx.tcx.has_attr(did, sym::must_use))
 }
 
 /// Checks if a function's body represents the identity function. Looks for bodies of the form:
@@ -2213,7 +2210,7 @@ pub fn is_expr_untyped_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>)
 pub fn is_expr_identity_function(cx: &LateContext<'_>, expr: &Expr<'_>) -> bool {
     match expr.kind {
         ExprKind::Closure(&Closure { body, .. }) => is_body_identity_function(cx, cx.tcx.hir().body(body)),
-        _ => path_def_id(cx, expr).map_or(false, |id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
+        _ => path_def_id(cx, expr).is_some_and(|id| cx.tcx.is_diagnostic_item(sym::convert_identity, id)),
     }
 }
 
@@ -2715,9 +2712,17 @@ pub fn walk_to_expr_usage<'tcx, T>(
 pub enum DefinedTy<'tcx> {
     // Used for locals and closures defined within the function.
     Hir(&'tcx hir::Ty<'tcx>),
-    /// Used for function signatures, and constant and static values. This includes the `ParamEnv`
-    /// from the definition site.
-    Mir(ParamEnvAnd<'tcx, Binder<'tcx, Ty<'tcx>>>),
+    /// Used for function signatures, and constant and static values. The type is
+    /// in the context of its definition site. We also track the `def_id` of its
+    /// definition site.
+    /// 
+    /// WARNING: As the `ty` in in the scope of the definition, not of the function
+    /// using it, you must be very careful with how you use it. Using it in the wrong
+    /// scope easily results in ICEs. 
+    Mir {
+        def_site_def_id: Option<DefId>,
+        ty: Binder<'tcx, Ty<'tcx>>,
+    },
 }
 
 /// The context an expressions value is used in.
@@ -2836,10 +2841,10 @@ impl<'tcx> ExprUseNode<'tcx> {
     pub fn defined_ty(&self, cx: &LateContext<'tcx>) -> Option<DefinedTy<'tcx>> {
         match *self {
             Self::LetStmt(LetStmt { ty: Some(ty), .. }) => Some(DefinedTy::Hir(ty)),
-            Self::ConstStatic(id) => Some(DefinedTy::Mir(
-                cx.param_env
-                    .and(Binder::dummy(cx.tcx.type_of(id).instantiate_identity())),
-            )),
+            Self::ConstStatic(id) => Some(DefinedTy::Mir {
+                def_site_def_id: Some(id.def_id.to_def_id()),
+                ty: Binder::dummy(cx.tcx.type_of(id).instantiate_identity()),
+        }),
             Self::Return(id) => {
                 if let Node::Expr(Expr {
                     kind: ExprKind::Closure(c),
@@ -2851,9 +2856,8 @@ impl<'tcx> ExprUseNode<'tcx> {
                         FnRetTy::Return(ty) => Some(DefinedTy::Hir(ty)),
                     }
                 } else {
-                    Some(DefinedTy::Mir(
-                        cx.param_env.and(cx.tcx.fn_sig(id).instantiate_identity().output()),
-                    ))
+                    let ty = cx.tcx.fn_sig(id).instantiate_identity().output();
+                    Some(DefinedTy::Mir { def_site_def_id: Some(id.def_id.to_def_id()), ty })
                 }
             },
             Self::Field(field) => match get_parent_expr_for_hir(cx, field.hir_id) {
@@ -2869,12 +2873,9 @@ impl<'tcx> ExprUseNode<'tcx> {
                             .find(|f| f.name == field.ident.name)
                             .map(|f| (adt, f))
                     })
-                    .map(|(adt, field_def)| {
-                        DefinedTy::Mir(
-                            cx.tcx
-                                .param_env(adt.did())
-                                .and(Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity())),
-                        )
+                    .map(|(adt, field_def)| DefinedTy::Mir {
+                            def_site_def_id: Some(adt.did()),
+                            ty: Binder::dummy(cx.tcx.type_of(field_def.did).instantiate_identity()),
                     }),
                 _ => None,
             },
@@ -2883,17 +2884,19 @@ impl<'tcx> ExprUseNode<'tcx> {
                 let (hir_ty, ty) = sig.input_with_hir(i)?;
                 Some(match hir_ty {
                     Some(hir_ty) => DefinedTy::Hir(hir_ty),
-                    None => DefinedTy::Mir(
-                        sig.predicates_id()
-                            .map_or(ParamEnv::empty(), |id| cx.tcx.param_env(id))
-                            .and(ty),
-                    ),
+                    None => DefinedTy::Mir {
+                        def_site_def_id:  sig.predicates_id(),
+                        ty,
+                    }
                 })
             },
             Self::MethodArg(id, _, i) => {
                 let id = cx.typeck_results().type_dependent_def_id(id)?;
                 let sig = cx.tcx.fn_sig(id).skip_binder();
-                Some(DefinedTy::Mir(cx.tcx.param_env(id).and(sig.input(i))))
+                Some(DefinedTy::Mir {
+                    def_site_def_id: Some(id),
+                    ty: sig.input(i),
+                })
             },
             Self::LetStmt(_) | Self::FieldAccess(..) | Self::Callee | Self::Other | Self::AddrOf(..) => None,
         }
@@ -3340,8 +3343,8 @@ pub fn get_path_from_caller_to_method_type<'tcx>(
     let assoc_item = tcx.associated_item(method);
     let def_id = assoc_item.container_id(tcx);
     match assoc_item.container {
-        rustc_ty::TraitContainer => get_path_to_callee(tcx, from, def_id),
-        rustc_ty::ImplContainer => {
+        rustc_ty::AssocItemContainer::Trait => get_path_to_callee(tcx, from, def_id),
+        rustc_ty::AssocItemContainer::Impl => {
             let ty = tcx.type_of(def_id).instantiate_identity();
             get_path_to_ty(tcx, from, ty, args)
         },
@@ -3459,7 +3462,7 @@ fn maybe_get_relative_path(from: &DefPath, to: &DefPath, max_super: usize) -> St
 pub fn is_parent_stmt(cx: &LateContext<'_>, id: HirId) -> bool {
     matches!(
         cx.tcx.parent_hir_node(id),
-        Node::Stmt(..) | Node::Block(Block { stmts: &[], .. })
+        Node::Stmt(..) | Node::Block(Block { stmts: [], .. })
     )
 }
 
diff --git a/src/tools/clippy/clippy_utils/src/numeric_literal.rs b/src/tools/clippy/clippy_utils/src/numeric_literal.rs
index c5a34160e3d..2c49df9d807 100644
--- a/src/tools/clippy/clippy_utils/src/numeric_literal.rs
+++ b/src/tools/clippy/clippy_utils/src/numeric_literal.rs
@@ -53,7 +53,7 @@ impl<'a> NumericLiteral<'a> {
                 .trim_start()
                 .chars()
                 .next()
-                .map_or(false, |c| c.is_ascii_digit())
+                .is_some_and(|c| c.is_ascii_digit())
         {
             let (unsuffixed, suffix) = split_suffix(src, lit_kind);
             let float = matches!(lit_kind, LitKind::Float(..));
diff --git a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
index 8f9f75d6824..3c9ea4bfaf4 100644
--- a/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
+++ b/src/tools/clippy/clippy_utils/src/qualify_min_const_fn.rs
@@ -142,7 +142,7 @@ fn check_rvalue<'tcx>(
                 // We cannot allow this for now.
                 return Err((span, "unsizing casts are only allowed for references right now".into()));
             };
-            let unsized_ty = tcx.struct_tail_for_codegen(pointee_ty, tcx.param_env(def_id));
+            let unsized_ty = tcx.struct_tail_for_codegen(pointee_ty, ty::TypingEnv::post_analysis(tcx, def_id));
             if let ty::Slice(_) | ty::Str = unsized_ty.kind() {
                 check_operand(tcx, op, span, body, msrv)?;
                 // Casting/coercing things to slices is fine.
@@ -233,6 +233,7 @@ fn check_statement<'tcx>(
         | StatementKind::PlaceMention(..)
         | StatementKind::Coverage(..)
         | StatementKind::ConstEvalCounter
+        | StatementKind::BackwardIncompatibleDropHint { .. }
         | StatementKind::Nop => Ok(()),
     }
 }
@@ -393,34 +394,31 @@ fn is_stable_const_fn(tcx: TyCtxt<'_>, def_id: DefId, msrv: &Msrv) -> bool {
 
                 msrv.meets(const_stab_rust_version)
             } else {
-                // Unstable const fn, check if the feature is enabled. We need both the regular stability
-                // feature and (if set) the const stability feature to const-call this function.
-                let stab = tcx.lookup_stability(def_id);
-                let is_enabled = stab.is_some_and(|s| s.is_stable() || tcx.features().enabled(s.feature))
-                    && const_stab.feature.is_none_or(|f| tcx.features().enabled(f));
-                is_enabled && msrv.current().is_none()
+                // Unstable const fn, check if the feature is enabled.
+                tcx.features().enabled(const_stab.feature) && msrv.current().is_none()
             }
         })
 }
 
 fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>) -> bool {
-    // FIXME(effects, fee1-dead) revert to const destruct once it works again
+    // FIXME(const_trait_impl, fee1-dead) revert to const destruct once it works again
     #[expect(unused)]
     fn is_ty_const_destruct_unused<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>) -> bool {
-        // Avoid selecting for simple cases, such as builtin types.
-        if ty::util::is_trivially_const_drop(ty) {
-            return true;
+        // If this doesn't need drop at all, then don't select `~const Destruct`.
+        if !ty.needs_drop(tcx, body.typing_env(tcx)) {
+            return false;
         }
 
-        // FIXME(effects) constness
+        let (infcx, param_env) =
+            tcx.infer_ctxt().build_with_typing_env(body.typing_env(tcx));
+        // FIXME(const_trait_impl) constness
         let obligation = Obligation::new(
             tcx,
             ObligationCause::dummy_with_span(body.span),
-            ConstCx::new(tcx, body).param_env,
+            param_env,
             TraitRef::new(tcx, tcx.require_lang_item(LangItem::Destruct, Some(body.span)), [ty]),
         );
 
-        let infcx = tcx.infer_ctxt().build(body.typing_mode(tcx));
         let mut selcx = SelectionContext::new(&infcx);
         let Some(impl_src) = selcx.select(&obligation).ok().flatten() else {
             return false;
@@ -438,5 +436,5 @@ fn is_ty_const_destruct<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>, body: &Body<'tcx>
         ocx.select_all_or_error().is_empty()
     }
 
-    !ty.needs_drop(tcx, ConstCx::new(tcx, body).param_env)
+    !ty.needs_drop(tcx, ConstCx::new(tcx, body).typing_env)
 }
diff --git a/src/tools/clippy/clippy_utils/src/sugg.rs b/src/tools/clippy/clippy_utils/src/sugg.rs
index 3255c51d009..25ebe879192 100644
--- a/src/tools/clippy/clippy_utils/src/sugg.rs
+++ b/src/tools/clippy/clippy_utils/src/sugg.rs
@@ -905,7 +905,7 @@ impl<'tcx> DerefDelegate<'_, 'tcx> {
             _ => return false,
         };
 
-        ty.map_or(false, |ty| matches!(ty.kind(), ty::Ref(_, inner, _) if inner.is_ref()))
+        ty.is_some_and(|ty| matches!(ty.kind(), ty::Ref(_, inner, _) if inner.is_ref()))
     }
 }
 
diff --git a/src/tools/clippy/clippy_utils/src/ty.rs b/src/tools/clippy/clippy_utils/src/ty.rs
index 41785e161d0..03e1a814a86 100644
--- a/src/tools/clippy/clippy_utils/src/ty.rs
+++ b/src/tools/clippy/clippy_utils/src/ty.rs
@@ -19,7 +19,7 @@ use rustc_middle::ty::layout::ValidityRequirement;
 use rustc_middle::ty::{
     self, AdtDef, AliasTy, AssocItem, AssocKind, Binder, BoundRegion, FnSig, GenericArg, GenericArgKind,
     GenericArgsRef, GenericParamDefKind, IntTy, ParamEnv, Region, RegionKind, TraitRef, Ty, TyCtxt, TypeSuperVisitable,
-    TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr, TypingMode,
+    TypeVisitable, TypeVisitableExt, TypeVisitor, UintTy, Upcast, VariantDef, VariantDiscr,
 };
 use rustc_span::symbol::Ident;
 use rustc_span::{DUMMY_SP, Span, Symbol, sym};
@@ -38,14 +38,14 @@ pub use type_certainty::expr_type_is_certain;
 
 /// Checks if the given type implements copy.
 pub fn is_copy<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
-    ty.is_copy_modulo_regions(cx.tcx, cx.param_env)
+    ty.is_copy_modulo_regions(cx.tcx, cx.typing_env())
 }
 
 /// This checks whether a given type is known to implement Debug.
 pub fn has_debug_impl<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
     cx.tcx
         .get_diagnostic_item(sym::Debug)
-        .map_or(false, |debug| implements_trait(cx, ty, debug, &[]))
+        .is_some_and(|debug| implements_trait(cx, ty, debug, &[]))
 }
 
 /// Checks whether a type can be partially moved.
@@ -226,7 +226,7 @@ pub fn implements_trait<'tcx>(
     trait_id: DefId,
     args: &[GenericArg<'tcx>],
 ) -> bool {
-    implements_trait_with_env_from_iter(cx.tcx, cx.param_env, ty, trait_id, None, args.iter().map(|&x| Some(x)))
+    implements_trait_with_env_from_iter(cx.tcx, cx.typing_env(), ty, trait_id, None, args.iter().map(|&x| Some(x)))
 }
 
 /// Same as `implements_trait` but allows using a `ParamEnv` different from the lint context.
@@ -235,19 +235,19 @@ pub fn implements_trait<'tcx>(
 /// environment, used for checking const traits.
 pub fn implements_trait_with_env<'tcx>(
     tcx: TyCtxt<'tcx>,
-    param_env: ParamEnv<'tcx>,
+    typing_env: ty::TypingEnv<'tcx>,
     ty: Ty<'tcx>,
     trait_id: DefId,
     callee_id: Option<DefId>,
     args: &[GenericArg<'tcx>],
 ) -> bool {
-    implements_trait_with_env_from_iter(tcx, param_env, ty, trait_id, callee_id, args.iter().map(|&x| Some(x)))
+    implements_trait_with_env_from_iter(tcx, typing_env, ty, trait_id, callee_id, args.iter().map(|&x| Some(x)))
 }
 
 /// Same as `implements_trait_from_env` but takes the arguments as an iterator.
 pub fn implements_trait_with_env_from_iter<'tcx>(
     tcx: TyCtxt<'tcx>,
-    param_env: ParamEnv<'tcx>,
+    typing_env: ty::TypingEnv<'tcx>,
     ty: Ty<'tcx>,
     trait_id: DefId,
     callee_id: Option<DefId>,
@@ -268,17 +268,13 @@ pub fn implements_trait_with_env_from_iter<'tcx>(
         return false;
     }
 
-    let infcx = tcx.infer_ctxt().build(TypingMode::from_param_env(param_env));
+    let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
     let args = args
         .into_iter()
         .map(|arg| arg.into().unwrap_or_else(|| infcx.next_ty_var(DUMMY_SP).into()))
         .collect::<Vec<_>>();
 
-    let trait_ref = TraitRef::new(
-        tcx,
-        trait_id,
-        [GenericArg::from(ty)].into_iter().chain(args),
-    );
+    let trait_ref = TraitRef::new(tcx, trait_id, [GenericArg::from(ty)].into_iter().chain(args));
 
     debug_assert_matches!(
         tcx.def_kind(trait_id),
@@ -471,7 +467,7 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
         if !seen.insert(ty) {
             return false;
         }
-        if !ty.has_significant_drop(cx.tcx, cx.param_env) {
+        if !ty.has_significant_drop(cx.tcx, cx.typing_env()) {
             false
         }
         // Check for std types which implement drop, but only for memory allocation.
@@ -491,7 +487,7 @@ pub fn needs_ordered_drop<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
             .tcx
             .lang_items()
             .drop_trait()
-            .map_or(false, |id| implements_trait(cx, ty, id, &[]))
+            .is_some_and(|id| implements_trait(cx, ty, id, &[]))
         {
             // This type doesn't implement drop, so no side effects here.
             // Check if any component type has any.
@@ -579,8 +575,9 @@ pub fn same_type_and_consts<'tcx>(a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
 
 /// Checks if a given type looks safe to be uninitialized.
 pub fn is_uninit_value_valid_for_ty<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> bool {
+    let typing_env = cx.typing_env().with_post_analysis_normalized(cx.tcx);
     cx.tcx
-        .check_validity_requirement((ValidityRequirement::Uninit, cx.param_env.and(ty)))
+        .check_validity_requirement((ValidityRequirement::Uninit, typing_env.as_query_input(ty)))
         .unwrap_or_else(|_| is_uninit_value_valid_for_ty_fallback(cx, ty))
 }
 
@@ -722,14 +719,14 @@ pub fn ty_sig<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> Option<ExprFnSig<'t
                 {
                     let output = bounds
                         .projection_bounds()
-                        .find(|p| lang_items.fn_once_output().map_or(false, |id| id == p.item_def_id()))
+                        .find(|p| lang_items.fn_once_output().is_some_and(|id| id == p.item_def_id()))
                         .map(|p| p.map_bound(|p| p.term.expect_type()));
                     Some(ExprFnSig::Trait(bound.map_bound(|b| b.args.type_at(0)), output, None))
                 },
                 _ => None,
             }
         },
-        ty::Alias(ty::Projection, proj) => match cx.tcx.try_normalize_erasing_regions(cx.param_env, ty) {
+        ty::Alias(ty::Projection, proj) => match cx.tcx.try_normalize_erasing_regions(cx.typing_env(), ty) {
             Ok(normalized_ty) if normalized_ty != ty => ty_sig(cx, normalized_ty),
             _ => sig_for_projection(cx, proj).or_else(|| sig_from_bounds(cx, ty, cx.param_env.caller_bounds(), None)),
         },
@@ -757,7 +754,7 @@ fn sig_from_bounds<'tcx>(
                     && p.self_ty() == ty =>
             {
                 let i = pred.kind().rebind(p.trait_ref.args.type_at(1));
-                if inputs.map_or(false, |inputs| i != inputs) {
+                if inputs.is_some_and(|inputs| i != inputs) {
                     // Multiple different fn trait impls. Is this even allowed?
                     return None;
                 }
@@ -798,7 +795,7 @@ fn sig_for_projection<'tcx>(cx: &LateContext<'tcx>, ty: AliasTy<'tcx>) -> Option
             {
                 let i = pred.kind().rebind(p.trait_ref.args.type_at(1));
 
-                if inputs.map_or(false, |inputs| inputs != i) {
+                if inputs.is_some_and(|inputs| inputs != i) {
                     // Multiple different fn trait impls. Is this even allowed?
                     return None;
                 }
@@ -975,9 +972,7 @@ pub fn approx_ty_size<'tcx>(cx: &LateContext<'tcx>, ty: Ty<'tcx>) -> u64 {
     match (cx.layout_of(ty).map(|layout| layout.size.bytes()), ty.kind()) {
         (Ok(size), _) => size,
         (Err(_), ty::Tuple(list)) => list.iter().map(|t| approx_ty_size(cx, t)).sum(),
-        (Err(_), ty::Array(t, n)) => {
-            n.try_to_target_usize(cx.tcx).unwrap_or_default() * approx_ty_size(cx, *t)
-        },
+        (Err(_), ty::Array(t, n)) => n.try_to_target_usize(cx.tcx).unwrap_or_default() * approx_ty_size(cx, *t),
         (Err(_), ty::Adt(def, subst)) if def.is_struct() => def
             .variants()
             .iter()
@@ -1117,12 +1112,12 @@ pub fn make_projection<'tcx>(
 /// succeeds as well as everything checked by `make_projection`.
 pub fn make_normalized_projection<'tcx>(
     tcx: TyCtxt<'tcx>,
-    param_env: ParamEnv<'tcx>,
+    typing_env: ty::TypingEnv<'tcx>,
     container_id: DefId,
     assoc_ty: Symbol,
     args: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>,
 ) -> Option<Ty<'tcx>> {
-    fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option<Ty<'tcx>> {
+    fn helper<'tcx>(tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: AliasTy<'tcx>) -> Option<Ty<'tcx>> {
         #[cfg(debug_assertions)]
         if let Some((i, arg)) = ty
             .args
@@ -1138,7 +1133,7 @@ pub fn make_normalized_projection<'tcx>(
             );
             return None;
         }
-        match tcx.try_normalize_erasing_regions(param_env, Ty::new_projection_from_args(tcx, ty.def_id, ty.args)) {
+        match tcx.try_normalize_erasing_regions(typing_env, Ty::new_projection_from_args(tcx, ty.def_id, ty.args)) {
             Ok(ty) => Some(ty),
             Err(e) => {
                 debug_assert!(false, "failed to normalize type `{ty}`: {e:#?}");
@@ -1146,7 +1141,7 @@ pub fn make_normalized_projection<'tcx>(
             },
         }
     }
-    helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, args)?)
+    helper(tcx, typing_env, make_projection(tcx, container_id, assoc_ty, args)?)
 }
 
 /// Helper to check if given type has inner mutability such as [`std::cell::Cell`] or
@@ -1244,12 +1239,12 @@ impl<'tcx> InteriorMut<'tcx> {
 
 pub fn make_normalized_projection_with_regions<'tcx>(
     tcx: TyCtxt<'tcx>,
-    param_env: ParamEnv<'tcx>,
+    typing_env: ty::TypingEnv<'tcx>,
     container_id: DefId,
     assoc_ty: Symbol,
     args: impl IntoIterator<Item = impl Into<GenericArg<'tcx>>>,
 ) -> Option<Ty<'tcx>> {
-    fn helper<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: AliasTy<'tcx>) -> Option<Ty<'tcx>> {
+    fn helper<'tcx>(tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: AliasTy<'tcx>) -> Option<Ty<'tcx>> {
         #[cfg(debug_assertions)]
         if let Some((i, arg)) = ty
             .args
@@ -1266,10 +1261,8 @@ pub fn make_normalized_projection_with_regions<'tcx>(
             return None;
         }
         let cause = ObligationCause::dummy();
-        match tcx
-            .infer_ctxt()
-            .build(TypingMode::from_param_env(param_env))
-            .at(&cause, param_env)
+        let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
+        match infcx.at(&cause, param_env)
             .query_normalize(Ty::new_projection_from_args(tcx, ty.def_id, ty.args))
         {
             Ok(ty) => Some(ty.value),
@@ -1279,20 +1272,18 @@ pub fn make_normalized_projection_with_regions<'tcx>(
             },
         }
     }
-    helper(tcx, param_env, make_projection(tcx, container_id, assoc_ty, args)?)
+    helper(tcx, typing_env, make_projection(tcx, container_id, assoc_ty, args)?)
 }
 
-pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
+pub fn normalize_with_regions<'tcx>(tcx: TyCtxt<'tcx>, typing_env: ty::TypingEnv<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> {
     let cause = ObligationCause::dummy();
-    match tcx.infer_ctxt().build(TypingMode::from_param_env(param_env)).at(&cause, param_env).query_normalize(ty) {
-        Ok(ty) => ty.value,
-        Err(_) => ty,
-    }
+    let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env);
+    infcx.at(&cause, param_env).query_normalize(ty).map_or(ty, |ty| ty.value)
 }
 
 /// Checks if the type is `core::mem::ManuallyDrop<_>`
 pub fn is_manually_drop(ty: Ty<'_>) -> bool {
-    ty.ty_adt_def().map_or(false, AdtDef::is_manually_drop)
+    ty.ty_adt_def().is_some_and(AdtDef::is_manually_drop)
 }
 
 /// Returns the deref chain of a type, starting with the type itself.
@@ -1301,7 +1292,7 @@ pub fn deref_chain<'cx, 'tcx>(cx: &'cx LateContext<'tcx>, ty: Ty<'tcx>) -> impl
         if let Some(deref_did) = cx.tcx.lang_items().deref_trait()
             && implements_trait(cx, ty, deref_did, &[])
         {
-            make_normalized_projection(cx.tcx, cx.param_env, deref_did, sym::Target, [ty])
+            make_normalized_projection(cx.tcx, cx.typing_env(), deref_did, sym::Target, [ty])
         } else {
             None
         }
diff --git a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
index 3021f21df12..d7640ebfb00 100644
--- a/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
+++ b/src/tools/clippy/clippy_utils/src/ty/type_certainty/mod.rs
@@ -302,9 +302,9 @@ fn type_is_inferable_from_arguments(cx: &LateContext<'_>, expr: &Expr<'_>) -> bo
     // Check that all type parameters appear in the functions input types.
     (0..(generics.parent_count + generics.own_params.len()) as u32).all(|index| {
         fn_sig
-                .inputs()
-                .iter()
-                .any(|input_ty| contains_param(*input_ty.skip_binder(), index))
+            .inputs()
+            .iter()
+            .any(|input_ty| contains_param(*input_ty.skip_binder(), index))
     })
 }
 
diff --git a/src/tools/clippy/clippy_utils/src/usage.rs b/src/tools/clippy/clippy_utils/src/usage.rs
index 8af3bdccaa1..c8c25456f69 100644
--- a/src/tools/clippy/clippy_utils/src/usage.rs
+++ b/src/tools/clippy/clippy_utils/src/usage.rs
@@ -67,7 +67,7 @@ impl<'tcx> Delegate<'tcx> for MutVarsDelegate {
     fn consume(&mut self, _: &PlaceWithHirId<'tcx>, _: HirId) {}
 
     fn borrow(&mut self, cmt: &PlaceWithHirId<'tcx>, _: HirId, bk: ty::BorrowKind) {
-        if bk == ty::BorrowKind::MutBorrow {
+        if bk == ty::BorrowKind::Mutable {
             self.update(cmt);
         }
     }
@@ -109,34 +109,28 @@ impl<'tcx> Visitor<'tcx> for ParamBindingIdCollector {
 pub struct BindingUsageFinder<'a, 'tcx> {
     cx: &'a LateContext<'tcx>,
     binding_ids: Vec<HirId>,
-    usage_found: bool,
 }
 impl<'a, 'tcx> BindingUsageFinder<'a, 'tcx> {
     pub fn are_params_used(cx: &'a LateContext<'tcx>, body: &'tcx hir::Body<'tcx>) -> bool {
         let mut finder = BindingUsageFinder {
             cx,
             binding_ids: ParamBindingIdCollector::collect_binding_hir_ids(body),
-            usage_found: false,
         };
-        finder.visit_body(body);
-        finder.usage_found
+        finder.visit_body(body).is_break()
     }
 }
 impl<'tcx> Visitor<'tcx> for BindingUsageFinder<'_, 'tcx> {
+    type Result = ControlFlow<()>;
     type NestedFilter = nested_filter::OnlyBodies;
 
-    fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) {
-        if !self.usage_found {
-            intravisit::walk_expr(self, expr);
-        }
-    }
-
-    fn visit_path(&mut self, path: &hir::Path<'tcx>, _: HirId) {
+    fn visit_path(&mut self, path: &hir::Path<'tcx>, _: HirId) -> Self::Result {
         if let Res::Local(id) = path.res {
             if self.binding_ids.contains(&id) {
-                self.usage_found = true;
+                return ControlFlow::Break(());
             }
         }
+
+        ControlFlow::Continue(())
     }
 
     fn nested_visit_map(&mut self) -> Self::Map {
diff --git a/src/tools/clippy/clippy_utils/src/visitors.rs b/src/tools/clippy/clippy_utils/src/visitors.rs
index 8db6502dbfb..a79be5ca7d4 100644
--- a/src/tools/clippy/clippy_utils/src/visitors.rs
+++ b/src/tools/clippy/clippy_utils/src/visitors.rs
@@ -324,17 +324,15 @@ pub fn is_local_used<'tcx>(cx: &LateContext<'tcx>, visitable: impl Visitable<'tc
 pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
     struct V<'a, 'tcx> {
         cx: &'a LateContext<'tcx>,
-        is_const: bool,
     }
+
     impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
+        type Result = ControlFlow<()>;
         type NestedFilter = intravisit::nested_filter::None;
 
-        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
-            if !self.is_const {
-                return;
-            }
+        fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result {
             match e.kind {
-                ExprKind::ConstBlock(_) => return,
+                ExprKind::ConstBlock(_) => return ControlFlow::Continue(()),
                 ExprKind::Call(
                     &Expr {
                         kind: ExprKind::Path(ref p),
@@ -346,13 +344,13 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) ->
                     .cx
                     .qpath_res(p, hir_id)
                     .opt_def_id()
-                    .map_or(false, |id| self.cx.tcx.is_const_fn(id)) => {},
+                    .is_some_and(|id| self.cx.tcx.is_const_fn(id)) => {},
                 ExprKind::MethodCall(..)
                     if self
                         .cx
                         .typeck_results()
                         .type_dependent_def_id(e.hir_id)
-                        .map_or(false, |id| self.cx.tcx.is_const_fn(id)) => {},
+                        .is_some_and(|id| self.cx.tcx.is_const_fn(id)) => {},
                 ExprKind::Binary(_, lhs, rhs)
                     if self.cx.typeck_results().expr_ty(lhs).peel_refs().is_primitive_ty()
                         && self.cx.typeck_results().expr_ty(rhs).peel_refs().is_primitive_ty() => {},
@@ -394,54 +392,49 @@ pub fn is_const_evaluatable<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) ->
                 | ExprKind::Type(..) => (),
 
                 _ => {
-                    self.is_const = false;
-                    return;
+                    return ControlFlow::Break(());
                 },
             }
-            walk_expr(self, e);
+
+            walk_expr(self, e)
         }
     }
 
-    let mut v = V { cx, is_const: true };
-    v.visit_expr(e);
-    v.is_const
+    let mut v = V { cx };
+    v.visit_expr(e).is_continue()
 }
 
 /// Checks if the given expression performs an unsafe operation outside of an unsafe block.
 pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
     struct V<'a, 'tcx> {
         cx: &'a LateContext<'tcx>,
-        is_unsafe: bool,
     }
     impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
         type NestedFilter = nested_filter::OnlyBodies;
+        type Result = ControlFlow<()>;
+
         fn nested_visit_map(&mut self) -> Self::Map {
             self.cx.tcx.hir()
         }
-        fn visit_expr(&mut self, e: &'tcx Expr<'_>) {
-            if self.is_unsafe {
-                return;
-            }
+        fn visit_expr(&mut self, e: &'tcx Expr<'_>) -> Self::Result {
             match e.kind {
                 ExprKind::Unary(UnOp::Deref, e) if self.cx.typeck_results().expr_ty(e).is_unsafe_ptr() => {
-                    self.is_unsafe = true;
+                    ControlFlow::Break(())
                 },
                 ExprKind::MethodCall(..)
                     if self
                         .cx
                         .typeck_results()
                         .type_dependent_def_id(e.hir_id)
-                        .map_or(false, |id| {
-                            self.cx.tcx.fn_sig(id).skip_binder().safety() == Safety::Unsafe
-                        }) =>
+                        .is_some_and(|id| self.cx.tcx.fn_sig(id).skip_binder().safety() == Safety::Unsafe) =>
                 {
-                    self.is_unsafe = true;
+                    ControlFlow::Break(())
                 },
                 ExprKind::Call(func, _) => match *self.cx.typeck_results().expr_ty(func).peel_refs().kind() {
                     ty::FnDef(id, _) if self.cx.tcx.fn_sig(id).skip_binder().safety() == Safety::Unsafe => {
-                        self.is_unsafe = true;
+                        ControlFlow::Break(())
                     },
-                    ty::FnPtr(_, hdr) if hdr.safety == Safety::Unsafe => self.is_unsafe = true,
+                    ty::FnPtr(_, hdr) if hdr.safety == Safety::Unsafe => ControlFlow::Break(()),
                     _ => walk_expr(self, e),
                 },
                 ExprKind::Path(ref p)
@@ -449,58 +442,56 @@ pub fn is_expr_unsafe<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'_>) -> bool {
                         .cx
                         .qpath_res(p, e.hir_id)
                         .opt_def_id()
-                        .map_or(false, |id| self.cx.tcx.is_mutable_static(id)) =>
+                        .is_some_and(|id| self.cx.tcx.is_mutable_static(id)) =>
                 {
-                    self.is_unsafe = true;
+                    ControlFlow::Break(())
                 },
                 _ => walk_expr(self, e),
             }
         }
-        fn visit_block(&mut self, b: &'tcx Block<'_>) {
-            if !matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
-                walk_block(self, b);
+        fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result {
+            if matches!(b.rules, BlockCheckMode::UnsafeBlock(_)) {
+                ControlFlow::Continue(())
+            } else {
+                walk_block(self, b)
             }
         }
-        fn visit_nested_item(&mut self, id: ItemId) {
-            if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind {
-                self.is_unsafe = i.safety == Safety::Unsafe;
+        fn visit_nested_item(&mut self, id: ItemId) -> Self::Result {
+            if let ItemKind::Impl(i) = &self.cx.tcx.hir().item(id).kind
+                && i.safety == Safety::Unsafe
+            {
+                ControlFlow::Break(())
+            } else {
+                ControlFlow::Continue(())
             }
         }
     }
-    let mut v = V { cx, is_unsafe: false };
-    v.visit_expr(e);
-    v.is_unsafe
+    let mut v = V { cx };
+    v.visit_expr(e).is_break()
 }
 
 /// Checks if the given expression contains an unsafe block
 pub fn contains_unsafe_block<'tcx>(cx: &LateContext<'tcx>, e: &'tcx Expr<'tcx>) -> bool {
     struct V<'cx, 'tcx> {
         cx: &'cx LateContext<'tcx>,
-        found_unsafe: bool,
     }
     impl<'tcx> Visitor<'tcx> for V<'_, 'tcx> {
+        type Result = ControlFlow<()>;
         type NestedFilter = nested_filter::OnlyBodies;
         fn nested_visit_map(&mut self) -> Self::Map {
             self.cx.tcx.hir()
         }
 
-        fn visit_block(&mut self, b: &'tcx Block<'_>) {
-            if self.found_unsafe {
-                return;
-            }
+        fn visit_block(&mut self, b: &'tcx Block<'_>) -> Self::Result {
             if b.rules == BlockCheckMode::UnsafeBlock(UnsafeSource::UserProvided) {
-                self.found_unsafe = true;
-                return;
+                ControlFlow::Break(())
+            } else {
+                walk_block(self, b)
             }
-            walk_block(self, b);
         }
     }
-    let mut v = V {
-        cx,
-        found_unsafe: false,
-    };
-    v.visit_expr(e);
-    v.found_unsafe
+    let mut v = V { cx };
+    v.visit_expr(e).is_break()
 }
 
 /// Runs the given function for each sub-expression producing the final value consumed by the parent
diff --git a/src/tools/clippy/rust-toolchain b/src/tools/clippy/rust-toolchain
index e11ab40b9de..e32e0cb3604 100644
--- a/src/tools/clippy/rust-toolchain
+++ b/src/tools/clippy/rust-toolchain
@@ -1,4 +1,4 @@
 [toolchain]
-channel = "nightly-2024-10-18"
+channel = "nightly-2024-11-14"
 components = ["cargo", "llvm-tools", "rust-src", "rust-std", "rustc", "rustc-dev", "rustfmt"]
 profile = "minimal"
diff --git a/src/tools/clippy/tests/compile-test.rs b/src/tools/clippy/tests/compile-test.rs
index 5774e20e0be..b8e0413e97b 100644
--- a/src/tools/clippy/tests/compile-test.rs
+++ b/src/tools/clippy/tests/compile-test.rs
@@ -396,7 +396,8 @@ struct Renderer<'a> {
 
 impl Renderer<'_> {
     fn markdown(input: &str) -> Safe<String> {
-        let parser = Parser::new_ext(input, Options::all());
+        let input = clippy_config::sanitize_explanation(input);
+        let parser = Parser::new_ext(&input, Options::all());
         let mut html_output = String::new();
         html::push_html(&mut html_output, parser);
         // Oh deer, what a hack :O
diff --git a/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs
index 1fd03cfe36d..d59e9cbbb61 100644
--- a/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs
+++ b/src/tools/clippy/tests/ui-internal/lint_without_lint_pass.rs
@@ -7,7 +7,7 @@ extern crate rustc_middle;
 #[macro_use]
 extern crate rustc_session;
 extern crate rustc_lint;
-use rustc_lint::LintPass;
+use rustc_lint::{LintPass, LintVec};
 
 declare_tool_lint! {
     pub clippy::TEST_LINT,
@@ -35,6 +35,9 @@ impl LintPass for Pass {
     fn name(&self) -> &'static str {
         "TEST_LINT"
     }
+    fn get_lints(&self) -> LintVec {
+        vec![TEST_LINT]
+    }
 }
 
 declare_lint_pass!(Pass2 => [TEST_LINT_REGISTERED]);
diff --git a/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.fixed b/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.fixed
new file mode 100644
index 00000000000..2cbd646a0fd
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.fixed
@@ -0,0 +1,24 @@
+#![feature(rustc_private)]
+#![warn(clippy::slow_symbol_comparisons)]
+
+extern crate rustc_span;
+
+use clippy_utils::sym;
+use rustc_span::Symbol;
+
+fn main() {
+    let symbol = sym!(example);
+    let other_symbol = sym!(other_example);
+
+    // Should lint
+    let slow_comparison = symbol.as_str() == "example";
+    //~^ error: comparing `Symbol` via `Symbol::intern`
+    let slow_comparison_macro = symbol.as_str() == "example";
+    //~^ error: comparing `Symbol` via `Symbol::intern`
+    let slow_comparison_backwards = symbol.as_str() == "example";
+    //~^ error: comparing `Symbol` via `Symbol::intern`
+
+    // Should not lint
+    let faster_comparison = symbol.as_str() == "other_example";
+    let preinterned_comparison = symbol == other_symbol;
+}
diff --git a/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.rs b/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.rs
new file mode 100644
index 00000000000..0cea3c3fcff
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.rs
@@ -0,0 +1,24 @@
+#![feature(rustc_private)]
+#![warn(clippy::slow_symbol_comparisons)]
+
+extern crate rustc_span;
+
+use clippy_utils::sym;
+use rustc_span::Symbol;
+
+fn main() {
+    let symbol = sym!(example);
+    let other_symbol = sym!(other_example);
+
+    // Should lint
+    let slow_comparison = symbol == Symbol::intern("example");
+    //~^ error: comparing `Symbol` via `Symbol::intern`
+    let slow_comparison_macro = symbol == sym!(example);
+    //~^ error: comparing `Symbol` via `Symbol::intern`
+    let slow_comparison_backwards = sym!(example) == symbol;
+    //~^ error: comparing `Symbol` via `Symbol::intern`
+
+    // Should not lint
+    let faster_comparison = symbol.as_str() == "other_example";
+    let preinterned_comparison = symbol == other_symbol;
+}
diff --git a/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.stderr b/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.stderr
new file mode 100644
index 00000000000..72cb20a7fed
--- /dev/null
+++ b/src/tools/clippy/tests/ui-internal/slow_symbol_comparisons.stderr
@@ -0,0 +1,23 @@
+error: comparing `Symbol` via `Symbol::intern`
+  --> tests/ui-internal/slow_symbol_comparisons.rs:14:27
+   |
+LL |     let slow_comparison = symbol == Symbol::intern("example");
+   |                           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use `Symbol::as_str` and check the string instead: `symbol.as_str() == "example"`
+   |
+   = note: `-D clippy::slow-symbol-comparisons` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::slow_symbol_comparisons)]`
+
+error: comparing `Symbol` via `Symbol::intern`
+  --> tests/ui-internal/slow_symbol_comparisons.rs:16:33
+   |
+LL |     let slow_comparison_macro = symbol == sym!(example);
+   |                                 ^^^^^^^^^^^^^^^^^^^^^^^ help: use `Symbol::as_str` and check the string instead: `symbol.as_str() == "example"`
+
+error: comparing `Symbol` via `Symbol::intern`
+  --> tests/ui-internal/slow_symbol_comparisons.rs:18:37
+   |
+LL |     let slow_comparison_backwards = sym!(example) == symbol;
+   |                                     ^^^^^^^^^^^^^^^^^^^^^^^ help: use `Symbol::as_str` and check the string instead: `symbol.as_str() == "example"`
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed
index 0a994842834..d3fab60f9e3 100644
--- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed
+++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.fixed
@@ -1,6 +1,7 @@
 //@aux-build:paths.rs
 #![deny(clippy::internal)]
 #![feature(rustc_private)]
+#![allow(clippy::unnecessary_map_or)]
 
 extern crate clippy_utils;
 extern crate paths;
diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs
index ba68de6c6d0..1b36f6b09e9 100644
--- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs
+++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.rs
@@ -1,6 +1,7 @@
 //@aux-build:paths.rs
 #![deny(clippy::internal)]
 #![feature(rustc_private)]
+#![allow(clippy::unnecessary_map_or)]
 
 extern crate clippy_utils;
 extern crate paths;
diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.stderr
index 79da1731613..79521c5037a 100644
--- a/src/tools/clippy/tests/ui-internal/unnecessary_def_path.stderr
+++ b/src/tools/clippy/tests/ui-internal/unnecessary_def_path.stderr
@@ -1,5 +1,5 @@
 error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:36:13
+  --> tests/ui-internal/unnecessary_def_path.rs:37:13
    |
 LL |     let _ = match_type(cx, ty, &OPTION);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)`
@@ -12,61 +12,61 @@ LL | #![deny(clippy::internal)]
    = note: `#[deny(clippy::unnecessary_def_path)]` implied by `#[deny(clippy::internal)]`
 
 error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:37:13
+  --> tests/ui-internal/unnecessary_def_path.rs:38:13
    |
 LL |     let _ = match_type(cx, ty, RESULT);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
 
 error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:38:13
+  --> tests/ui-internal/unnecessary_def_path.rs:39:13
    |
 LL |     let _ = match_type(cx, ty, &["core", "result", "Result"]);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
 
 error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:42:13
+  --> tests/ui-internal/unnecessary_def_path.rs:43:13
    |
 LL |     let _ = clippy_utils::ty::match_type(cx, ty, rc_path);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Rc)`
 
 error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:44:13
+  --> tests/ui-internal/unnecessary_def_path.rs:45:13
    |
 LL |     let _ = match_type(cx, ty, &paths::OPTION);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Option)`
 
 error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:45:13
+  --> tests/ui-internal/unnecessary_def_path.rs:46:13
    |
 LL |     let _ = match_type(cx, ty, paths::RESULT);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::Result)`
 
 error: use of a def path to a `LangItem`
-  --> tests/ui-internal/unnecessary_def_path.rs:47:13
+  --> tests/ui-internal/unnecessary_def_path.rs:48:13
    |
 LL |     let _ = match_type(cx, ty, &["alloc", "boxed", "Box"]);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_lang_item(cx, ty, LangItem::OwnedBox)`
 
 error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:48:13
+  --> tests/ui-internal/unnecessary_def_path.rs:49:13
    |
 LL |     let _ = match_type(cx, ty, &["core", "mem", "maybe_uninit", "MaybeUninit", "uninit"]);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_type_diagnostic_item(cx, ty, sym::maybe_uninit_uninit)`
 
 error: use of a def path to a `LangItem`
-  --> tests/ui-internal/unnecessary_def_path.rs:50:13
+  --> tests/ui-internal/unnecessary_def_path.rs:51:13
    |
 LL |     let _ = match_def_path(cx, did, &["alloc", "boxed", "Box"]);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OwnedBox) == Some(did)`
 
 error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:51:13
+  --> tests/ui-internal/unnecessary_def_path.rs:52:13
    |
 LL |     let _ = match_def_path(cx, did, &["core", "option", "Option"]);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.is_diagnostic_item(sym::Option, did)`
 
 error: use of a def path to a `LangItem`
-  --> tests/ui-internal/unnecessary_def_path.rs:52:13
+  --> tests/ui-internal/unnecessary_def_path.rs:53:13
    |
 LL |     let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `cx.tcx.lang_items().get(LangItem::OptionSome) == Some(did)`
@@ -74,25 +74,25 @@ LL |     let _ = match_def_path(cx, did, &["core", "option", "Option", "Some"]);
    = help: if this `DefId` came from a constructor expression or pattern then the parent `DefId` should be used instead
 
 error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:54:13
+  --> tests/ui-internal/unnecessary_def_path.rs:55:13
    |
 LL |     let _ = match_trait_method(cx, expr, &["core", "convert", "AsRef"]);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_trait_method(cx, expr, sym::AsRef)`
 
 error: use of a def path to a diagnostic item
-  --> tests/ui-internal/unnecessary_def_path.rs:56:13
+  --> tests/ui-internal/unnecessary_def_path.rs:57:13
    |
 LL |     let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option"]);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_path_diagnostic_item(cx, expr, sym::Option)`
 
 error: use of a def path to a `LangItem`
-  --> tests/ui-internal/unnecessary_def_path.rs:57:13
+  --> tests/ui-internal/unnecessary_def_path.rs:58:13
    |
 LL |     let _ = is_expr_path_def_path(cx, expr, &["core", "iter", "traits", "Iterator", "next"]);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `path_res(cx, expr).opt_def_id().map_or(false, |id| cx.tcx.lang_items().get(LangItem::IteratorNext) == Some(id))`
 
 error: use of a def path to a `LangItem`
-  --> tests/ui-internal/unnecessary_def_path.rs:58:13
+  --> tests/ui-internal/unnecessary_def_path.rs:59:13
    |
 LL |     let _ = is_expr_path_def_path(cx, expr, &["core", "option", "Option", "Some"]);
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `is_res_lang_ctor(cx, path_res(cx, expr), LangItem::OptionSome)`
diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed
index eb79fdbc4b4..8e7f020c1f6 100644
--- a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed
+++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.fixed
@@ -1,6 +1,7 @@
 #![feature(rustc_private)]
 #![deny(clippy::internal)]
 #![allow(
+    clippy::slow_symbol_comparisons,
     clippy::borrow_deref_ref,
     clippy::unnecessary_operation,
     unused_must_use,
diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs
index bbea13af92a..9aeeb9aaf3a 100644
--- a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs
+++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.rs
@@ -1,6 +1,7 @@
 #![feature(rustc_private)]
 #![deny(clippy::internal)]
 #![allow(
+    clippy::slow_symbol_comparisons,
     clippy::borrow_deref_ref,
     clippy::unnecessary_operation,
     unused_must_use,
diff --git a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr
index 551167a9ff5..668c11722f9 100644
--- a/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr
+++ b/src/tools/clippy/tests/ui-internal/unnecessary_symbol_str.stderr
@@ -1,5 +1,5 @@
 error: unnecessary `Symbol` to string conversion
-  --> tests/ui-internal/unnecessary_symbol_str.rs:15:5
+  --> tests/ui-internal/unnecessary_symbol_str.rs:16:5
    |
 LL |     Symbol::intern("foo").as_str() == "clippy";
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::sym::clippy`
@@ -12,25 +12,25 @@ LL | #![deny(clippy::internal)]
    = note: `#[deny(clippy::unnecessary_symbol_str)]` implied by `#[deny(clippy::internal)]`
 
 error: unnecessary `Symbol` to string conversion
-  --> tests/ui-internal/unnecessary_symbol_str.rs:16:5
+  --> tests/ui-internal/unnecessary_symbol_str.rs:17:5
    |
 LL |     Symbol::intern("foo").to_string() == "self";
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") == rustc_span::symbol::kw::SelfLower`
 
 error: unnecessary `Symbol` to string conversion
-  --> tests/ui-internal/unnecessary_symbol_str.rs:17:5
+  --> tests/ui-internal/unnecessary_symbol_str.rs:18:5
    |
 LL |     Symbol::intern("foo").to_ident_string() != "Self";
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Symbol::intern("foo") != rustc_span::symbol::kw::SelfUpper`
 
 error: unnecessary `Symbol` to string conversion
-  --> tests/ui-internal/unnecessary_symbol_str.rs:18:5
+  --> tests/ui-internal/unnecessary_symbol_str.rs:19:5
    |
 LL |     &*Ident::empty().as_str() == "clippy";
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `Ident::empty().name == rustc_span::sym::clippy`
 
 error: unnecessary `Symbol` to string conversion
-  --> tests/ui-internal/unnecessary_symbol_str.rs:19:5
+  --> tests/ui-internal/unnecessary_symbol_str.rs:20:5
    |
 LL |     "clippy" == Ident::empty().to_string();
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `rustc_span::sym::clippy == Ident::empty().name`
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1/clippy.toml
new file mode 100644
index 00000000000..fbdb0c9c37d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1/clippy.toml
@@ -0,0 +1 @@
+trait-assoc-item-kinds-order = ["fn", "type", "const", "type"]
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2/clippy.toml
new file mode 100644
index 00000000000..720655e5cce
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2/clippy.toml
@@ -0,0 +1 @@
+trait-assoc-item-kinds-order = ["const", "type"]
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3/clippy.toml
new file mode 100644
index 00000000000..0dd5407be10
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3/clippy.toml
@@ -0,0 +1 @@
+source-item-ordering = ["enum", "impl", "module", "struct", "trait", "struct"]
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/default/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/default/clippy.toml
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/default/clippy.toml
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml
new file mode 100644
index 00000000000..ddca5cfa577
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/default_exp/clippy.toml
@@ -0,0 +1,12 @@
+trait-assoc-item-kinds-order = ["const", "type", "fn"]
+source-item-ordering = ["enum", "impl", "module", "struct", "trait"]
+module-item-order-groupings = [
+    ["modules", ["extern_crate", "mod", "foreign_mod"]],
+    ["use", ["use"]],
+    ["macros", ["macro"]],
+    ["global_asm", ["global_asm"]],
+    ["UPPER_SNAKE_CASE", ["static", "const"]],
+    ["PascalCase", ["ty_alias", "enum", "struct", "union", "trait", "trait_alias", "impl"]],
+    ["lower_snake_case", ["fn"]]
+]
+
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_enum/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_enum/clippy.toml
new file mode 100644
index 00000000000..2144bdc9a0c
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_enum/clippy.toml
@@ -0,0 +1 @@
+source-item-ordering = ["enum"]
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_impl/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_impl/clippy.toml
new file mode 100644
index 00000000000..54b6727fabf
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_impl/clippy.toml
@@ -0,0 +1 @@
+source-item-ordering = ["impl"]
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_trait/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_trait/clippy.toml
new file mode 100644
index 00000000000..b551611c35e
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/only_trait/clippy.toml
@@ -0,0 +1 @@
+source-item-ordering = ["trait"]
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_1.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_1.stderr
new file mode 100644
index 00000000000..e441c7c1241
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_1.stderr
@@ -0,0 +1,8 @@
+error: error reading Clippy's configuration file: Some trait associated item kinds were configured more than once, or were missing, in the source ordering configuration. The trait associated item kinds are: [Const, Fn, Type]
+  --> $DIR/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1/clippy.toml:1:32
+   |
+LL | trait-assoc-item-kinds-order = ["fn", "type", "const", "type"]
+   |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_2.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_2.stderr
new file mode 100644
index 00000000000..183f0b03319
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_2.stderr
@@ -0,0 +1,8 @@
+error: error reading Clippy's configuration file: Some trait associated item kinds were configured more than once, or were missing, in the source ordering configuration. The trait associated item kinds are: [Const, Fn, Type]
+  --> $DIR/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2/clippy.toml:1:32
+   |
+LL | trait-assoc-item-kinds-order = ["const", "type"]
+   |                                ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_3.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_3.stderr
new file mode 100644
index 00000000000..abf58dbd110
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.bad_conf_3.stderr
@@ -0,0 +1,8 @@
+error: error reading Clippy's configuration file: The category "Struct" was enabled more than once in the source ordering configuration.
+  --> $DIR/tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3/clippy.toml:1:24
+   |
+LL | source-item-ordering = ["enum", "impl", "module", "struct", "trait", "struct"]
+   |                        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs
new file mode 100644
index 00000000000..e9bcc30c15e
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good.rs
@@ -0,0 +1,186 @@
+//@aux-build:../../ui/auxiliary/proc_macros.rs
+//@revisions: default default_exp bad_conf_1 bad_conf_2 bad_conf_3
+//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default
+//@[default_exp] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default_exp
+//@[bad_conf_1] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_1
+//@[bad_conf_2] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_2
+//@[bad_conf_3] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/bad_conf_3
+
+#![allow(dead_code)]
+#![warn(clippy::arbitrary_source_item_ordering)]
+
+/// This module gets linted before clippy gives up.
+mod i_am_just_right {
+    const AFTER: i8 = 0;
+
+    const BEFORE: i8 = 0;
+}
+
+/// Since the upper module passes linting, the lint now recurses into this module.
+mod this_is_in_the_wrong_position {
+    const A: i8 = 1;
+    const C: i8 = 0;
+}
+
+// Use statements should not be linted internally - this is normally auto-sorted using rustfmt.
+use std::rc::{Rc, Weak};
+use std::sync::{Arc, Barrier, RwLock};
+
+const SNAKE_CASE: &str = "zzzzzzzz";
+
+const ZIS_SHOULD_BE_EVEN_EARLIER: () = ();
+
+const ZIS_SHOULD_BE_REALLY_EARLY: () = ();
+
+trait BasicEmptyTrait {}
+
+trait CloneSelf {
+    fn clone_self(&self) -> Self;
+}
+
+enum EnumOrdered {
+    A,
+    B,
+    C,
+}
+
+enum EnumUnordered {
+    A,
+    B,
+    C,
+}
+
+#[allow(clippy::arbitrary_source_item_ordering)]
+enum EnumUnorderedAllowed {
+    A,
+    B,
+    C,
+}
+
+struct StructOrdered {
+    a: bool,
+    b: bool,
+    c: bool,
+}
+
+impl BasicEmptyTrait for StructOrdered {}
+
+impl CloneSelf for StructOrdered {
+    fn clone_self(&self) -> Self {
+        Self {
+            a: true,
+            b: true,
+            c: true,
+        }
+    }
+}
+
+impl Default for StructOrdered {
+    fn default() -> Self {
+        Self {
+            a: true,
+            b: true,
+            c: true,
+        }
+    }
+}
+
+impl std::clone::Clone for StructOrdered {
+    fn clone(&self) -> Self {
+        Self {
+            a: true,
+            b: true,
+            c: true,
+        }
+    }
+}
+
+#[derive(Clone, Default)]
+struct StructUnordered {
+    a: bool,
+    b: bool,
+    c: bool,
+    d: bool,
+}
+
+impl TraitUnordered for StructUnordered {
+    const A: bool = false;
+    const B: bool = false;
+    const C: bool = false;
+
+    type SomeType = ();
+
+    fn a() {}
+    fn b() {}
+    fn c() {}
+}
+
+impl TraitUnorderedItemKinds for StructUnordered {
+    const A: bool = false;
+    const B: bool = false;
+    const C: bool = false;
+
+    type SomeType = ();
+
+    fn a() {}
+    fn b() {}
+    fn c() {}
+}
+
+struct StructUnorderedGeneric<T> {
+    _1: std::marker::PhantomData<T>,
+    a: bool,
+    b: bool,
+    c: bool,
+    d: bool,
+}
+
+trait TraitOrdered {
+    const A: bool;
+    const B: bool;
+    const C: bool;
+
+    type SomeType;
+
+    fn a();
+    fn b();
+    fn c();
+}
+
+trait TraitUnordered {
+    const A: bool;
+    const B: bool;
+    const C: bool;
+
+    type SomeType;
+
+    fn a();
+    fn b();
+    fn c();
+}
+
+trait TraitUnorderedItemKinds {
+    const A: bool;
+    const B: bool;
+    const C: bool;
+
+    type SomeType;
+
+    fn a();
+    fn b();
+    fn c();
+}
+
+#[derive(std::clone::Clone, Default)]
+struct ZisShouldBeBeforeZeMainFn;
+
+fn main() {
+    // test code goes here
+}
+
+#[cfg(test)]
+mod test {
+    const B: i8 = 1;
+
+    const A: i8 = 0;
+}
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good_var_1.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good_var_1.rs
new file mode 100644
index 00000000000..0fccbd4790b
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_good_var_1.rs
@@ -0,0 +1,174 @@
+//@aux-build:../../ui/auxiliary/proc_macros.rs
+//@revisions: var_1
+//@[var_1] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/var_1
+
+#![allow(dead_code)]
+#![warn(clippy::arbitrary_source_item_ordering)]
+
+/// This module gets linted before clippy gives up.
+mod i_am_just_right {
+    const AFTER: i8 = 0;
+
+    const BEFORE: i8 = 0;
+}
+
+/// Since the upper module passes linting, the lint now recurses into this module.
+mod this_is_in_the_wrong_position {
+    const A: i8 = 1;
+    const C: i8 = 0;
+}
+
+// Use statements should not be linted internally - this is normally auto-sorted using rustfmt.
+use std::rc::{Rc, Weak};
+use std::sync::{Arc, Barrier, RwLock};
+
+const SNAKE_CASE: &str = "zzzzzzzz";
+
+const ZIS_SHOULD_BE_EVEN_EARLIER: () = ();
+
+const ZIS_SHOULD_BE_REALLY_EARLY: () = ();
+
+trait BasicEmptyTrait {}
+
+trait CloneSelf {
+    fn clone_self(&self) -> Self;
+}
+
+enum EnumOrdered {
+    A,
+    B,
+    C,
+}
+
+enum EnumUnordered {
+    A,
+    B,
+    C,
+}
+
+#[allow(clippy::arbitrary_source_item_ordering)]
+enum EnumUnorderedAllowed {
+    A,
+    B,
+    C,
+}
+
+struct StructOrdered {
+    a: bool,
+    b: bool,
+    c: bool,
+}
+
+impl BasicEmptyTrait for StructOrdered {}
+
+impl CloneSelf for StructOrdered {
+    fn clone_self(&self) -> Self {
+        Self {
+            a: true,
+            b: true,
+            c: true,
+        }
+    }
+}
+
+impl Default for StructOrdered {
+    fn default() -> Self {
+        Self {
+            a: true,
+            b: true,
+            c: true,
+        }
+    }
+}
+
+impl std::clone::Clone for StructOrdered {
+    fn clone(&self) -> Self {
+        Self {
+            a: true,
+            b: true,
+            c: true,
+        }
+    }
+}
+
+#[derive(Clone, Default)]
+struct StructUnordered {
+    a: bool,
+    b: bool,
+    c: bool,
+    d: bool,
+}
+
+impl TraitUnordered for StructUnordered {
+    fn a() {}
+    fn b() {}
+    fn c() {}
+
+    type SomeType = ();
+
+    const A: bool = false;
+    const B: bool = false;
+    const C: bool = false;
+}
+
+impl TraitUnorderedItemKinds for StructUnordered {
+    fn a() {}
+
+    type SomeType = ();
+
+    const A: bool = false;
+}
+
+struct StructUnorderedGeneric<T> {
+    _1: std::marker::PhantomData<T>,
+    a: bool,
+    b: bool,
+    c: bool,
+    d: bool,
+}
+
+trait TraitOrdered {
+    fn a();
+    fn b();
+    fn c();
+
+    type SomeType;
+
+    const A: bool;
+    const B: bool;
+    const C: bool;
+}
+
+trait TraitUnordered {
+    fn a();
+    fn b();
+    fn c();
+
+    type SomeType;
+
+    const A: bool;
+    const B: bool;
+    const C: bool;
+}
+
+trait TraitUnorderedItemKinds {
+    fn a();
+
+    type SomeType;
+
+    const A: bool;
+}
+
+#[derive(std::clone::Clone, Default)]
+struct ZisShouldBeBeforeZeMainFn;
+
+fn main() {
+    // test code goes here
+}
+
+#[cfg(test)]
+mod test {
+    const B: i8 = 1;
+
+    const A: i8 = 0;
+}
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr
new file mode 100644
index 00000000000..062bf25ea62
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.default.stderr
@@ -0,0 +1,226 @@
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:21:14
+   |
+LL | use std::rc::Weak;
+   |              ^^^^
+   |
+note: should be placed before `SNAKE_CASE`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:19:7
+   |
+LL | const SNAKE_CASE: &str = "zzzzzzzz";
+   |       ^^^^^^^^^^
+   = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]`
+
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:64:1
+   |
+LL | / impl CloneSelf for StructOrdered {
+LL | |     fn clone_self(&self) -> Self {
+LL | |         Self {
+LL | |             a: true,
+...  |
+LL | |     }
+LL | | }
+   | |_^
+   |
+note: should be placed before the following item
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:54:1
+   |
+LL | / impl Default for StructOrdered {
+LL | |     fn default() -> Self {
+LL | |         Self {
+LL | |             a: true,
+...  |
+LL | |     }
+LL | | }
+   | |_^
+
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:136:7
+   |
+LL | const ZIS_SHOULD_BE_REALLY_EARLY: () = ();
+   |       ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: should be placed before `TraitUnorderedItemKinds`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:124:7
+   |
+LL | trait TraitUnorderedItemKinds {
+   |       ^^^^^^^^^^^^^^^^^^^^^^^
+
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:151:1
+   |
+LL | impl BasicEmptyTrait for StructOrdered {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: should be placed before the following item
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:138:1
+   |
+LL | / impl TraitUnordered for StructUnordered {
+LL | |     const A: bool = false;
+LL | |     const C: bool = false;
+LL | |     const B: bool = false;
+...  |
+LL | |     fn b() {}
+LL | | }
+   | |_^
+
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:170:5
+   |
+LL | mod this_is_in_the_wrong_position {
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: should be placed before `main`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:165:4
+   |
+LL | fn main() {
+   |    ^^^^
+
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:178:7
+   |
+LL | const ZIS_SHOULD_BE_EVEN_EARLIER: () = ();
+   |       ^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: should be placed before `ZisShouldBeBeforeZeMainFn`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:176:8
+   |
+LL | struct ZisShouldBeBeforeZeMainFn;
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:12:11
+   |
+LL |     const AFTER: i8 = 0;
+   |           ^^^^^
+   |
+note: should be placed before `BEFORE`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:10:11
+   |
+LL |     const BEFORE: i8 = 0;
+   |           ^^^^^^
+
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:38:5
+   |
+LL |     B,
+   |     ^
+   |
+note: should be placed before `C`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:37:5
+   |
+LL |     C,
+   |     ^
+
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:88:5
+   |
+LL |     b: bool,
+   |     ^
+   |
+note: should be placed before `c`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:87:5
+   |
+LL |     c: bool,
+   |     ^
+
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:96:5
+   |
+LL |     b: bool,
+   |     ^
+   |
+note: should be placed before `c`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:95:5
+   |
+LL |     c: bool,
+   |     ^
+
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:115:11
+   |
+LL |     const B: bool;
+   |           ^
+   |
+note: should be placed before `C`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:114:11
+   |
+LL |     const C: bool;
+   |           ^
+
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:121:8
+   |
+LL |     fn b();
+   |        ^
+   |
+note: should be placed before `c`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:120:8
+   |
+LL |     fn c();
+   |        ^
+
+error: incorrect ordering of trait items (defined order: [Const, Type, Fn])
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:127:5
+   |
+LL |     const A: bool;
+   |     ^^^^^^^^^^^^^^
+   |
+note: should be placed before `SomeType`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:125:5
+   |
+LL |     type SomeType;
+   |     ^^^^^^^^^^^^^^
+
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:141:11
+   |
+LL |     const B: bool = false;
+   |           ^
+   |
+note: should be placed before `C`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:140:11
+   |
+LL |     const C: bool = false;
+   |           ^
+
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:147:8
+   |
+LL |     fn b() {}
+   |        ^
+   |
+note: should be placed before `c`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:146:8
+   |
+LL |     fn c() {}
+   |        ^
+
+error: incorrect ordering of impl items (defined order: [Const, Type, Fn])
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:156:5
+   |
+LL |     const A: bool = false;
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+   |
+note: should be placed before `SomeType`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:154:5
+   |
+LL |     type SomeType = ();
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:172:11
+   |
+LL |     const A: i8 = 1;
+   |           ^
+   |
+note: should be placed before `C`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs:171:11
+   |
+LL |     const C: i8 = 0;
+   |           ^
+
+error: aborting due to 17 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs
new file mode 100644
index 00000000000..2765bf935b7
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed.rs
@@ -0,0 +1,185 @@
+//@aux-build:../../ui/auxiliary/proc_macros.rs
+//@revisions: default
+//@[default] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/default
+
+#![allow(dead_code)]
+#![warn(clippy::arbitrary_source_item_ordering)]
+
+/// This module gets linted before clippy gives up.
+mod i_am_just_right {
+    const BEFORE: i8 = 0;
+
+    const AFTER: i8 = 0;
+}
+
+// Use statements should not be linted internally - this is normally auto-sorted using rustfmt.
+use std::rc::Rc;
+use std::sync::{Arc, Barrier, RwLock};
+
+const SNAKE_CASE: &str = "zzzzzzzz";
+
+use std::rc::Weak;
+
+trait BasicEmptyTrait {}
+
+trait CloneSelf {
+    fn clone_self(&self) -> Self;
+}
+
+enum EnumOrdered {
+    A,
+    B,
+    C,
+}
+
+enum EnumUnordered {
+    A,
+    C,
+    B,
+}
+
+#[allow(clippy::arbitrary_source_item_ordering)]
+enum EnumUnorderedAllowed {
+    A,
+    C,
+    B,
+}
+
+struct StructOrdered {
+    a: bool,
+    b: bool,
+    c: bool,
+}
+
+impl Default for StructOrdered {
+    fn default() -> Self {
+        Self {
+            a: true,
+            b: true,
+            c: true,
+        }
+    }
+}
+
+impl CloneSelf for StructOrdered {
+    fn clone_self(&self) -> Self {
+        Self {
+            a: true,
+            b: true,
+            c: true,
+        }
+    }
+}
+
+impl std::clone::Clone for StructOrdered {
+    fn clone(&self) -> Self {
+        Self {
+            a: true,
+            b: true,
+            c: true,
+        }
+    }
+}
+
+#[derive(Default, Clone)]
+struct StructUnordered {
+    a: bool,
+    c: bool,
+    b: bool,
+    d: bool,
+}
+
+struct StructUnorderedGeneric<T> {
+    _1: std::marker::PhantomData<T>,
+    a: bool,
+    c: bool,
+    b: bool,
+    d: bool,
+}
+
+trait TraitOrdered {
+    const A: bool;
+    const B: bool;
+    const C: bool;
+
+    type SomeType;
+
+    fn a();
+    fn b();
+    fn c();
+}
+
+trait TraitUnordered {
+    const A: bool;
+    const C: bool;
+    const B: bool;
+
+    type SomeType;
+
+    fn a();
+    fn c();
+    fn b();
+}
+
+trait TraitUnorderedItemKinds {
+    type SomeType;
+
+    const A: bool;
+    const B: bool;
+    const C: bool;
+
+    fn a();
+    fn b();
+    fn c();
+}
+
+const ZIS_SHOULD_BE_REALLY_EARLY: () = ();
+
+impl TraitUnordered for StructUnordered {
+    const A: bool = false;
+    const C: bool = false;
+    const B: bool = false;
+
+    type SomeType = ();
+
+    fn a() {}
+    fn c() {}
+    fn b() {}
+}
+
+// Trait impls should be located just after the type they implement it for.
+impl BasicEmptyTrait for StructOrdered {}
+
+impl TraitUnorderedItemKinds for StructUnordered {
+    type SomeType = ();
+
+    const A: bool = false;
+    const B: bool = false;
+    const C: bool = false;
+
+    fn a() {}
+    fn b() {}
+    fn c() {}
+}
+
+fn main() {
+    // test code goes here
+}
+
+/// Note that the linting pass is stopped before recursing into this module.
+mod this_is_in_the_wrong_position {
+    const C: i8 = 0;
+    const A: i8 = 1;
+}
+
+#[derive(Default, std::clone::Clone)]
+struct ZisShouldBeBeforeZeMainFn;
+
+const ZIS_SHOULD_BE_EVEN_EARLIER: () = ();
+
+#[cfg(test)]
+mod test {
+    const B: i8 = 1;
+
+    const A: i8 = 0;
+}
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs
new file mode 100644
index 00000000000..44902336573
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs
@@ -0,0 +1,174 @@
+//@aux-build:../../ui/auxiliary/proc_macros.rs
+//@revisions: var_1
+//@[var_1] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/var_1
+
+#![allow(dead_code)]
+#![warn(clippy::arbitrary_source_item_ordering)]
+
+/// This module gets linted before clippy gives up.
+mod i_am_just_right {
+    const AFTER: i8 = 0;
+
+    const BEFORE: i8 = 0;
+}
+
+/// Since the upper module passes linting, the lint now recurses into this module.
+mod this_is_in_the_wrong_position {
+    const A: i8 = 1;
+    const C: i8 = 0;
+}
+
+// Use statements should not be linted internally - this is normally auto-sorted using rustfmt.
+use std::rc::{Rc, Weak};
+use std::sync::{Arc, Barrier, RwLock};
+
+const SNAKE_CASE: &str = "zzzzzzzz";
+
+const ZIS_SHOULD_BE_EVEN_EARLIER: () = ();
+
+const ZIS_SHOULD_BE_REALLY_EARLY: () = ();
+
+trait BasicEmptyTrait {}
+
+trait CloneSelf {
+    fn clone_self(&self) -> Self;
+}
+
+enum EnumOrdered {
+    A,
+    B,
+    C,
+}
+
+enum EnumUnordered {
+    A,
+    B,
+    C,
+}
+
+#[allow(clippy::arbitrary_source_item_ordering)]
+enum EnumUnorderedAllowed {
+    A,
+    B,
+    C,
+}
+
+struct StructOrdered {
+    a: bool,
+    b: bool,
+    c: bool,
+}
+
+impl BasicEmptyTrait for StructOrdered {}
+
+impl CloneSelf for StructOrdered {
+    fn clone_self(&self) -> Self {
+        Self {
+            a: true,
+            b: true,
+            c: true,
+        }
+    }
+}
+
+impl Default for StructOrdered {
+    fn default() -> Self {
+        Self {
+            a: true,
+            b: true,
+            c: true,
+        }
+    }
+}
+
+impl std::clone::Clone for StructOrdered {
+    fn clone(&self) -> Self {
+        Self {
+            a: true,
+            b: true,
+            c: true,
+        }
+    }
+}
+
+#[derive(Clone, Default)]
+struct StructUnordered {
+    a: bool,
+    b: bool,
+    c: bool,
+    d: bool,
+}
+
+impl TraitUnordered for StructUnordered {
+    fn a() {}
+    fn c() {}
+    fn b() {}
+
+    type SomeType = ();
+
+    const A: bool = false;
+    const C: bool = false;
+    const B: bool = false;
+}
+
+impl TraitUnorderedItemKinds for StructUnordered {
+    const A: bool = false;
+
+    type SomeType = ();
+
+    fn a() {}
+}
+
+struct StructUnorderedGeneric<T> {
+    _1: std::marker::PhantomData<T>,
+    a: bool,
+    b: bool,
+    c: bool,
+    d: bool,
+}
+
+trait TraitOrdered {
+    fn a();
+    fn b();
+    fn c();
+
+    type SomeType;
+
+    const A: bool;
+    const B: bool;
+    const C: bool;
+}
+
+trait TraitUnordered {
+    fn a();
+    fn c();
+    fn b();
+
+    type SomeType;
+
+    const A: bool;
+    const C: bool;
+    const B: bool;
+}
+
+trait TraitUnorderedItemKinds {
+    const A: bool;
+
+    type SomeType;
+
+    fn a();
+}
+
+#[derive(std::clone::Clone, Default)]
+struct ZisShouldBeBeforeZeMainFn;
+
+fn main() {
+    // test code goes here
+}
+
+#[cfg(test)]
+mod test {
+    const B: i8 = 1;
+
+    const A: i8 = 0;
+}
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.var_1.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.var_1.stderr
new file mode 100644
index 00000000000..f31f7f68c17
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.var_1.stderr
@@ -0,0 +1,100 @@
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:105:8
+   |
+LL |     fn b() {}
+   |        ^
+   |
+note: should be placed before `c`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:104:8
+   |
+LL |     fn c() {}
+   |        ^
+   = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]`
+
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:111:11
+   |
+LL |     const B: bool = false;
+   |           ^
+   |
+note: should be placed before `C`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:110:11
+   |
+LL |     const C: bool = false;
+   |           ^
+
+error: incorrect ordering of impl items (defined order: [Fn, Type, Const])
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:117:5
+   |
+LL |     type SomeType = ();
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+note: should be placed before `A`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:115:5
+   |
+LL |     const A: bool = false;
+   |     ^^^^^^^^^^^^^^^^^^^^^^
+
+error: incorrect ordering of impl items (defined order: [Fn, Type, Const])
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:119:5
+   |
+LL |     fn a() {}
+   |     ^^^^^^^^^
+   |
+note: should be placed before `SomeType`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:117:5
+   |
+LL |     type SomeType = ();
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:145:8
+   |
+LL |     fn b();
+   |        ^
+   |
+note: should be placed before `c`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:144:8
+   |
+LL |     fn c();
+   |        ^
+
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:151:11
+   |
+LL |     const B: bool;
+   |           ^
+   |
+note: should be placed before `C`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:150:11
+   |
+LL |     const C: bool;
+   |           ^
+
+error: incorrect ordering of trait items (defined order: [Fn, Type, Const])
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:157:5
+   |
+LL |     type SomeType;
+   |     ^^^^^^^^^^^^^^
+   |
+note: should be placed before `A`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:155:5
+   |
+LL |     const A: bool;
+   |     ^^^^^^^^^^^^^^
+
+error: incorrect ordering of trait items (defined order: [Fn, Type, Const])
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:159:5
+   |
+LL |     fn a();
+   |     ^^^^^^^
+   |
+note: should be placed before `SomeType`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_mixed_var_1.rs:157:5
+   |
+LL |     type SomeType;
+   |     ^^^^^^^^^^^^^^
+
+error: aborting due to 8 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.only_enum.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.only_enum.stderr
new file mode 100644
index 00000000000..57069fd8fee
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.only_enum.stderr
@@ -0,0 +1,16 @@
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.rs:22:5
+   |
+LL |     A,
+   |     ^
+   |
+note: should be placed before `B`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.rs:21:5
+   |
+LL |     B,
+   |     ^
+   = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]`
+
+error: aborting due to 1 previous error
+
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.rs
new file mode 100644
index 00000000000..e8002c4a163
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_enum.rs
@@ -0,0 +1,43 @@
+//@aux-build:../../ui/auxiliary/proc_macros.rs
+//@revisions: only_enum
+//@[only_enum] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/only_enum
+
+#![allow(dead_code)]
+#![warn(clippy::arbitrary_source_item_ordering)]
+
+fn main() {}
+
+struct StructUnordered {
+    b: bool,
+    a: bool,
+}
+
+enum EnumOrdered {
+    A,
+    B,
+}
+
+enum EnumUnordered {
+    B,
+    A,
+}
+
+trait TraitUnordered {
+    const B: bool;
+    const A: bool;
+
+    type SomeType;
+
+    fn b();
+    fn a();
+}
+
+trait TraitUnorderedItemKinds {
+    type SomeType;
+
+    const A: bool;
+
+    fn a();
+}
+
+const ZIS_SHOULD_BE_AT_THE_TOP: () = ();
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.only_impl.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.only_impl.stderr
new file mode 100644
index 00000000000..40348ecbdae
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.only_impl.stderr
@@ -0,0 +1,40 @@
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:43:8
+   |
+LL |     fn a() {}
+   |        ^
+   |
+note: should be placed before `b`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:42:8
+   |
+LL |     fn b() {}
+   |        ^
+   = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]`
+
+error: incorrect ordering of impl items (defined order: [Const, Type, Fn])
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:45:5
+   |
+LL |     type SomeType = i8;
+   |     ^^^^^^^^^^^^^^^^^^^
+   |
+note: should be placed before `a`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:43:5
+   |
+LL |     fn a() {}
+   |     ^^^^^^^^^
+
+error: incorrect ordering of impl items (defined order: [Const, Type, Fn])
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:47:5
+   |
+LL |     const A: bool = true;
+   |     ^^^^^^^^^^^^^^^^^^^^^
+   |
+note: should be placed before `SomeType`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs:45:5
+   |
+LL |     type SomeType = i8;
+   |     ^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs
new file mode 100644
index 00000000000..bd969c865b5
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_impl.rs
@@ -0,0 +1,67 @@
+//@aux-build:../../ui/auxiliary/proc_macros.rs
+//@revisions: only_impl
+//@[only_impl] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/only_impl
+
+#![allow(dead_code)]
+#![warn(clippy::arbitrary_source_item_ordering)]
+
+fn main() {}
+
+struct StructUnordered {
+    b: bool,
+    a: bool,
+}
+
+struct BasicStruct {}
+
+trait BasicTrait {
+    const A: bool;
+
+    type SomeType;
+
+    fn b();
+    fn a();
+}
+
+enum EnumUnordered {
+    B,
+    A,
+}
+
+trait TraitUnordered {
+    const B: bool;
+    const A: bool;
+
+    type SomeType;
+
+    fn b();
+    fn a();
+}
+
+impl BasicTrait for StructUnordered {
+    fn b() {}
+    fn a() {}
+
+    type SomeType = i8;
+
+    const A: bool = true;
+}
+
+trait TraitUnorderedItemKinds {
+    type SomeType;
+
+    const A: bool;
+
+    fn a();
+}
+
+const ZIS_SHOULD_BE_AT_THE_TOP: () = ();
+
+impl BasicTrait for BasicStruct {
+    const A: bool = true;
+
+    type SomeType = i8;
+
+    fn a() {}
+    fn b() {}
+}
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.only_trait.stderr b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.only_trait.stderr
new file mode 100644
index 00000000000..9b86ebd48e3
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.only_trait.stderr
@@ -0,0 +1,40 @@
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:32:11
+   |
+LL |     const A: bool;
+   |           ^
+   |
+note: should be placed before `B`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:31:11
+   |
+LL |     const B: bool;
+   |           ^
+   = note: `-D clippy::arbitrary-source-item-ordering` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::arbitrary_source_item_ordering)]`
+
+error: incorrect ordering of items (must be alphabetically ordered)
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:37:8
+   |
+LL |     fn a();
+   |        ^
+   |
+note: should be placed before `b`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:36:8
+   |
+LL |     fn b();
+   |        ^
+
+error: incorrect ordering of trait items (defined order: [Const, Type, Fn])
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:43:5
+   |
+LL |     const A: bool;
+   |     ^^^^^^^^^^^^^^
+   |
+note: should be placed before `SomeType`
+  --> tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs:41:5
+   |
+LL |     type SomeType;
+   |     ^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors
+
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs
new file mode 100644
index 00000000000..979a52ecb10
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/ordering_only_trait.rs
@@ -0,0 +1,48 @@
+//@aux-build:../../ui/auxiliary/proc_macros.rs
+//@revisions: only_trait
+//@[only_trait] rustc-env:CLIPPY_CONF_DIR=tests/ui-toml/arbitrary_source_item_ordering/only_trait
+
+#![allow(dead_code)]
+#![warn(clippy::arbitrary_source_item_ordering)]
+
+fn main() {}
+
+struct StructUnordered {
+    b: bool,
+    a: bool,
+}
+
+trait TraitOrdered {
+    const A: bool;
+    const B: bool;
+
+    type SomeType;
+
+    fn a();
+    fn b();
+}
+
+enum EnumUnordered {
+    B,
+    A,
+}
+
+trait TraitUnordered {
+    const B: bool;
+    const A: bool;
+
+    type SomeType;
+
+    fn b();
+    fn a();
+}
+
+trait TraitUnorderedItemKinds {
+    type SomeType;
+
+    const A: bool;
+
+    fn a();
+}
+
+const ZIS_SHOULD_BE_AT_THE_TOP: () = ();
diff --git a/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/var_1/clippy.toml b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/var_1/clippy.toml
new file mode 100644
index 00000000000..bede035e388
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/arbitrary_source_item_ordering/var_1/clippy.toml
@@ -0,0 +1 @@
+trait-assoc-item-kinds-order = ["fn", "type", "const"]
diff --git a/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr
index ddeb2f8cc70..2e3386628b4 100644
--- a/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr
+++ b/src/tools/clippy/tests/ui-toml/disallowed_macros/disallowed_macros.stderr
@@ -1,11 +1,39 @@
+error: use of a disallowed macro `std::vec`
+  --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:16:5
+   |
+LL |     vec![1, 2, 3];
+   |     ^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::disallowed-macros` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::disallowed_macros)]`
+
+error: use of a disallowed macro `serde::Serialize`
+  --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:18:14
+   |
+LL |     #[derive(Serialize)]
+   |              ^^^^^^^^^
+   |
+   = note: no serializing
+
+error: use of a disallowed macro `macros::attr`
+  --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:31:1
+   |
+LL | / macros::attr! {
+LL | |     struct S;
+LL | | }
+   | |_^
+
+error: use of a disallowed macro `proc_macros::Derive`
+  --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:47:10
+   |
+LL | #[derive(Derive)]
+   |          ^^^^^^
+
 error: use of a disallowed macro `std::println`
   --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:13:5
    |
 LL |     println!("one");
    |     ^^^^^^^^^^^^^^^
-   |
-   = note: `-D clippy::disallowed-macros` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::disallowed_macros)]`
 
 error: use of a disallowed macro `std::println`
   --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:14:5
@@ -19,20 +47,6 @@ error: use of a disallowed macro `std::cfg`
 LL |     cfg!(unix);
    |     ^^^^^^^^^^
 
-error: use of a disallowed macro `std::vec`
-  --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:16:5
-   |
-LL |     vec![1, 2, 3];
-   |     ^^^^^^^^^^^^^
-
-error: use of a disallowed macro `serde::Serialize`
-  --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:18:14
-   |
-LL |     #[derive(Serialize)]
-   |              ^^^^^^^^^
-   |
-   = note: no serializing
-
 error: use of a disallowed macro `macros::expr`
   --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:21:13
    |
@@ -69,14 +83,6 @@ error: use of a disallowed macro `macros::binop`
 LL |     let _ = macros::binop!(1);
    |             ^^^^^^^^^^^^^^^^^
 
-error: use of a disallowed macro `macros::attr`
-  --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:31:1
-   |
-LL | / macros::attr! {
-LL | |     struct S;
-LL | | }
-   | |_^
-
 error: use of a disallowed macro `macros::item`
   --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:36:5
    |
@@ -95,11 +101,5 @@ error: use of a disallowed macro `macros::item`
 LL |     macros::item!();
    |     ^^^^^^^^^^^^^^^
 
-error: use of a disallowed macro `proc_macros::Derive`
-  --> tests/ui-toml/disallowed_macros/disallowed_macros.rs:47:10
-   |
-LL | #[derive(Derive)]
-   |          ^^^^^^
-
 error: aborting due to 16 previous errors
 
diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/empty.txt b/src/tools/clippy/tests/ui-toml/large_include_file/empty.txt
new file mode 100644
index 00000000000..e69de29bb2d
--- /dev/null
+++ b/src/tools/clippy/tests/ui-toml/large_include_file/empty.txt
diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs
index f3dbb6ad1cf..8a6dd36501c 100644
--- a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs
+++ b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.rs
@@ -1,8 +1,8 @@
 #![warn(clippy::large_include_file)]
 
 // Good
-const GOOD_INCLUDE_BYTES: &[u8; 581] = include_bytes!("large_include_file.rs");
-const GOOD_INCLUDE_STR: &str = include_str!("large_include_file.rs");
+const GOOD_INCLUDE_BYTES: &[u8; 68] = include_bytes!("../../ui/author.rs");
+const GOOD_INCLUDE_STR: &str = include_str!("../../ui/author.rs");
 
 #[allow(clippy::large_include_file)]
 const ALLOWED_TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
@@ -11,6 +11,13 @@ const ALLOWED_TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
 
 // Bad
 const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
+//~^ large_include_file
 const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
+//~^ large_include_file
 
+#[doc = include_str!("too_big.txt")]
+//~^ large_include_file
+// Should not lint!
+// Regression test for <https://github.com/rust-lang/rust-clippy/issues/13670>.
+#[doc = include_str!("empty.txt")]
 fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr
index 34224065f07..9e1494a47bb 100644
--- a/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr
+++ b/src/tools/clippy/tests/ui-toml/large_include_file/large_include_file.stderr
@@ -9,12 +9,20 @@ LL | const TOO_BIG_INCLUDE_BYTES: &[u8; 654] = include_bytes!("too_big.txt");
    = help: to override `-D warnings` add `#[allow(clippy::large_include_file)]`
 
 error: attempted to include a large file
-  --> tests/ui-toml/large_include_file/large_include_file.rs:14:35
+  --> tests/ui-toml/large_include_file/large_include_file.rs:15:35
    |
 LL | const TOO_BIG_INCLUDE_STR: &str = include_str!("too_big.txt");
    |                                   ^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = note: the configuration allows a maximum size of 600 bytes
 
-error: aborting due to 2 previous errors
+error: attempted to include a large file
+  --> tests/ui-toml/large_include_file/large_include_file.rs:18:1
+   |
+LL | #[doc = include_str!("too_big.txt")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: the configuration allows a maximum size of 600 bytes
+
+error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.rs b/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.rs
index 79c8751468d..c5d00c91b05 100644
--- a/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.rs
+++ b/src/tools/clippy/tests/ui-toml/private-doc-errors/doc_lints.rs
@@ -51,4 +51,10 @@ pub mod __macro {
     }
 }
 
+#[warn(clippy::missing_errors_doc)]
+#[test]
+fn test() -> Result<(), ()> {
+    Ok(())
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
index 5cf9c0fb271..6fa583fc041 100644
--- a/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
+++ b/src/tools/clippy/tests/ui-toml/toml_unknown_key/conf_unknown_key.stderr
@@ -54,12 +54,14 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
            max-trait-bounds
            min-ident-chars-threshold
            missing-docs-in-crate-items
+           module-item-order-groupings
            msrv
            pass-by-value-size-limit
            pub-underscore-fields-behavior
            semicolon-inside-block-ignore-singleline
            semicolon-outside-block-ignore-multiline
            single-char-binding-names-threshold
+           source-item-ordering
            stack-size-threshold
            standard-macro-braces
            struct-field-name-threshold
@@ -68,6 +70,7 @@ error: error reading Clippy's configuration file: unknown field `foobar`, expect
            too-large-for-stack
            too-many-arguments-threshold
            too-many-lines-threshold
+           trait-assoc-item-kinds-order
            trivial-copy-size-limit
            type-complexity-threshold
            unnecessary-box-size
@@ -138,12 +141,14 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
            max-trait-bounds
            min-ident-chars-threshold
            missing-docs-in-crate-items
+           module-item-order-groupings
            msrv
            pass-by-value-size-limit
            pub-underscore-fields-behavior
            semicolon-inside-block-ignore-singleline
            semicolon-outside-block-ignore-multiline
            single-char-binding-names-threshold
+           source-item-ordering
            stack-size-threshold
            standard-macro-braces
            struct-field-name-threshold
@@ -152,6 +157,7 @@ error: error reading Clippy's configuration file: unknown field `barfoo`, expect
            too-large-for-stack
            too-many-arguments-threshold
            too-many-lines-threshold
+           trait-assoc-item-kinds-order
            trivial-copy-size-limit
            type-complexity-threshold
            unnecessary-box-size
@@ -222,12 +228,14 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
            max-trait-bounds
            min-ident-chars-threshold
            missing-docs-in-crate-items
+           module-item-order-groupings
            msrv
            pass-by-value-size-limit
            pub-underscore-fields-behavior
            semicolon-inside-block-ignore-singleline
            semicolon-outside-block-ignore-multiline
            single-char-binding-names-threshold
+           source-item-ordering
            stack-size-threshold
            standard-macro-braces
            struct-field-name-threshold
@@ -236,6 +244,7 @@ error: error reading Clippy's configuration file: unknown field `allow_mixed_uni
            too-large-for-stack
            too-many-arguments-threshold
            too-many-lines-threshold
+           trait-assoc-item-kinds-order
            trivial-copy-size-limit
            type-complexity-threshold
            unnecessary-box-size
diff --git a/src/tools/clippy/tests/ui/allow_attributes.fixed b/src/tools/clippy/tests/ui/allow_attributes.fixed
index 058dbb77a32..8f6c962e2f5 100644
--- a/src/tools/clippy/tests/ui/allow_attributes.fixed
+++ b/src/tools/clippy/tests/ui/allow_attributes.fixed
@@ -1,4 +1,5 @@
 //@aux-build:proc_macros.rs
+//@aux-build:proc_macro_derive.rs
 #![allow(unused)]
 #![warn(clippy::allow_attributes)]
 #![no_main]
@@ -65,3 +66,9 @@ fn deny_allow_attributes() -> Option<u8> {
     allow?;
     Some(42)
 }
+
+// Edge case where the generated tokens spans match on #[repr(transparent)] which tricks the proc
+// macro check
+#[repr(transparent)]
+#[derive(proc_macro_derive::AllowLintSameSpan)] // This macro generates tokens with the same span as the whole struct and repr
+struct IgnoreDerived;
diff --git a/src/tools/clippy/tests/ui/allow_attributes.rs b/src/tools/clippy/tests/ui/allow_attributes.rs
index 6d94ce50e4c..cb6c4dcf715 100644
--- a/src/tools/clippy/tests/ui/allow_attributes.rs
+++ b/src/tools/clippy/tests/ui/allow_attributes.rs
@@ -1,4 +1,5 @@
 //@aux-build:proc_macros.rs
+//@aux-build:proc_macro_derive.rs
 #![allow(unused)]
 #![warn(clippy::allow_attributes)]
 #![no_main]
@@ -65,3 +66,9 @@ fn deny_allow_attributes() -> Option<u8> {
     allow?;
     Some(42)
 }
+
+// Edge case where the generated tokens spans match on #[repr(transparent)] which tricks the proc
+// macro check
+#[repr(transparent)]
+#[derive(proc_macro_derive::AllowLintSameSpan)] // This macro generates tokens with the same span as the whole struct and repr
+struct IgnoreDerived;
diff --git a/src/tools/clippy/tests/ui/allow_attributes.stderr b/src/tools/clippy/tests/ui/allow_attributes.stderr
index 10dac0bc808..5a4ff287acd 100644
--- a/src/tools/clippy/tests/ui/allow_attributes.stderr
+++ b/src/tools/clippy/tests/ui/allow_attributes.stderr
@@ -1,5 +1,5 @@
 error: #[allow] attribute found
-  --> tests/ui/allow_attributes.rs:12:3
+  --> tests/ui/allow_attributes.rs:13:3
    |
 LL | #[allow(dead_code)]
    |   ^^^^^ help: replace it with: `expect`
@@ -8,24 +8,16 @@ LL | #[allow(dead_code)]
    = help: to override `-D warnings` add `#[allow(clippy::allow_attributes)]`
 
 error: #[allow] attribute found
-  --> tests/ui/allow_attributes.rs:21:30
+  --> tests/ui/allow_attributes.rs:22:30
    |
 LL | #[cfg_attr(panic = "unwind", allow(dead_code))]
    |                              ^^^^^ help: replace it with: `expect`
 
 error: #[allow] attribute found
-  --> tests/ui/allow_attributes.rs:52:7
+  --> tests/ui/allow_attributes.rs:53:7
    |
 LL |     #[allow(unused)]
    |       ^^^^^ help: replace it with: `expect`
 
-error: #[allow] attribute found
-  --> tests/ui/allow_attributes.rs:52:7
-   |
-LL |     #[allow(unused)]
-   |       ^^^^^ help: replace it with: `expect`
-   |
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr b/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr
index 86d7845df04..9c1ac5af91b 100644
--- a/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr
+++ b/src/tools/clippy/tests/ui/allow_attributes_without_reason.stderr
@@ -43,14 +43,5 @@ LL |     #[allow(unused)]
    |
    = help: try adding a reason at the end with `, reason = ".."`
 
-error: `allow` attribute without specifying a reason
-  --> tests/ui/allow_attributes_without_reason.rs:46:5
-   |
-LL |     #[allow(unused)]
-   |     ^^^^^^^^^^^^^^^^
-   |
-   = help: try adding a reason at the end with `, reason = ".."`
-   = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
-
-error: aborting due to 6 previous errors
+error: aborting due to 5 previous errors
 
diff --git a/src/tools/clippy/tests/ui/attrs.stderr b/src/tools/clippy/tests/ui/attrs.stderr
index cd409fc8701..a7fdceaba6f 100644
--- a/src/tools/clippy/tests/ui/attrs.stderr
+++ b/src/tools/clippy/tests/ui/attrs.stderr
@@ -1,12 +1,3 @@
-error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea
-  --> tests/ui/attrs.rs:5:1
-   |
-LL | #[inline(always)]
-   | ^^^^^^^^^^^^^^^^^
-   |
-   = note: `-D clippy::inline-always` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::inline_always)]`
-
 error: the since field must contain a semver-compliant version
   --> tests/ui/attrs.rs:27:14
    |
@@ -22,5 +13,14 @@ error: the since field must contain a semver-compliant version
 LL | #[deprecated(since = "1")]
    |              ^^^^^^^^^^^
 
+error: you have declared `#[inline(always)]` on `test_attr_lint`. This is usually a bad idea
+  --> tests/ui/attrs.rs:5:1
+   |
+LL | #[inline(always)]
+   | ^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::inline-always` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::inline_always)]`
+
 error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui-internal/author.rs b/src/tools/clippy/tests/ui/author.rs
index eb1f3e3f870..0a1be356896 100644
--- a/src/tools/clippy/tests/ui-internal/author.rs
+++ b/src/tools/clippy/tests/ui/author.rs
@@ -1,5 +1,3 @@
-#![warn(clippy::author)]
-
 fn main() {
     #[clippy::author]
     let x: char = 0x45 as char;
diff --git a/src/tools/clippy/tests/ui-internal/author.stdout b/src/tools/clippy/tests/ui/author.stdout
index eed704e82fe..eed704e82fe 100644
--- a/src/tools/clippy/tests/ui-internal/author.stdout
+++ b/src/tools/clippy/tests/ui/author.stdout
diff --git a/src/tools/clippy/tests/ui-internal/author/blocks.rs b/src/tools/clippy/tests/ui/author/blocks.rs
index 164f7d0d9d6..164f7d0d9d6 100644
--- a/src/tools/clippy/tests/ui-internal/author/blocks.rs
+++ b/src/tools/clippy/tests/ui/author/blocks.rs
diff --git a/src/tools/clippy/tests/ui-internal/author/blocks.stdout b/src/tools/clippy/tests/ui/author/blocks.stdout
index 6bf48d5ba4e..6bf48d5ba4e 100644
--- a/src/tools/clippy/tests/ui-internal/author/blocks.stdout
+++ b/src/tools/clippy/tests/ui/author/blocks.stdout
diff --git a/src/tools/clippy/tests/ui-internal/author/call.rs b/src/tools/clippy/tests/ui/author/call.rs
index e99c3c41dc4..e99c3c41dc4 100644
--- a/src/tools/clippy/tests/ui-internal/author/call.rs
+++ b/src/tools/clippy/tests/ui/author/call.rs
diff --git a/src/tools/clippy/tests/ui-internal/author/call.stdout b/src/tools/clippy/tests/ui/author/call.stdout
index 59d4da490fe..59d4da490fe 100644
--- a/src/tools/clippy/tests/ui-internal/author/call.stdout
+++ b/src/tools/clippy/tests/ui/author/call.stdout
diff --git a/src/tools/clippy/tests/ui-internal/author/if.rs b/src/tools/clippy/tests/ui/author/if.rs
index 946088ab346..946088ab346 100644
--- a/src/tools/clippy/tests/ui-internal/author/if.rs
+++ b/src/tools/clippy/tests/ui/author/if.rs
diff --git a/src/tools/clippy/tests/ui-internal/author/if.stdout b/src/tools/clippy/tests/ui/author/if.stdout
index a85dcddd331..a85dcddd331 100644
--- a/src/tools/clippy/tests/ui-internal/author/if.stdout
+++ b/src/tools/clippy/tests/ui/author/if.stdout
diff --git a/src/tools/clippy/tests/ui-internal/author/issue_3849.rs b/src/tools/clippy/tests/ui/author/issue_3849.rs
index 5f65746d71f..5f65746d71f 100644
--- a/src/tools/clippy/tests/ui-internal/author/issue_3849.rs
+++ b/src/tools/clippy/tests/ui/author/issue_3849.rs
diff --git a/src/tools/clippy/tests/ui-internal/author/issue_3849.stdout b/src/tools/clippy/tests/ui/author/issue_3849.stdout
index a5a8c0304ee..a5a8c0304ee 100644
--- a/src/tools/clippy/tests/ui-internal/author/issue_3849.stdout
+++ b/src/tools/clippy/tests/ui/author/issue_3849.stdout
diff --git a/src/tools/clippy/tests/ui-internal/author/loop.rs b/src/tools/clippy/tests/ui/author/loop.rs
index ff5b6100117..ff5b6100117 100644
--- a/src/tools/clippy/tests/ui-internal/author/loop.rs
+++ b/src/tools/clippy/tests/ui/author/loop.rs
diff --git a/src/tools/clippy/tests/ui-internal/author/loop.stdout b/src/tools/clippy/tests/ui/author/loop.stdout
index 609d2491061..609d2491061 100644
--- a/src/tools/clippy/tests/ui-internal/author/loop.stdout
+++ b/src/tools/clippy/tests/ui/author/loop.stdout
diff --git a/src/tools/clippy/tests/ui-internal/author/macro_in_closure.rs b/src/tools/clippy/tests/ui/author/macro_in_closure.rs
index 444e6a12165..444e6a12165 100644
--- a/src/tools/clippy/tests/ui-internal/author/macro_in_closure.rs
+++ b/src/tools/clippy/tests/ui/author/macro_in_closure.rs
diff --git a/src/tools/clippy/tests/ui-internal/author/macro_in_closure.stdout b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
index 66caf382d89..66caf382d89 100644
--- a/src/tools/clippy/tests/ui-internal/author/macro_in_closure.stdout
+++ b/src/tools/clippy/tests/ui/author/macro_in_closure.stdout
diff --git a/src/tools/clippy/tests/ui-internal/author/macro_in_loop.rs b/src/tools/clippy/tests/ui/author/macro_in_loop.rs
index 8a520501f8d..8a520501f8d 100644
--- a/src/tools/clippy/tests/ui-internal/author/macro_in_loop.rs
+++ b/src/tools/clippy/tests/ui/author/macro_in_loop.rs
diff --git a/src/tools/clippy/tests/ui-internal/author/macro_in_loop.stdout b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout
index 3f9be297c33..3f9be297c33 100644
--- a/src/tools/clippy/tests/ui-internal/author/macro_in_loop.stdout
+++ b/src/tools/clippy/tests/ui/author/macro_in_loop.stdout
diff --git a/src/tools/clippy/tests/ui-internal/author/matches.rs b/src/tools/clippy/tests/ui/author/matches.rs
index 674e07ec2d3..674e07ec2d3 100644
--- a/src/tools/clippy/tests/ui-internal/author/matches.rs
+++ b/src/tools/clippy/tests/ui/author/matches.rs
diff --git a/src/tools/clippy/tests/ui-internal/author/matches.stdout b/src/tools/clippy/tests/ui/author/matches.stdout
index 91b3b6f6877..91b3b6f6877 100644
--- a/src/tools/clippy/tests/ui-internal/author/matches.stdout
+++ b/src/tools/clippy/tests/ui/author/matches.stdout
diff --git a/src/tools/clippy/tests/ui-internal/author/repeat.rs b/src/tools/clippy/tests/ui/author/repeat.rs
index d8e9d589e68..d8e9d589e68 100644
--- a/src/tools/clippy/tests/ui-internal/author/repeat.rs
+++ b/src/tools/clippy/tests/ui/author/repeat.rs
diff --git a/src/tools/clippy/tests/ui-internal/author/repeat.stdout b/src/tools/clippy/tests/ui/author/repeat.stdout
index d9e3f864f12..d9e3f864f12 100644
--- a/src/tools/clippy/tests/ui-internal/author/repeat.stdout
+++ b/src/tools/clippy/tests/ui/author/repeat.stdout
diff --git a/src/tools/clippy/tests/ui-internal/author/struct.rs b/src/tools/clippy/tests/ui/author/struct.rs
index a99bdfc1313..a99bdfc1313 100644
--- a/src/tools/clippy/tests/ui-internal/author/struct.rs
+++ b/src/tools/clippy/tests/ui/author/struct.rs
diff --git a/src/tools/clippy/tests/ui-internal/author/struct.stdout b/src/tools/clippy/tests/ui/author/struct.stdout
index 0b332d5e7d0..0b332d5e7d0 100644
--- a/src/tools/clippy/tests/ui-internal/author/struct.stdout
+++ b/src/tools/clippy/tests/ui/author/struct.stdout
diff --git a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
index bd90042c1da..fbf84337382 100644
--- a/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
+++ b/src/tools/clippy/tests/ui/auxiliary/proc_macro_derive.rs
@@ -1,4 +1,4 @@
-#![feature(repr128, proc_macro_quote)]
+#![feature(repr128, proc_macro_quote, proc_macro_span)]
 #![allow(incomplete_features)]
 #![allow(clippy::field_reassign_with_default)]
 #![allow(clippy::eq_op)]
@@ -182,3 +182,51 @@ pub fn non_canonical_clone_derive(_: TokenStream) -> TokenStream {
         impl Copy for NonCanonicalClone {}
     }
 }
+
+// Derive macro that generates the following but where all generated spans are set to the entire
+// input span.
+//
+// ```
+// #[allow(clippy::missing_const_for_fn)]
+// fn check() {}
+// ```
+#[proc_macro_derive(AllowLintSameSpan)]
+pub fn allow_lint_same_span_derive(input: TokenStream) -> TokenStream {
+    let mut iter = input.into_iter();
+    let first = iter.next().unwrap();
+    let last = iter.last().unwrap();
+    let span = first.span().join(last.span()).unwrap();
+    let span_help = |mut t: TokenTree| -> TokenTree {
+        t.set_span(span);
+        t
+    };
+    // Generate the TokenStream but setting all the spans to the entire input span
+    <TokenStream as FromIterator<TokenTree>>::from_iter([
+        span_help(Punct::new('#', Spacing::Alone).into()),
+        span_help(
+            Group::new(
+                Delimiter::Bracket,
+                <TokenStream as FromIterator<TokenTree>>::from_iter([
+                    Ident::new("allow", span).into(),
+                    span_help(
+                        Group::new(
+                            Delimiter::Parenthesis,
+                            <TokenStream as FromIterator<TokenTree>>::from_iter([
+                                Ident::new("clippy", span).into(),
+                                span_help(Punct::new(':', Spacing::Joint).into()),
+                                span_help(Punct::new(':', Spacing::Alone).into()),
+                                Ident::new("missing_const_for_fn", span).into(),
+                            ]),
+                        )
+                        .into(),
+                    ),
+                ]),
+            )
+            .into(),
+        ),
+        Ident::new("fn", span).into(),
+        Ident::new("check", span).into(),
+        span_help(Group::new(Delimiter::Parenthesis, TokenStream::new()).into()),
+        span_help(Group::new(Delimiter::Brace, TokenStream::new()).into()),
+    ])
+}
diff --git a/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr b/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr
index d410f25b2c2..1bad259b09a 100644
--- a/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr
+++ b/src/tools/clippy/tests/ui/blanket_clippy_restriction_lints.stderr
@@ -1,12 +1,17 @@
 error: `clippy::restriction` is not meant to be enabled as a group
+   |
+   = note: because of the command line `--warn clippy::restriction`
+   = help: enable the restriction lints you need individually
+   = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::blanket_clippy_restriction_lints)]`
+
+error: `clippy::restriction` is not meant to be enabled as a group
   --> tests/ui/blanket_clippy_restriction_lints.rs:6:9
    |
 LL | #![warn(clippy::restriction)]
    |         ^^^^^^^^^^^^^^^^^^^
    |
    = help: enable the restriction lints you need individually
-   = note: `-D clippy::blanket-clippy-restriction-lints` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(clippy::blanket_clippy_restriction_lints)]`
 
 error: `clippy::restriction` is not meant to be enabled as a group
   --> tests/ui/blanket_clippy_restriction_lints.rs:8:9
@@ -24,10 +29,5 @@ LL | #![forbid(clippy::restriction)]
    |
    = help: enable the restriction lints you need individually
 
-error: `clippy::restriction` is not meant to be enabled as a group
-   |
-   = note: because of the command line `--warn clippy::restriction`
-   = help: enable the restriction lints you need individually
-
 error: aborting due to 4 previous errors
 
diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref.fixed b/src/tools/clippy/tests/ui/borrow_deref_ref.fixed
index ea5e983de3b..22e984c46d2 100644
--- a/src/tools/clippy/tests/ui/borrow_deref_ref.fixed
+++ b/src/tools/clippy/tests/ui/borrow_deref_ref.fixed
@@ -71,3 +71,9 @@ mod false_negative {
         assert_ne!(addr_x, addr_y);
     }
 }
+
+fn issue_13584() {
+    let s = "Hello, world!\n";
+    let p = &raw const *s;
+    let _ = p as *const i8;
+}
diff --git a/src/tools/clippy/tests/ui/borrow_deref_ref.rs b/src/tools/clippy/tests/ui/borrow_deref_ref.rs
index 8c8905b150e..61d89193f42 100644
--- a/src/tools/clippy/tests/ui/borrow_deref_ref.rs
+++ b/src/tools/clippy/tests/ui/borrow_deref_ref.rs
@@ -71,3 +71,9 @@ mod false_negative {
         assert_ne!(addr_x, addr_y);
     }
 }
+
+fn issue_13584() {
+    let s = "Hello, world!\n";
+    let p = &raw const *s;
+    let _ = p as *const i8;
+}
diff --git a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.fixed b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.fixed
index 8b4a165a97c..c4d77f24f12 100644
--- a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.fixed
+++ b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.fixed
@@ -1,4 +1,5 @@
 #![warn(clippy::case_sensitive_file_extension_comparisons)]
+#![allow(clippy::unnecessary_map_or)]
 
 use std::string::String;
 
diff --git a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs
index e4b8178110b..690e93c2639 100644
--- a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs
+++ b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.rs
@@ -1,4 +1,5 @@
 #![warn(clippy::case_sensitive_file_extension_comparisons)]
+#![allow(clippy::unnecessary_map_or)]
 
 use std::string::String;
 
diff --git a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr
index d203f91b832..e21815f251b 100644
--- a/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr
+++ b/src/tools/clippy/tests/ui/case_sensitive_file_extension_comparisons.stderr
@@ -1,5 +1,5 @@
 error: case-sensitive file extension comparison
-  --> tests/ui/case_sensitive_file_extension_comparisons.rs:13:5
+  --> tests/ui/case_sensitive_file_extension_comparisons.rs:14:5
    |
 LL |     filename.ends_with(".rs")
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -15,7 +15,7 @@ LL +         .map_or(false, |ext| ext.eq_ignore_ascii_case("rs"))
    |
 
 error: case-sensitive file extension comparison
-  --> tests/ui/case_sensitive_file_extension_comparisons.rs:18:13
+  --> tests/ui/case_sensitive_file_extension_comparisons.rs:19:13
    |
 LL |     let _ = String::new().ends_with(".ext12");
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -29,7 +29,7 @@ LL ~         .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12"));
    |
 
 error: case-sensitive file extension comparison
-  --> tests/ui/case_sensitive_file_extension_comparisons.rs:19:13
+  --> tests/ui/case_sensitive_file_extension_comparisons.rs:20:13
    |
 LL |     let _ = "str".ends_with(".ext12");
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -43,7 +43,7 @@ LL ~         .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12"));
    |
 
 error: case-sensitive file extension comparison
-  --> tests/ui/case_sensitive_file_extension_comparisons.rs:23:17
+  --> tests/ui/case_sensitive_file_extension_comparisons.rs:24:17
    |
 LL |         let _ = "str".ends_with(".ext12");
    |                 ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -57,7 +57,7 @@ LL ~             .map_or(false, |ext| ext.eq_ignore_ascii_case("ext12"));
    |
 
 error: case-sensitive file extension comparison
-  --> tests/ui/case_sensitive_file_extension_comparisons.rs:30:13
+  --> tests/ui/case_sensitive_file_extension_comparisons.rs:31:13
    |
 LL |     let _ = String::new().ends_with(".EXT12");
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -71,7 +71,7 @@ LL ~         .map_or(false, |ext| ext.eq_ignore_ascii_case("EXT12"));
    |
 
 error: case-sensitive file extension comparison
-  --> tests/ui/case_sensitive_file_extension_comparisons.rs:31:13
+  --> tests/ui/case_sensitive_file_extension_comparisons.rs:32:13
    |
 LL |     let _ = "str".ends_with(".EXT12");
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/const_is_empty.rs b/src/tools/clippy/tests/ui/const_is_empty.rs
index 04e0de91ecf..b5e27b61548 100644
--- a/src/tools/clippy/tests/ui/const_is_empty.rs
+++ b/src/tools/clippy/tests/ui/const_is_empty.rs
@@ -171,3 +171,17 @@ fn constant_from_external_crate() {
     let _ = std::env::consts::EXE_EXTENSION.is_empty();
     // Do not lint, `exe_ext` comes from the `std` crate
 }
+
+fn issue_13106() {
+    const {
+        assert!(!NON_EMPTY_STR.is_empty());
+    }
+
+    const {
+        assert!(EMPTY_STR.is_empty());
+    }
+
+    const {
+        EMPTY_STR.is_empty();
+    }
+}
diff --git a/src/tools/clippy/tests/ui/const_is_empty.stderr b/src/tools/clippy/tests/ui/const_is_empty.stderr
index 7f80b520b1a..0afba940d8b 100644
--- a/src/tools/clippy/tests/ui/const_is_empty.stderr
+++ b/src/tools/clippy/tests/ui/const_is_empty.stderr
@@ -157,5 +157,11 @@ error: this expression always evaluates to true
 LL |     let _ = val.is_empty();
    |             ^^^^^^^^^^^^^^
 
-error: aborting due to 26 previous errors
+error: this expression always evaluates to true
+  --> tests/ui/const_is_empty.rs:185:9
+   |
+LL |         EMPTY_STR.is_empty();
+   |         ^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 27 previous errors
 
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
index 355f2bc7736..5cf5c608a85 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.fixed
@@ -54,8 +54,9 @@ fn test_units() {
 
 /// This tests allowed identifiers.
 /// KiB MiB GiB TiB PiB EiB
+/// MHz GHz THz
 /// AccessKit
-/// CoreFoundation CoreGraphics CoreText
+/// CoAP CoreFoundation CoreGraphics CoreText
 /// Direct2D Direct3D DirectWrite DirectX
 /// ECMAScript
 /// GPLv2 GPLv3
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.rs b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
index 9ced2677622..420211c6539 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.rs
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.rs
@@ -54,8 +54,9 @@ fn test_units() {
 
 /// This tests allowed identifiers.
 /// KiB MiB GiB TiB PiB EiB
+/// MHz GHz THz
 /// AccessKit
-/// CoreFoundation CoreGraphics CoreText
+/// CoAP CoreFoundation CoreGraphics CoreText
 /// Direct2D Direct3D DirectWrite DirectX
 /// ECMAScript
 /// GPLv2 GPLv3
diff --git a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
index 67c0464149c..27a04e4b558 100644
--- a/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
+++ b/src/tools/clippy/tests/ui/doc/doc-fixable.stderr
@@ -133,7 +133,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:78:5
+  --> tests/ui/doc/doc-fixable.rs:79:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -144,7 +144,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:95:5
+  --> tests/ui/doc/doc-fixable.rs:96:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -155,7 +155,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:103:8
+  --> tests/ui/doc/doc-fixable.rs:104:8
    |
 LL | /// ## CamelCaseThing
    |        ^^^^^^^^^^^^^^
@@ -166,7 +166,7 @@ LL | /// ## `CamelCaseThing`
    |        ~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:106:7
+  --> tests/ui/doc/doc-fixable.rs:107:7
    |
 LL | /// # CamelCaseThing
    |       ^^^^^^^^^^^^^^
@@ -177,7 +177,7 @@ LL | /// # `CamelCaseThing`
    |       ~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:108:22
+  --> tests/ui/doc/doc-fixable.rs:109:22
    |
 LL | /// Not a title #897 CamelCaseThing
    |                      ^^^^^^^^^^^^^^
@@ -188,7 +188,7 @@ LL | /// Not a title #897 `CamelCaseThing`
    |                      ~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:109:5
+  --> tests/ui/doc/doc-fixable.rs:110:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -199,7 +199,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:116:5
+  --> tests/ui/doc/doc-fixable.rs:117:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -210,7 +210,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:129:5
+  --> tests/ui/doc/doc-fixable.rs:130:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -221,7 +221,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:140:43
+  --> tests/ui/doc/doc-fixable.rs:141:43
    |
 LL | /** E.g., serialization of an empty list: FooBar
    |                                           ^^^^^^
@@ -232,7 +232,7 @@ LL | /** E.g., serialization of an empty list: `FooBar`
    |                                           ~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:145:5
+  --> tests/ui/doc/doc-fixable.rs:146:5
    |
 LL | And BarQuz too.
    |     ^^^^^^
@@ -243,7 +243,7 @@ LL | And `BarQuz` too.
    |     ~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:146:1
+  --> tests/ui/doc/doc-fixable.rs:147:1
    |
 LL | be_sure_we_got_to_the_end_of_it
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -254,7 +254,7 @@ LL | `be_sure_we_got_to_the_end_of_it`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:151:43
+  --> tests/ui/doc/doc-fixable.rs:152:43
    |
 LL | /** E.g., serialization of an empty list: FooBar
    |                                           ^^^^^^
@@ -265,7 +265,7 @@ LL | /** E.g., serialization of an empty list: `FooBar`
    |                                           ~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:156:5
+  --> tests/ui/doc/doc-fixable.rs:157:5
    |
 LL | And BarQuz too.
    |     ^^^^^^
@@ -276,7 +276,7 @@ LL | And `BarQuz` too.
    |     ~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:157:1
+  --> tests/ui/doc/doc-fixable.rs:158:1
    |
 LL | be_sure_we_got_to_the_end_of_it
    | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -287,7 +287,7 @@ LL | `be_sure_we_got_to_the_end_of_it`
    |
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:168:5
+  --> tests/ui/doc/doc-fixable.rs:169:5
    |
 LL | /// be_sure_we_got_to_the_end_of_it
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -298,7 +298,7 @@ LL | /// `be_sure_we_got_to_the_end_of_it`
    |     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:187:22
+  --> tests/ui/doc/doc-fixable.rs:188:22
    |
 LL | /// An iterator over mycrate::Collection's values.
    |                      ^^^^^^^^^^^^^^^^^^^
@@ -309,7 +309,7 @@ LL | /// An iterator over `mycrate::Collection`'s values.
    |                      ~~~~~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:211:34
+  --> tests/ui/doc/doc-fixable.rs:212:34
    |
 LL | /// Foo \[bar\] \[baz\] \[qux\]. DocMarkdownLint
    |                                  ^^^^^^^^^^^^^^^
@@ -320,7 +320,7 @@ LL | /// Foo \[bar\] \[baz\] \[qux\]. `DocMarkdownLint`
    |                                  ~~~~~~~~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:234:22
+  --> tests/ui/doc/doc-fixable.rs:235:22
    |
 LL | /// There is no try (do() or do_not()).
    |                      ^^^^
@@ -331,7 +331,7 @@ LL | /// There is no try (`do()` or do_not()).
    |                      ~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:234:30
+  --> tests/ui/doc/doc-fixable.rs:235:30
    |
 LL | /// There is no try (do() or do_not()).
    |                              ^^^^^^^^
@@ -342,7 +342,7 @@ LL | /// There is no try (do() or `do_not()`).
    |                              ~~~~~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:237:5
+  --> tests/ui/doc/doc-fixable.rs:238:5
    |
 LL | /// ABes
    |     ^^^^
@@ -353,7 +353,7 @@ LL | /// `ABes`
    |     ~~~~~~
 
 error: item in documentation is missing backticks
-  --> tests/ui/doc/doc-fixable.rs:243:9
+  --> tests/ui/doc/doc-fixable.rs:244:9
    |
 LL |     /// foo()
    |         ^^^^^
@@ -364,7 +364,7 @@ LL |     /// `foo()`
    |         ~~~~~~~
 
 error: you should put bare URLs between `<`/`>` or make a proper Markdown link
-  --> tests/ui/doc/doc-fixable.rs:247:5
+  --> tests/ui/doc/doc-fixable.rs:248:5
    |
 LL | /// https://github.com/rust-lang/rust-clippy/pull/12836
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `<https://github.com/rust-lang/rust-clippy/pull/12836>`
diff --git a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr
index c238b4c9a17..2852e26680f 100644
--- a/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr
+++ b/src/tools/clippy/tests/ui/empty_line_after/doc_comments.stderr
@@ -3,7 +3,7 @@ error: empty line after doc comment
    |
 LL | / /// for the crate
 LL | |
-   | |_
+   | |_^
 LL |   fn first_in_crate() {}
    |   ------------------- the comment documents this function
    |
@@ -22,7 +22,7 @@ error: empty line after doc comment
    |
 LL | /     /// for the module
 LL | |
-   | |_
+   | |_^
 LL |       fn first_in_module() {}
    |       -------------------- the comment documents this function
    |
@@ -39,7 +39,7 @@ error: empty line after doc comment
    |
 LL | /     /// # Indented
 LL | |
-   | |_
+   | |_^
 LL |       /// Blank line
 LL |       fn indented() {}
    |       ------------- the comment documents this function
@@ -55,7 +55,7 @@ error: empty line after doc comment
    |
 LL | / /// This should produce a warning
 LL | |
-   | |_
+   | |_^
 LL |   fn with_doc_and_newline() {}
    |   ------------------------- the comment documents this function
    |
@@ -69,7 +69,7 @@ LL | |
 LL | | /** This is also a doc comment and is part of the warning
 LL | |  */
 LL | |
-   | |_
+   | |_^
 ...
 LL |   fn three_attributes() {}
    |   --------------------- the comment documents this function
@@ -82,7 +82,7 @@ error: empty line after doc comment
 LL | /     /// docs for `old_code`
 LL | |     // fn old_code() {}
 LL | |
-   | |_
+   | |_^
 LL |       fn new_code() {}
    |       ------------- the comment documents this function
    |
@@ -102,7 +102,7 @@ LL | |     /// Docs
 LL | |     /// for OldB
 LL | |     // struct OldB;
 LL | |
-   | |_
+   | |_^
 ...
 LL |       struct Multiple;
    |       --------------- the comment documents this struct
@@ -125,7 +125,7 @@ LL | /     /**
 LL | |      * Meant to be inner doc comment
 LL | |      */
 LL | |
-   | |_
+   | |_^
 LL |       fn first_in_module() {}
    |       -------------------- the comment documents this function
    |
@@ -143,7 +143,7 @@ LL | |      * Docs for `old_code`
 LL | |      */
 LL | |     /* fn old_code() {} */
 LL | |
-   | |_
+   | |_^
 ...
 LL |       fn new_code() {}
    |       ------------- the comment documents this function
@@ -161,7 +161,7 @@ error: empty line after doc comment
 LL | /     /// Docs for `old_code2`
 LL | |     /* fn old_code2() {} */
 LL | |
-   | |_
+   | |_^
 LL |       /// Docs for `new_code2`
 LL |       fn new_code2() {}
    |       -------------- the comment documents this function
diff --git a/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.stderr b/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.stderr
index 958b40424a9..75fc23e9e7e 100644
--- a/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.stderr
+++ b/src/tools/clippy/tests/ui/empty_line_after/outer_attribute.stderr
@@ -3,7 +3,7 @@ error: empty line after outer attribute
    |
 LL | / #[crate_type = "lib"]
 LL | |
-   | |_
+   | |_^
 LL |   fn first_in_crate() {}
    |   ------------------- the attribute applies to this function
    |
@@ -20,7 +20,7 @@ error: empty line after outer attribute
    |
 LL | / #[inline]
 LL | |
-   | |_
+   | |_^
 LL |   /// some comment
 LL |   fn with_one_newline_and_comment() {}
    |   --------------------------------- the attribute applies to this function
@@ -32,7 +32,7 @@ error: empty line after outer attribute
    |
 LL | / #[inline]
 LL | |
-   | |_
+   | |_^
 LL |   fn with_one_newline() {}
    |   --------------------- the attribute applies to this function
    |
@@ -44,7 +44,7 @@ error: empty lines after outer attribute
 LL | /     #[crate_type = "lib"]
 LL | |
 LL | |
-   | |_
+   | |_^
 LL |       fn with_two_newlines() {}
    |       ---------------------- the attribute applies to this function
    |
@@ -59,7 +59,7 @@ error: empty line after outer attribute
    |
 LL | / #[doc = "doc attributes should be considered attributes"]
 LL | |
-   | |_
+   | |_^
 LL |   enum Baz {
    |   -------- the attribute applies to this enum
    |
@@ -70,7 +70,7 @@ error: empty line after outer attribute
    |
 LL | / #[repr(C)]
 LL | |
-   | |_
+   | |_^
 LL |   struct Foo {
    |   ---------- the attribute applies to this struct
    |
@@ -81,7 +81,7 @@ error: empty line after outer attribute
    |
 LL | / #[allow(dead_code)]
 LL | |
-   | |_
+   | |_^
 LL |   mod foo {}
    |   ------- the attribute applies to this module
    |
@@ -93,7 +93,7 @@ error: empty line after outer attribute
 LL | / #[inline]
 LL | | // Still lint cases where the empty line does not immediately follow the attribute
 LL | |
-   | |_
+   | |_^
 LL |   fn comment_before_empty_line() {}
    |   ------------------------------ the attribute applies to this function
    |
@@ -106,7 +106,7 @@ LL | / #[allow(unused)]
 LL | |
 LL | | // This comment is isolated
 LL | |
-   | |_
+   | |_^
 LL |   pub fn isolated_comment() {}
    |   ------------------------- the attribute applies to this function
    |
diff --git a/src/tools/clippy/tests/ui/four_forward_slashes.stderr b/src/tools/clippy/tests/ui/four_forward_slashes.stderr
index a2218ea4d10..3606a2227a0 100644
--- a/src/tools/clippy/tests/ui/four_forward_slashes.stderr
+++ b/src/tools/clippy/tests/ui/four_forward_slashes.stderr
@@ -3,7 +3,7 @@ error: this item has comments with 4 forward slashes (`////`). These look like d
    |
 LL | / //// whoops
 LL | | fn a() {}
-   | |_
+   | |_^
    |
    = note: `-D clippy::four-forward-slashes` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::four_forward_slashes)]`
@@ -18,7 +18,7 @@ error: this item has comments with 4 forward slashes (`////`). These look like d
 LL | / //// whoops
 LL | | #[allow(dead_code)]
 LL | | fn b() {}
-   | |_
+   | |_^
    |
 help: make this a doc comment by removing one `/`
    |
@@ -32,7 +32,7 @@ LL | / //// whoops
 LL | | //// two borked comments!
 LL | | #[track_caller]
 LL | | fn c() {}
-   | |_
+   | |_^
    |
 help: turn these into doc comments by removing one `/`
    |
@@ -46,7 +46,7 @@ error: this item has comments with 4 forward slashes (`////`). These look like d
 LL | / //// between attributes
 LL | | #[allow(dead_code)]
 LL | | fn g() {}
-   | |_
+   | |_^
    |
 help: make this a doc comment by removing one `/`
    |
@@ -58,7 +58,7 @@ error: this item has comments with 4 forward slashes (`////`). These look like d
    |
 LL | /     //// not very start of contents
 LL | | fn h() {}
-   | |_
+   | |_^
    |
 help: make this a doc comment by removing one `/`
    |
diff --git a/src/tools/clippy/tests/ui/four_forward_slashes_first_line.stderr b/src/tools/clippy/tests/ui/four_forward_slashes_first_line.stderr
index 23937034b7e..81732346412 100644
--- a/src/tools/clippy/tests/ui/four_forward_slashes_first_line.stderr
+++ b/src/tools/clippy/tests/ui/four_forward_slashes_first_line.stderr
@@ -3,7 +3,7 @@ error: this item has comments with 4 forward slashes (`////`). These look like d
    |
 LL | / //// borked doc comment on the first line. doesn't combust!
 LL | | fn a() {}
-   | |_
+   | |_^
    |
    = note: `-D clippy::four-forward-slashes` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::four_forward_slashes)]`
diff --git a/src/tools/clippy/tests/ui/identity_op.fixed b/src/tools/clippy/tests/ui/identity_op.fixed
index b18d8560f6f..2e8e2366de6 100644
--- a/src/tools/clippy/tests/ui/identity_op.fixed
+++ b/src/tools/clippy/tests/ui/identity_op.fixed
@@ -116,7 +116,7 @@ fn main() {
     //~^ ERROR: this operation has no effect
     (match a { 0 => 10, _ => 20 }) + if b { 3 } else { 4 };
     //~^ ERROR: this operation has no effect
-    (if b { 1 } else { 2 });
+    ((if b { 1 } else { 2 }));
     //~^ ERROR: this operation has no effect
 
     ({ a }) + 3;
@@ -212,3 +212,47 @@ fn issue_12050() {
         //~^ ERROR: this operation has no effect
     }
 }
+
+fn issue_13470() {
+    let x = 1i32;
+    let y = 1i32;
+    // Removes the + 0i32 while keeping the parentheses around x + y so the cast operation works
+    let _: u64 = (x + y) as u64;
+    //~^ ERROR: this operation has no effect
+    // both of the next two lines should look the same after rustfix
+    let _: u64 = 1u64 & (x + y) as u64;
+    //~^ ERROR: this operation has no effect
+    // Same as above, but with extra redundant parenthesis
+    let _: u64 = 1u64 & ((x + y)) as u64;
+    //~^ ERROR: this operation has no effect
+    // Should maintain parenthesis even if the surrounding expr has the same precedence
+    let _: u64 = 5u64 + ((x + y)) as u64;
+    //~^ ERROR: this operation has no effect
+
+    // If we don't maintain the parens here, the behavior changes
+    let _ = -(x + y);
+    //~^ ERROR: this operation has no effect
+    // Similarly, we need to maintain parens here
+    let _ = -(x / y);
+    //~^ ERROR: this operation has no effect
+    // Maintain parenthesis if the parent expr is of higher precedence
+    let _ = 2i32 * (x + y);
+    //~^ ERROR: this operation has no effect
+    // Maintain parenthesis if the parent expr is the same precedence
+    // as not all operations are associative
+    let _ = 2i32 - (x - y);
+    //~^ ERROR: this operation has no effect
+    // But make sure that inner parens still exist
+    let z = 1i32;
+    let _ = 2 + (x + (y * z));
+    //~^ ERROR: this operation has no effect
+    // Maintain parenthesis if the parent expr is of lower precedence
+    // This is for clarity, and clippy will not warn on these being unnecessary
+    let _ = 2i32 + (x * y);
+    //~^ ERROR: this operation has no effect
+
+    let x = 1i16;
+    let y = 1i16;
+    let _: u64 = 1u64 + ((x as i32 + y as i32) as u64);
+    //~^ ERROR: this operation has no effect
+}
diff --git a/src/tools/clippy/tests/ui/identity_op.rs b/src/tools/clippy/tests/ui/identity_op.rs
index f1f01b42447..3e20fa6f2b8 100644
--- a/src/tools/clippy/tests/ui/identity_op.rs
+++ b/src/tools/clippy/tests/ui/identity_op.rs
@@ -212,3 +212,47 @@ fn issue_12050() {
         //~^ ERROR: this operation has no effect
     }
 }
+
+fn issue_13470() {
+    let x = 1i32;
+    let y = 1i32;
+    // Removes the + 0i32 while keeping the parentheses around x + y so the cast operation works
+    let _: u64 = (x + y + 0i32) as u64;
+    //~^ ERROR: this operation has no effect
+    // both of the next two lines should look the same after rustfix
+    let _: u64 = 1u64 & (x + y + 0i32) as u64;
+    //~^ ERROR: this operation has no effect
+    // Same as above, but with extra redundant parenthesis
+    let _: u64 = 1u64 & ((x + y) + 0i32) as u64;
+    //~^ ERROR: this operation has no effect
+    // Should maintain parenthesis even if the surrounding expr has the same precedence
+    let _: u64 = 5u64 + ((x + y) + 0i32) as u64;
+    //~^ ERROR: this operation has no effect
+
+    // If we don't maintain the parens here, the behavior changes
+    let _ = -(x + y + 0i32);
+    //~^ ERROR: this operation has no effect
+    // Similarly, we need to maintain parens here
+    let _ = -(x / y / 1i32);
+    //~^ ERROR: this operation has no effect
+    // Maintain parenthesis if the parent expr is of higher precedence
+    let _ = 2i32 * (x + y + 0i32);
+    //~^ ERROR: this operation has no effect
+    // Maintain parenthesis if the parent expr is the same precedence
+    // as not all operations are associative
+    let _ = 2i32 - (x - y - 0i32);
+    //~^ ERROR: this operation has no effect
+    // But make sure that inner parens still exist
+    let z = 1i32;
+    let _ = 2 + (x + (y * z) + 0);
+    //~^ ERROR: this operation has no effect
+    // Maintain parenthesis if the parent expr is of lower precedence
+    // This is for clarity, and clippy will not warn on these being unnecessary
+    let _ = 2i32 + (x * y * 1i32);
+    //~^ ERROR: this operation has no effect
+
+    let x = 1i16;
+    let y = 1i16;
+    let _: u64 = 1u64 + ((x as i32 + y as i32) as u64 + 0u64);
+    //~^ ERROR: this operation has no effect
+}
diff --git a/src/tools/clippy/tests/ui/identity_op.stderr b/src/tools/clippy/tests/ui/identity_op.stderr
index 9fff86b86f9..99a58ef2c1b 100644
--- a/src/tools/clippy/tests/ui/identity_op.stderr
+++ b/src/tools/clippy/tests/ui/identity_op.stderr
@@ -149,7 +149,7 @@ error: this operation has no effect
   --> tests/ui/identity_op.rs:119:5
    |
 LL |     (if b { 1 } else { 2 }) + 0;
-   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(if b { 1 } else { 2 })`
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `((if b { 1 } else { 2 }))`
 
 error: this operation has no effect
   --> tests/ui/identity_op.rs:122:5
@@ -313,5 +313,71 @@ error: this operation has no effect
 LL |         let _: i32 = **&&*&x + 0;
    |                      ^^^^^^^^^^^ help: consider reducing it to: `***&&*&x`
 
-error: aborting due to 52 previous errors
+error: this operation has no effect
+  --> tests/ui/identity_op.rs:220:18
+   |
+LL |     let _: u64 = (x + y + 0i32) as u64;
+   |                  ^^^^^^^^^^^^^^ help: consider reducing it to: `(x + y)`
+
+error: this operation has no effect
+  --> tests/ui/identity_op.rs:223:25
+   |
+LL |     let _: u64 = 1u64 & (x + y + 0i32) as u64;
+   |                         ^^^^^^^^^^^^^^ help: consider reducing it to: `(x + y)`
+
+error: this operation has no effect
+  --> tests/ui/identity_op.rs:226:25
+   |
+LL |     let _: u64 = 1u64 & ((x + y) + 0i32) as u64;
+   |                         ^^^^^^^^^^^^^^^^ help: consider reducing it to: `((x + y))`
+
+error: this operation has no effect
+  --> tests/ui/identity_op.rs:229:25
+   |
+LL |     let _: u64 = 5u64 + ((x + y) + 0i32) as u64;
+   |                         ^^^^^^^^^^^^^^^^ help: consider reducing it to: `((x + y))`
+
+error: this operation has no effect
+  --> tests/ui/identity_op.rs:233:14
+   |
+LL |     let _ = -(x + y + 0i32);
+   |              ^^^^^^^^^^^^^^ help: consider reducing it to: `(x + y)`
+
+error: this operation has no effect
+  --> tests/ui/identity_op.rs:236:14
+   |
+LL |     let _ = -(x / y / 1i32);
+   |              ^^^^^^^^^^^^^^ help: consider reducing it to: `(x / y)`
+
+error: this operation has no effect
+  --> tests/ui/identity_op.rs:239:20
+   |
+LL |     let _ = 2i32 * (x + y + 0i32);
+   |                    ^^^^^^^^^^^^^^ help: consider reducing it to: `(x + y)`
+
+error: this operation has no effect
+  --> tests/ui/identity_op.rs:243:20
+   |
+LL |     let _ = 2i32 - (x - y - 0i32);
+   |                    ^^^^^^^^^^^^^^ help: consider reducing it to: `(x - y)`
+
+error: this operation has no effect
+  --> tests/ui/identity_op.rs:247:17
+   |
+LL |     let _ = 2 + (x + (y * z) + 0);
+   |                 ^^^^^^^^^^^^^^^^^ help: consider reducing it to: `(x + (y * z))`
+
+error: this operation has no effect
+  --> tests/ui/identity_op.rs:251:20
+   |
+LL |     let _ = 2i32 + (x * y * 1i32);
+   |                    ^^^^^^^^^^^^^^ help: consider reducing it to: `(x * y)`
+
+error: this operation has no effect
+  --> tests/ui/identity_op.rs:256:25
+   |
+LL |     let _: u64 = 1u64 + ((x as i32 + y as i32) as u64 + 0u64);
+   |                         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: consider reducing it to: `((x as i32 + y as i32) as u64)`
+
+error: aborting due to 63 previous errors
 
diff --git a/src/tools/clippy/tests/ui/infinite_loops.rs b/src/tools/clippy/tests/ui/infinite_loops.rs
index b6cb7ff49b0..0613eddce26 100644
--- a/src/tools/clippy/tests/ui/infinite_loops.rs
+++ b/src/tools/clippy/tests/ui/infinite_loops.rs
@@ -3,6 +3,7 @@
 
 #![allow(clippy::never_loop)]
 #![warn(clippy::infinite_loop)]
+#![feature(async_closure)]
 
 extern crate proc_macros;
 use proc_macros::{external, with_span};
@@ -428,4 +429,23 @@ fn continue_outer() {
     }
 }
 
+// don't suggest adding `-> !` to async fn/closure that already returning `-> !`
+mod issue_12338 {
+    use super::do_something;
+
+    async fn foo() -> ! {
+        loop {
+            do_something();
+        }
+    }
+
+    fn bar() {
+        let _ = async || -> ! {
+            loop {
+                do_something();
+            }
+        };
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/tests/ui/infinite_loops.stderr b/src/tools/clippy/tests/ui/infinite_loops.stderr
index 7635a7442f4..3a3ed7d0fe8 100644
--- a/src/tools/clippy/tests/ui/infinite_loops.stderr
+++ b/src/tools/clippy/tests/ui/infinite_loops.stderr
@@ -1,5 +1,5 @@
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:13:5
+  --> tests/ui/infinite_loops.rs:14:5
    |
 LL | /     loop {
 LL | |
@@ -15,7 +15,7 @@ LL | fn no_break() -> ! {
    |               ++++
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:20:5
+  --> tests/ui/infinite_loops.rs:21:5
    |
 LL | /     loop {
 LL | |
@@ -32,7 +32,7 @@ LL | fn all_inf() -> ! {
    |              ++++
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:22:9
+  --> tests/ui/infinite_loops.rs:23:9
    |
 LL | /         loop {
 LL | |
@@ -49,7 +49,7 @@ LL | fn all_inf() -> ! {
    |              ++++
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:24:13
+  --> tests/ui/infinite_loops.rs:25:13
    |
 LL | /             loop {
 LL | |
@@ -63,7 +63,7 @@ LL | fn all_inf() -> ! {
    |              ++++
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:38:5
+  --> tests/ui/infinite_loops.rs:39:5
    |
 LL | /     loop {
 LL | |
@@ -74,7 +74,7 @@ LL | |     }
    = help: if this is not intended, try adding a `break` or `return` condition in the loop
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:51:5
+  --> tests/ui/infinite_loops.rs:52:5
    |
 LL | /     loop {
 LL | |         fn inner_fn() -> ! {
@@ -90,7 +90,7 @@ LL | fn no_break_never_ret_noise() -> ! {
    |                               ++++
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:94:5
+  --> tests/ui/infinite_loops.rs:95:5
    |
 LL | /     loop {
 LL | |
@@ -107,7 +107,7 @@ LL | fn break_inner_but_not_outer_1(cond: bool) -> ! {
    |                                            ++++
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:105:5
+  --> tests/ui/infinite_loops.rs:106:5
    |
 LL | /     loop {
 LL | |
@@ -124,7 +124,7 @@ LL | fn break_inner_but_not_outer_2(cond: bool) -> ! {
    |                                            ++++
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:119:9
+  --> tests/ui/infinite_loops.rs:120:9
    |
 LL | /         loop {
 LL | |
@@ -138,7 +138,7 @@ LL | fn break_outer_but_not_inner() -> ! {
    |                                ++++
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:142:9
+  --> tests/ui/infinite_loops.rs:143:9
    |
 LL | /         loop {
 LL | |
@@ -155,7 +155,7 @@ LL | fn break_wrong_loop(cond: bool) -> ! {
    |                                 ++++
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:182:5
+  --> tests/ui/infinite_loops.rs:183:5
    |
 LL | /     loop {
 LL | |
@@ -172,7 +172,7 @@ LL | fn match_like() -> ! {
    |                 ++++
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:223:5
+  --> tests/ui/infinite_loops.rs:224:5
    |
 LL | /     loop {
 LL | |
@@ -186,7 +186,7 @@ LL | fn match_like() -> ! {
    |                 ++++
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:228:5
+  --> tests/ui/infinite_loops.rs:229:5
    |
 LL | /     loop {
 LL | |
@@ -203,7 +203,7 @@ LL | fn match_like() -> ! {
    |                 ++++
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:333:9
+  --> tests/ui/infinite_loops.rs:334:9
    |
 LL | /         loop {
 LL | |
@@ -217,7 +217,7 @@ LL |     fn problematic_trait_method() -> ! {
    |                                   ++++
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:343:9
+  --> tests/ui/infinite_loops.rs:344:9
    |
 LL | /         loop {
 LL | |
@@ -231,7 +231,7 @@ LL |     fn could_be_problematic() -> ! {
    |                               ++++
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:352:9
+  --> tests/ui/infinite_loops.rs:353:9
    |
 LL | /         loop {
 LL | |
@@ -245,7 +245,7 @@ LL |     let _loop_forever = || -> ! {
    |                            ++++
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:366:8
+  --> tests/ui/infinite_loops.rs:367:8
    |
 LL |       Ok(loop {
    |  ________^
@@ -256,7 +256,7 @@ LL | |     })
    = help: if this is not intended, try adding a `break` or `return` condition in the loop
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:408:5
+  --> tests/ui/infinite_loops.rs:409:5
    |
 LL | /     'infinite: loop {
 LL | |
@@ -272,7 +272,7 @@ LL | fn continue_outer() -> ! {
    |                     ++++
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:415:5
+  --> tests/ui/infinite_loops.rs:416:5
    |
 LL | /     loop {
 LL | |
@@ -289,7 +289,7 @@ LL | fn continue_outer() -> ! {
    |                     ++++
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:417:9
+  --> tests/ui/infinite_loops.rs:418:9
    |
 LL | /         'inner: loop {
 LL | |             loop {
@@ -304,7 +304,7 @@ LL | fn continue_outer() -> ! {
    |                     ++++
 
 error: infinite loop detected
-  --> tests/ui/infinite_loops.rs:425:5
+  --> tests/ui/infinite_loops.rs:426:5
    |
 LL | /     loop {
 LL | |
diff --git a/src/tools/clippy/tests/ui/iter_without_into_iter.stderr b/src/tools/clippy/tests/ui/iter_without_into_iter.stderr
index 7c42fa1dd89..d748c85003b 100644
--- a/src/tools/clippy/tests/ui/iter_without_into_iter.stderr
+++ b/src/tools/clippy/tests/ui/iter_without_into_iter.stderr
@@ -13,8 +13,8 @@ help: consider implementing `IntoIterator` for `&S1`
    |
 LL + 
 LL + impl IntoIterator for &S1 {
-LL +     type IntoIter = std::slice::Iter<'_, u8>;
 LL +     type Item = &u8;
+LL +     type IntoIter = std::slice::Iter<'_, u8>;
 LL +     fn into_iter(self) -> Self::IntoIter {
 LL +         self.iter()
 LL +     }
@@ -34,8 +34,8 @@ help: consider implementing `IntoIterator` for `&mut S1`
    |
 LL + 
 LL + impl IntoIterator for &mut S1 {
-LL +     type IntoIter = std::slice::IterMut<'_, u8>;
 LL +     type Item = &mut u8;
+LL +     type IntoIter = std::slice::IterMut<'_, u8>;
 LL +     fn into_iter(self) -> Self::IntoIter {
 LL +         self.iter()
 LL +     }
@@ -55,8 +55,8 @@ help: consider implementing `IntoIterator` for `&S3<'a>`
    |
 LL + 
 LL + impl IntoIterator for &S3<'a> {
-LL +     type IntoIter = std::slice::Iter<'_, u8>;
 LL +     type Item = &u8;
+LL +     type IntoIter = std::slice::Iter<'_, u8>;
 LL +     fn into_iter(self) -> Self::IntoIter {
 LL +         self.iter()
 LL +     }
@@ -76,8 +76,8 @@ help: consider implementing `IntoIterator` for `&mut S3<'a>`
    |
 LL + 
 LL + impl IntoIterator for &mut S3<'a> {
-LL +     type IntoIter = std::slice::IterMut<'_, u8>;
 LL +     type Item = &mut u8;
+LL +     type IntoIter = std::slice::IterMut<'_, u8>;
 LL +     fn into_iter(self) -> Self::IntoIter {
 LL +         self.iter()
 LL +     }
@@ -96,8 +96,8 @@ help: consider implementing `IntoIterator` for `&S8<T>`
    |
 LL + 
 LL + impl IntoIterator for &S8<T> {
-LL +     type IntoIter = std::slice::Iter<'static, T>;
 LL +     type Item = &T;
+LL +     type IntoIter = std::slice::Iter<'static, T>;
 LL +     fn into_iter(self) -> Self::IntoIter {
 LL +         self.iter()
 LL +     }
@@ -117,8 +117,8 @@ help: consider implementing `IntoIterator` for `&S9<T>`
    |
 LL + 
 LL + impl IntoIterator for &S9<T> {
-LL +     type IntoIter = std::slice::Iter<'_, T>;
 LL +     type Item = &T;
+LL +     type IntoIter = std::slice::Iter<'_, T>;
 LL +     fn into_iter(self) -> Self::IntoIter {
 LL +         self.iter()
 LL +     }
@@ -138,8 +138,8 @@ help: consider implementing `IntoIterator` for `&mut S9<T>`
    |
 LL + 
 LL + impl IntoIterator for &mut S9<T> {
-LL +     type IntoIter = std::slice::IterMut<'_, T>;
 LL +     type Item = &mut T;
+LL +     type IntoIter = std::slice::IterMut<'_, T>;
 LL +     fn into_iter(self) -> Self::IntoIter {
 LL +         self.iter()
 LL +     }
@@ -162,8 +162,8 @@ help: consider implementing `IntoIterator` for `&Issue12037`
    |
 LL ~         
 LL + impl IntoIterator for &Issue12037 {
-LL +     type IntoIter = std::slice::Iter<'_, u8>;
 LL +     type Item = &u8;
+LL +     type IntoIter = std::slice::Iter<'_, u8>;
 LL +     fn into_iter(self) -> Self::IntoIter {
 LL +         self.iter()
 LL +     }
diff --git a/src/tools/clippy/tests/ui/large_const_arrays.fixed b/src/tools/clippy/tests/ui/large_const_arrays.fixed
index 543ce460e7b..e5b28cb6a9d 100644
--- a/src/tools/clippy/tests/ui/large_const_arrays.fixed
+++ b/src/tools/clippy/tests/ui/large_const_arrays.fixed
@@ -9,11 +9,13 @@ pub struct S {
 // Should lint
 pub(crate) static FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000];
 pub static FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
+static FOO_COMPUTED: [u32; 1_000 * 100] = [0u32; 1_000 * 100];
 static FOO: [u32; 1_000_000] = [0u32; 1_000_000];
 
 // Good
 pub(crate) const G_FOO_PUB_CRATE: [u32; 250] = [0u32; 250];
 pub const G_FOO_PUB: [u32; 250] = [0u32; 250];
+const G_FOO_COMPUTED: [u32; 25 * 10] = [0u32; 25 * 10];
 const G_FOO: [u32; 250] = [0u32; 250];
 
 fn main() {
diff --git a/src/tools/clippy/tests/ui/large_const_arrays.rs b/src/tools/clippy/tests/ui/large_const_arrays.rs
index e23a8081171..b9593225da2 100644
--- a/src/tools/clippy/tests/ui/large_const_arrays.rs
+++ b/src/tools/clippy/tests/ui/large_const_arrays.rs
@@ -9,11 +9,13 @@ pub struct S {
 // Should lint
 pub(crate) const FOO_PUB_CRATE: [u32; 1_000_000] = [0u32; 1_000_000];
 pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
+const FOO_COMPUTED: [u32; 1_000 * 100] = [0u32; 1_000 * 100];
 const FOO: [u32; 1_000_000] = [0u32; 1_000_000];
 
 // Good
 pub(crate) const G_FOO_PUB_CRATE: [u32; 250] = [0u32; 250];
 pub const G_FOO_PUB: [u32; 250] = [0u32; 250];
+const G_FOO_COMPUTED: [u32; 25 * 10] = [0u32; 25 * 10];
 const G_FOO: [u32; 250] = [0u32; 250];
 
 fn main() {
diff --git a/src/tools/clippy/tests/ui/large_const_arrays.stderr b/src/tools/clippy/tests/ui/large_const_arrays.stderr
index 88f921b81f7..27112205390 100644
--- a/src/tools/clippy/tests/ui/large_const_arrays.stderr
+++ b/src/tools/clippy/tests/ui/large_const_arrays.stderr
@@ -20,13 +20,21 @@ LL | pub const FOO_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
 error: large array defined as const
   --> tests/ui/large_const_arrays.rs:12:1
    |
+LL | const FOO_COMPUTED: [u32; 1_000 * 100] = [0u32; 1_000 * 100];
+   | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   | |
+   | help: make this a static item: `static`
+
+error: large array defined as const
+  --> tests/ui/large_const_arrays.rs:13:1
+   |
 LL | const FOO: [u32; 1_000_000] = [0u32; 1_000_000];
    | -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    | |
    | help: make this a static item: `static`
 
 error: large array defined as const
-  --> tests/ui/large_const_arrays.rs:21:5
+  --> tests/ui/large_const_arrays.rs:23:5
    |
 LL |     pub const BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
    |     ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -34,7 +42,7 @@ LL |     pub const BAR_PUB: [u32; 1_000_000] = [0u32; 1_000_000];
    |         help: make this a static item: `static`
 
 error: large array defined as const
-  --> tests/ui/large_const_arrays.rs:22:5
+  --> tests/ui/large_const_arrays.rs:24:5
    |
 LL |     const BAR: [u32; 1_000_000] = [0u32; 1_000_000];
    |     -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -42,7 +50,7 @@ LL |     const BAR: [u32; 1_000_000] = [0u32; 1_000_000];
    |     help: make this a static item: `static`
 
 error: large array defined as const
-  --> tests/ui/large_const_arrays.rs:23:5
+  --> tests/ui/large_const_arrays.rs:25:5
    |
 LL |     pub const BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000];
    |     ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -50,7 +58,7 @@ LL |     pub const BAR_STRUCT_PUB: [S; 5_000] = [S { data: [0; 32] }; 5_000];
    |         help: make this a static item: `static`
 
 error: large array defined as const
-  --> tests/ui/large_const_arrays.rs:24:5
+  --> tests/ui/large_const_arrays.rs:26:5
    |
 LL |     const BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000];
    |     -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -58,7 +66,7 @@ LL |     const BAR_STRUCT: [S; 5_000] = [S { data: [0; 32] }; 5_000];
    |     help: make this a static item: `static`
 
 error: large array defined as const
-  --> tests/ui/large_const_arrays.rs:25:5
+  --> tests/ui/large_const_arrays.rs:27:5
    |
 LL |     pub const BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000];
    |     ^^^^-----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -66,12 +74,12 @@ LL |     pub const BAR_S_PUB: [Option<&str>; 200_000] = [Some("str"); 200_000];
    |         help: make this a static item: `static`
 
 error: large array defined as const
-  --> tests/ui/large_const_arrays.rs:26:5
+  --> tests/ui/large_const_arrays.rs:28:5
    |
 LL |     const BAR_S: [Option<&str>; 200_000] = [Some("str"); 200_000];
    |     -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |     |
    |     help: make this a static item: `static`
 
-error: aborting due to 9 previous errors
+error: aborting due to 10 previous errors
 
diff --git a/src/tools/clippy/tests/ui/manual_bits.fixed b/src/tools/clippy/tests/ui/manual_bits.fixed
index 4de01905e8a..8e5cb7d38b9 100644
--- a/src/tools/clippy/tests/ui/manual_bits.fixed
+++ b/src/tools/clippy/tests/ui/manual_bits.fixed
@@ -56,3 +56,14 @@ fn main() {
     let _ = (u128::BITS as usize).pow(5);
     let _ = &(u128::BITS as usize);
 }
+
+fn should_not_lint() {
+    macro_rules! bits_via_macro {
+        ($T: ty) => {
+            size_of::<$T>() * 8;
+        };
+    }
+
+    bits_via_macro!(u8);
+    bits_via_macro!(String);
+}
diff --git a/src/tools/clippy/tests/ui/manual_bits.rs b/src/tools/clippy/tests/ui/manual_bits.rs
index d4f369fcf87..5e492ed15d2 100644
--- a/src/tools/clippy/tests/ui/manual_bits.rs
+++ b/src/tools/clippy/tests/ui/manual_bits.rs
@@ -56,3 +56,14 @@ fn main() {
     let _ = (size_of::<u128>() * 8).pow(5);
     let _ = &(size_of::<u128>() * 8);
 }
+
+fn should_not_lint() {
+    macro_rules! bits_via_macro {
+        ($T: ty) => {
+            size_of::<$T>() * 8;
+        };
+    }
+
+    bits_via_macro!(u8);
+    bits_via_macro!(String);
+}
diff --git a/src/tools/clippy/tests/ui/map_all_any_identity.fixed b/src/tools/clippy/tests/ui/map_all_any_identity.fixed
new file mode 100644
index 00000000000..c92462ab9c2
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_all_any_identity.fixed
@@ -0,0 +1,21 @@
+#![warn(clippy::map_all_any_identity)]
+
+fn main() {
+    let _ = ["foo"].into_iter().any(|s| s == "foo");
+    //~^ map_all_any_identity
+    let _ = ["foo"].into_iter().all(|s| s == "foo");
+    //~^ map_all_any_identity
+
+    //
+    // Do not lint
+    //
+    // Not identity
+    let _ = ["foo"].into_iter().map(|s| s.len()).any(|n| n > 0);
+    // Macro
+    macro_rules! map {
+        ($x:expr) => {
+            $x.into_iter().map(|s| s == "foo")
+        };
+    }
+    map!(["foo"]).any(|a| a);
+}
diff --git a/src/tools/clippy/tests/ui/map_all_any_identity.rs b/src/tools/clippy/tests/ui/map_all_any_identity.rs
new file mode 100644
index 00000000000..0e4a564ca04
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_all_any_identity.rs
@@ -0,0 +1,21 @@
+#![warn(clippy::map_all_any_identity)]
+
+fn main() {
+    let _ = ["foo"].into_iter().map(|s| s == "foo").any(|a| a);
+    //~^ map_all_any_identity
+    let _ = ["foo"].into_iter().map(|s| s == "foo").all(std::convert::identity);
+    //~^ map_all_any_identity
+
+    //
+    // Do not lint
+    //
+    // Not identity
+    let _ = ["foo"].into_iter().map(|s| s.len()).any(|n| n > 0);
+    // Macro
+    macro_rules! map {
+        ($x:expr) => {
+            $x.into_iter().map(|s| s == "foo")
+        };
+    }
+    map!(["foo"]).any(|a| a);
+}
diff --git a/src/tools/clippy/tests/ui/map_all_any_identity.stderr b/src/tools/clippy/tests/ui/map_all_any_identity.stderr
new file mode 100644
index 00000000000..98fdcc2a939
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_all_any_identity.stderr
@@ -0,0 +1,26 @@
+error: usage of `.map(...).any(identity)`
+  --> tests/ui/map_all_any_identity.rs:4:33
+   |
+LL |     let _ = ["foo"].into_iter().map(|s| s == "foo").any(|a| a);
+   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::map-all-any-identity` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::map_all_any_identity)]`
+help: use `.any(...)` instead
+   |
+LL |     let _ = ["foo"].into_iter().any(|s| s == "foo");
+   |                                 ~~~~~~~~~~~~~~~~~~~
+
+error: usage of `.map(...).all(identity)`
+  --> tests/ui/map_all_any_identity.rs:6:33
+   |
+LL |     let _ = ["foo"].into_iter().map(|s| s == "foo").all(std::convert::identity);
+   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: use `.all(...)` instead
+   |
+LL |     let _ = ["foo"].into_iter().all(|s| s == "foo");
+   |                                 ~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 2 previous errors
+
diff --git a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.fixed b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.fixed
new file mode 100644
index 00000000000..cf520e71a64
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.fixed
@@ -0,0 +1,73 @@
+#![allow(
+    unused,
+    clippy::redundant_closure,
+    clippy::reversed_empty_ranges,
+    clippy::identity_op
+)]
+#![warn(clippy::map_with_unused_argument_over_ranges)]
+
+fn do_something() -> usize {
+    todo!()
+}
+
+fn do_something_interesting(x: usize, y: usize) -> usize {
+    todo!()
+}
+
+macro_rules! gen {
+    () => {
+        (0..10).map(|_| do_something());
+    };
+}
+
+fn main() {
+    // These should be raised
+    std::iter::repeat_with(|| do_something()).take(10);
+    std::iter::repeat_with(|| do_something()).take(10);
+    std::iter::repeat_with(|| do_something()).take(11);
+    std::iter::repeat_with(|| do_something()).take(7);
+    std::iter::repeat_with(|| do_something()).take(8);
+    std::iter::repeat_n(3, 10);
+    std::iter::repeat_with(|| {
+        let x = 3;
+        x + 2
+    }).take(10);
+    std::iter::repeat_with(|| do_something()).take(10).collect::<Vec<_>>();
+    let upper = 4;
+    std::iter::repeat_with(|| do_something()).take(upper);
+    let upper_fn = || 4;
+    std::iter::repeat_with(|| do_something()).take(upper_fn());
+    std::iter::repeat_with(|| do_something()).take(upper_fn() + 1);
+    std::iter::repeat_with(|| do_something()).take(upper_fn() - 2);
+    std::iter::repeat_with(|| do_something()).take(upper_fn() - 1);
+    (-3..9).map(|_| do_something());
+    std::iter::repeat_with(|| do_something()).take(0);
+    std::iter::repeat_with(|| do_something()).take(1);
+    std::iter::repeat_with(|| do_something()).take((1 << 4) - 0);
+    // These should not be raised
+    gen!();
+    let lower = 2;
+    let lower_fn = || 2;
+    (lower..upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled
+    (lower_fn()..upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled
+    (lower_fn()..=upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled
+    (0..10).map(|x| do_something_interesting(x, 4)); // Actual map over range
+    "Foobar".chars().map(|_| do_something()); // Not a map over range
+    // i128::MAX == 340282366920938463463374607431768211455
+    (0..=340282366920938463463374607431768211455).map(|_: u128| do_something()); // Can't be replaced due to overflow
+}
+
+#[clippy::msrv = "1.27"]
+fn msrv_1_27() {
+    (0..10).map(|_| do_something());
+}
+
+#[clippy::msrv = "1.28"]
+fn msrv_1_28() {
+    std::iter::repeat_with(|| do_something()).take(10);
+}
+
+#[clippy::msrv = "1.81"]
+fn msrv_1_82() {
+    std::iter::repeat(3).take(10);
+}
diff --git a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.rs b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.rs
new file mode 100644
index 00000000000..298eee9ca3f
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.rs
@@ -0,0 +1,73 @@
+#![allow(
+    unused,
+    clippy::redundant_closure,
+    clippy::reversed_empty_ranges,
+    clippy::identity_op
+)]
+#![warn(clippy::map_with_unused_argument_over_ranges)]
+
+fn do_something() -> usize {
+    todo!()
+}
+
+fn do_something_interesting(x: usize, y: usize) -> usize {
+    todo!()
+}
+
+macro_rules! gen {
+    () => {
+        (0..10).map(|_| do_something());
+    };
+}
+
+fn main() {
+    // These should be raised
+    (0..10).map(|_| do_something());
+    (0..10).map(|_foo| do_something());
+    (0..=10).map(|_| do_something());
+    (3..10).map(|_| do_something());
+    (3..=10).map(|_| do_something());
+    (0..10).map(|_| 3);
+    (0..10).map(|_| {
+        let x = 3;
+        x + 2
+    });
+    (0..10).map(|_| do_something()).collect::<Vec<_>>();
+    let upper = 4;
+    (0..upper).map(|_| do_something());
+    let upper_fn = || 4;
+    (0..upper_fn()).map(|_| do_something());
+    (0..=upper_fn()).map(|_| do_something());
+    (2..upper_fn()).map(|_| do_something());
+    (2..=upper_fn()).map(|_| do_something());
+    (-3..9).map(|_| do_something());
+    (9..3).map(|_| do_something());
+    (9..=9).map(|_| do_something());
+    (1..=1 << 4).map(|_| do_something());
+    // These should not be raised
+    gen!();
+    let lower = 2;
+    let lower_fn = || 2;
+    (lower..upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled
+    (lower_fn()..upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled
+    (lower_fn()..=upper_fn()).map(|_| do_something()); // Ranges not starting at zero not yet handled
+    (0..10).map(|x| do_something_interesting(x, 4)); // Actual map over range
+    "Foobar".chars().map(|_| do_something()); // Not a map over range
+    // i128::MAX == 340282366920938463463374607431768211455
+    (0..=340282366920938463463374607431768211455).map(|_: u128| do_something()); // Can't be replaced due to overflow
+}
+
+#[clippy::msrv = "1.27"]
+fn msrv_1_27() {
+    (0..10).map(|_| do_something());
+}
+
+#[clippy::msrv = "1.28"]
+fn msrv_1_28() {
+    (0..10).map(|_| do_something());
+}
+
+#[clippy::msrv = "1.81"]
+fn msrv_1_82() {
+    (0..10).map(|_| 3);
+}
diff --git a/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.stderr b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.stderr
new file mode 100644
index 00000000000..0b56c6d9521
--- /dev/null
+++ b/src/tools/clippy/tests/ui/map_with_unused_argument_over_ranges.stderr
@@ -0,0 +1,223 @@
+error: map of a closure that does not depend on its parameter over a range
+  --> tests/ui/map_with_unused_argument_over_ranges.rs:25:5
+   |
+LL |     (0..10).map(|_| do_something());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::map-with-unused-argument-over-ranges` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::map_with_unused_argument_over_ranges)]`
+help: remove the explicit range and use `repeat_with` and `take`
+   |
+LL -     (0..10).map(|_| do_something());
+LL +     std::iter::repeat_with(|| do_something()).take(10);
+   |
+
+error: map of a closure that does not depend on its parameter over a range
+  --> tests/ui/map_with_unused_argument_over_ranges.rs:26:5
+   |
+LL |     (0..10).map(|_foo| do_something());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the explicit range and use `repeat_with` and `take`
+   |
+LL -     (0..10).map(|_foo| do_something());
+LL +     std::iter::repeat_with(|| do_something()).take(10);
+   |
+
+error: map of a closure that does not depend on its parameter over a range
+  --> tests/ui/map_with_unused_argument_over_ranges.rs:27:5
+   |
+LL |     (0..=10).map(|_| do_something());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the explicit range and use `repeat_with` and `take`
+   |
+LL -     (0..=10).map(|_| do_something());
+LL +     std::iter::repeat_with(|| do_something()).take(11);
+   |
+
+error: map of a closure that does not depend on its parameter over a range
+  --> tests/ui/map_with_unused_argument_over_ranges.rs:28:5
+   |
+LL |     (3..10).map(|_| do_something());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the explicit range and use `repeat_with` and `take`
+   |
+LL -     (3..10).map(|_| do_something());
+LL +     std::iter::repeat_with(|| do_something()).take(7);
+   |
+
+error: map of a closure that does not depend on its parameter over a range
+  --> tests/ui/map_with_unused_argument_over_ranges.rs:29:5
+   |
+LL |     (3..=10).map(|_| do_something());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the explicit range and use `repeat_with` and `take`
+   |
+LL -     (3..=10).map(|_| do_something());
+LL +     std::iter::repeat_with(|| do_something()).take(8);
+   |
+
+error: map of a closure that does not depend on its parameter over a range
+  --> tests/ui/map_with_unused_argument_over_ranges.rs:30:5
+   |
+LL |     (0..10).map(|_| 3);
+   |     ^^^^^^^^^^^^^^^^^^
+   |
+help: remove the explicit range and use `repeat_n`
+   |
+LL |     std::iter::repeat_n(3, 10);
+   |     ~~~~~~~~~~~~~~~~~~~ ~~~~~
+
+error: map of a closure that does not depend on its parameter over a range
+  --> tests/ui/map_with_unused_argument_over_ranges.rs:31:5
+   |
+LL | /     (0..10).map(|_| {
+LL | |         let x = 3;
+LL | |         x + 2
+LL | |     });
+   | |______^
+   |
+help: remove the explicit range and use `repeat_with` and `take`
+   |
+LL ~     std::iter::repeat_with(|| {
+LL |         let x = 3;
+LL |         x + 2
+LL ~     }).take(10);
+   |
+
+error: map of a closure that does not depend on its parameter over a range
+  --> tests/ui/map_with_unused_argument_over_ranges.rs:35:5
+   |
+LL |     (0..10).map(|_| do_something()).collect::<Vec<_>>();
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the explicit range and use `repeat_with` and `take`
+   |
+LL -     (0..10).map(|_| do_something()).collect::<Vec<_>>();
+LL +     std::iter::repeat_with(|| do_something()).take(10).collect::<Vec<_>>();
+   |
+
+error: map of a closure that does not depend on its parameter over a range
+  --> tests/ui/map_with_unused_argument_over_ranges.rs:37:5
+   |
+LL |     (0..upper).map(|_| do_something());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the explicit range and use `repeat_with` and `take`
+   |
+LL -     (0..upper).map(|_| do_something());
+LL +     std::iter::repeat_with(|| do_something()).take(upper);
+   |
+
+error: map of a closure that does not depend on its parameter over a range
+  --> tests/ui/map_with_unused_argument_over_ranges.rs:39:5
+   |
+LL |     (0..upper_fn()).map(|_| do_something());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the explicit range and use `repeat_with` and `take`
+   |
+LL -     (0..upper_fn()).map(|_| do_something());
+LL +     std::iter::repeat_with(|| do_something()).take(upper_fn());
+   |
+
+error: map of a closure that does not depend on its parameter over a range
+  --> tests/ui/map_with_unused_argument_over_ranges.rs:40:5
+   |
+LL |     (0..=upper_fn()).map(|_| do_something());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the explicit range and use `repeat_with` and `take`
+   |
+LL -     (0..=upper_fn()).map(|_| do_something());
+LL +     std::iter::repeat_with(|| do_something()).take(upper_fn() + 1);
+   |
+
+error: map of a closure that does not depend on its parameter over a range
+  --> tests/ui/map_with_unused_argument_over_ranges.rs:41:5
+   |
+LL |     (2..upper_fn()).map(|_| do_something());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the explicit range and use `repeat_with` and `take`
+   |
+LL -     (2..upper_fn()).map(|_| do_something());
+LL +     std::iter::repeat_with(|| do_something()).take(upper_fn() - 2);
+   |
+
+error: map of a closure that does not depend on its parameter over a range
+  --> tests/ui/map_with_unused_argument_over_ranges.rs:42:5
+   |
+LL |     (2..=upper_fn()).map(|_| do_something());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the explicit range and use `repeat_with` and `take`
+   |
+LL -     (2..=upper_fn()).map(|_| do_something());
+LL +     std::iter::repeat_with(|| do_something()).take(upper_fn() - 1);
+   |
+
+error: map of a closure that does not depend on its parameter over a range
+  --> tests/ui/map_with_unused_argument_over_ranges.rs:44:5
+   |
+LL |     (9..3).map(|_| do_something());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the explicit range and use `repeat_with` and `take`
+   |
+LL -     (9..3).map(|_| do_something());
+LL +     std::iter::repeat_with(|| do_something()).take(0);
+   |
+
+error: map of a closure that does not depend on its parameter over a range
+  --> tests/ui/map_with_unused_argument_over_ranges.rs:45:5
+   |
+LL |     (9..=9).map(|_| do_something());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the explicit range and use `repeat_with` and `take`
+   |
+LL -     (9..=9).map(|_| do_something());
+LL +     std::iter::repeat_with(|| do_something()).take(1);
+   |
+
+error: map of a closure that does not depend on its parameter over a range
+  --> tests/ui/map_with_unused_argument_over_ranges.rs:46:5
+   |
+LL |     (1..=1 << 4).map(|_| do_something());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the explicit range and use `repeat_with` and `take`
+   |
+LL -     (1..=1 << 4).map(|_| do_something());
+LL +     std::iter::repeat_with(|| do_something()).take((1 << 4) - 0);
+   |
+
+error: map of a closure that does not depend on its parameter over a range
+  --> tests/ui/map_with_unused_argument_over_ranges.rs:67:5
+   |
+LL |     (0..10).map(|_| do_something());
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: remove the explicit range and use `repeat_with` and `take`
+   |
+LL -     (0..10).map(|_| do_something());
+LL +     std::iter::repeat_with(|| do_something()).take(10);
+   |
+
+error: map of a closure that does not depend on its parameter over a range
+  --> tests/ui/map_with_unused_argument_over_ranges.rs:72:5
+   |
+LL |     (0..10).map(|_| 3);
+   |     ^^^^^^^^^^^^^^^^^^
+   |
+help: remove the explicit range and use `repeat` and `take`
+   |
+LL |     std::iter::repeat(3).take(10);
+   |     ~~~~~~~~~~~~~~~~~ ~ +++++++++
+
+error: aborting due to 18 previous errors
+
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 f7b6e1a186b..754fe061c4a 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
@@ -2,7 +2,6 @@
 #![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)]
 #![feature(const_trait_impl, abi_vectorcall)]
 
-
 use std::mem::transmute;
 
 struct Game {
@@ -104,7 +103,7 @@ fn main() {}
 
 struct D;
 
-/* FIXME(effects)
+/* FIXME(const_trait_impl)
 impl const Drop for D {
     fn drop(&mut self) {
         todo!();
@@ -113,7 +112,7 @@ impl const Drop for D {
 */
 
 // Lint this, since it can be dropped in const contexts
-// FIXME(effects)
+// FIXME(const_trait_impl)
 const fn d(this: D) {}
 //~^ ERROR: this could be a `const fn`
 
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 4866e321024..460be0733e0 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
@@ -2,7 +2,6 @@
 #![allow(incomplete_features, clippy::let_and_return, clippy::missing_transmute_annotations)]
 #![feature(const_trait_impl, abi_vectorcall)]
 
-
 use std::mem::transmute;
 
 struct Game {
@@ -104,7 +103,7 @@ fn main() {}
 
 struct D;
 
-/* FIXME(effects)
+/* FIXME(const_trait_impl)
 impl const Drop for D {
     fn drop(&mut self) {
         todo!();
@@ -113,7 +112,7 @@ impl const Drop for D {
 */
 
 // Lint this, since it can be dropped in const contexts
-// FIXME(effects)
+// FIXME(const_trait_impl)
 fn d(this: D) {}
 //~^ ERROR: this could be a `const fn`
 
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 f28dec5c7f1..d553c522556 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
@@ -1,5 +1,5 @@
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:14:5
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:13:5
    |
 LL | /     pub fn new() -> Self {
 LL | |
@@ -16,7 +16,7 @@ LL |     pub const fn new() -> Self {
    |         +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:20:5
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:19:5
    |
 LL | /     fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T; N]) -> &'a [T; N] {
 LL | |
@@ -30,7 +30,7 @@ LL |     const fn const_generic_params<'a, T, const N: usize>(&self, b: &'a [T;
    |     +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:27:1
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:26:1
    |
 LL | / fn one() -> i32 {
 LL | |
@@ -44,7 +44,7 @@ LL | const fn one() -> i32 {
    | +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:33:1
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:32:1
    |
 LL | / fn two() -> i32 {
 LL | |
@@ -59,7 +59,7 @@ LL | const fn two() -> i32 {
    | +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:40:1
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:39:1
    |
 LL | / fn string() -> String {
 LL | |
@@ -73,7 +73,7 @@ LL | const fn string() -> String {
    | +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:46:1
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:45:1
    |
 LL | / unsafe fn four() -> i32 {
 LL | |
@@ -87,7 +87,7 @@ LL | const unsafe fn four() -> i32 {
    | +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:52:1
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:51:1
    |
 LL | / fn generic<T>(t: T) -> T {
 LL | |
@@ -101,7 +101,7 @@ LL | const fn generic<T>(t: T) -> T {
    | +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:61:1
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:60:1
    |
 LL | / fn generic_arr<T: Copy>(t: [T; 1]) -> T {
 LL | |
@@ -115,7 +115,7 @@ LL | const fn generic_arr<T: Copy>(t: [T; 1]) -> T {
    | +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:75:9
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:74:9
    |
 LL | /         pub fn b(self, a: &A) -> B {
 LL | |
@@ -129,7 +129,7 @@ LL |         pub const fn b(self, a: &A) -> B {
    |             +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:85:5
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:84:5
    |
 LL | /     fn const_fn_stabilized_before_msrv(byte: u8) {
 LL | |
@@ -143,7 +143,7 @@ LL |     const fn const_fn_stabilized_before_msrv(byte: u8) {
    |     +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:97:1
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:96:1
    |
 LL | / fn msrv_1_46() -> i32 {
 LL | |
@@ -157,7 +157,7 @@ LL | const fn msrv_1_46() -> i32 {
    | +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:117:1
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:116:1
    |
 LL | fn d(this: D) {}
    | ^^^^^^^^^^^^^^^^
@@ -168,7 +168,7 @@ LL | const fn d(this: D) {}
    | +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:125:9
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:124:9
    |
 LL | /         fn deref_ptr_can_be_const(self) -> usize {
 LL | |
@@ -182,7 +182,7 @@ LL |         const fn deref_ptr_can_be_const(self) -> usize {
    |         +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:130:9
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:129:9
    |
 LL | /         fn deref_copied_val(self) -> usize {
 LL | |
@@ -196,7 +196,7 @@ LL |         const fn deref_copied_val(self) -> usize {
    |         +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:141:5
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:140:5
    |
 LL | /     fn union_access_can_be_const() {
 LL | |
@@ -211,7 +211,7 @@ 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:149:9
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:148:9
    |
 LL |         extern "C" fn c() {}
    |         ^^^^^^^^^^^^^^^^^^^^
@@ -222,7 +222,7 @@ 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
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:152:9
    |
 LL |         extern fn implicit_c() {}
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -233,7 +233,7 @@ 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
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:169:9
    |
 LL | /         pub fn new(strings: Vec<String>) -> Self {
 LL | |             Self { strings }
@@ -246,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:175:9
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:174:9
    |
 LL | /         pub fn empty() -> Self {
 LL | |             Self { strings: Vec::new() }
@@ -259,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:186:9
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:185:9
    |
 LL | /         pub fn new(text: String) -> Self {
 LL | |             let vec = Vec::new();
@@ -273,7 +273,7 @@ LL |         pub const fn new(text: String) -> Self {
    |             +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:205:5
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:204:5
    |
 LL |     fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -284,7 +284,7 @@ LL |     const fn alias_ty_is_projection(bar: <() as FooTrait>::Foo) {}
    |     +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:209:5
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:208:5
    |
 LL |     extern "C-unwind" fn c_unwind() {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -295,7 +295,7 @@ LL |     const extern "C-unwind" fn c_unwind() {}
    |     +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:211:5
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:210:5
    |
 LL |     extern "system" fn system() {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -306,7 +306,7 @@ LL |     const extern "system" fn system() {}
    |     +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:213:5
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:212:5
    |
 LL |     extern "system-unwind" fn system_unwind() {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -317,7 +317,7 @@ LL |     const extern "system-unwind" fn system_unwind() {}
    |     +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:215:5
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:214:5
    |
 LL |     pub extern "vectorcall" fn std_call() {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -328,7 +328,7 @@ LL |     pub const extern "vectorcall" fn std_call() {}
    |         +++++
 
 error: this could be a `const fn`
-  --> tests/ui/missing_const_for_fn/could_be_const.rs:217:5
+  --> tests/ui/missing_const_for_fn/could_be_const.rs:216:5
    |
 LL |     pub extern "vectorcall-unwind" fn std_call_unwind() {}
    |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/missing_doc.rs b/src/tools/clippy/tests/ui/missing_doc.rs
index 9c936d7fa23..31bc4309582 100644
--- a/src/tools/clippy/tests/ui/missing_doc.rs
+++ b/src/tools/clippy/tests/ui/missing_doc.rs
@@ -116,6 +116,14 @@ with_span!(span pub fn foo_pm() {});
 with_span!(span pub static FOO_PM: u32 = 0;);
 with_span!(span pub const FOO2_PM: u32 = 0;);
 
+// Don't lint unnamed constants
+const _: () = ();
+
+fn issue13298() {
+    // Rustdoc doesn't generate documentation for items within other items like fns or consts
+    const MSG: &str = "Hello, world!";
+}
+
 // issue #12197
 // Undocumented field originated inside of spanned proc-macro attribute
 /// Some dox for struct.
diff --git a/src/tools/clippy/tests/ui/missing_doc.stderr b/src/tools/clippy/tests/ui/missing_doc.stderr
index ef0f96a5b71..133c76ac9d4 100644
--- a/src/tools/clippy/tests/ui/missing_doc.stderr
+++ b/src/tools/clippy/tests/ui/missing_doc.stderr
@@ -88,5 +88,14 @@ error: missing documentation for a function
 LL |         fn also_undocumented2() {}
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^
 
-error: aborting due to 13 previous errors
+error: missing documentation for a function
+  --> tests/ui/missing_doc.rs:122:1
+   |
+LL | / fn issue13298() {
+LL | |     // Rustdoc doesn't generate documentation for items within other items like fns or consts
+LL | |     const MSG: &str = "Hello, world!";
+LL | | }
+   | |_^
+
+error: aborting due to 14 previous errors
 
diff --git a/src/tools/clippy/tests/ui/needless_as_bytes.fixed b/src/tools/clippy/tests/ui/needless_as_bytes.fixed
new file mode 100644
index 00000000000..042342311fd
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_as_bytes.fixed
@@ -0,0 +1,50 @@
+#![warn(clippy::needless_as_bytes)]
+#![allow(clippy::const_is_empty)]
+
+struct S;
+
+impl S {
+    fn as_bytes(&self) -> &[u8] {
+        &[]
+    }
+}
+
+fn main() {
+    if "some string".is_empty() {
+        //~^ needless_as_bytes
+        println!("len = {}", "some string".len());
+        //~^ needless_as_bytes
+    }
+
+    let s = String::from("yet another string");
+    if s.is_empty() {
+        //~^ needless_as_bytes
+        println!("len = {}", s.len());
+        //~^ needless_as_bytes
+    }
+
+    // Do not lint
+    let _ = S.as_bytes().is_empty();
+    let _ = S.as_bytes().len();
+    let _ = (&String::new() as &dyn AsBytes).as_bytes().len();
+    macro_rules! m {
+        (1) => {
+            ""
+        };
+        (2) => {
+            "".as_bytes()
+        };
+    }
+    m!(1).as_bytes().len();
+    m!(2).len();
+}
+
+pub trait AsBytes {
+    fn as_bytes(&self) -> &[u8];
+}
+
+impl AsBytes for String {
+    fn as_bytes(&self) -> &[u8] {
+        &[]
+    }
+}
diff --git a/src/tools/clippy/tests/ui/needless_as_bytes.rs b/src/tools/clippy/tests/ui/needless_as_bytes.rs
new file mode 100644
index 00000000000..c481e041e0a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_as_bytes.rs
@@ -0,0 +1,50 @@
+#![warn(clippy::needless_as_bytes)]
+#![allow(clippy::const_is_empty)]
+
+struct S;
+
+impl S {
+    fn as_bytes(&self) -> &[u8] {
+        &[]
+    }
+}
+
+fn main() {
+    if "some string".as_bytes().is_empty() {
+        //~^ needless_as_bytes
+        println!("len = {}", "some string".as_bytes().len());
+        //~^ needless_as_bytes
+    }
+
+    let s = String::from("yet another string");
+    if s.as_bytes().is_empty() {
+        //~^ needless_as_bytes
+        println!("len = {}", s.as_bytes().len());
+        //~^ needless_as_bytes
+    }
+
+    // Do not lint
+    let _ = S.as_bytes().is_empty();
+    let _ = S.as_bytes().len();
+    let _ = (&String::new() as &dyn AsBytes).as_bytes().len();
+    macro_rules! m {
+        (1) => {
+            ""
+        };
+        (2) => {
+            "".as_bytes()
+        };
+    }
+    m!(1).as_bytes().len();
+    m!(2).len();
+}
+
+pub trait AsBytes {
+    fn as_bytes(&self) -> &[u8];
+}
+
+impl AsBytes for String {
+    fn as_bytes(&self) -> &[u8] {
+        &[]
+    }
+}
diff --git a/src/tools/clippy/tests/ui/needless_as_bytes.stderr b/src/tools/clippy/tests/ui/needless_as_bytes.stderr
new file mode 100644
index 00000000000..3391238a142
--- /dev/null
+++ b/src/tools/clippy/tests/ui/needless_as_bytes.stderr
@@ -0,0 +1,29 @@
+error: needless call to `as_bytes()`
+  --> tests/ui/needless_as_bytes.rs:13:8
+   |
+LL |     if "some string".as_bytes().is_empty() {
+   |        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `"some string".is_empty()`
+   |
+   = note: `-D clippy::needless-as-bytes` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::needless_as_bytes)]`
+
+error: needless call to `as_bytes()`
+  --> tests/ui/needless_as_bytes.rs:15:30
+   |
+LL |         println!("len = {}", "some string".as_bytes().len());
+   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `"some string".len()`
+
+error: needless call to `as_bytes()`
+  --> tests/ui/needless_as_bytes.rs:20:8
+   |
+LL |     if s.as_bytes().is_empty() {
+   |        ^^^^^^^^^^^^^^^^^^^^^^^ help: `is_empty()` can be called directly on strings: `s.is_empty()`
+
+error: needless call to `as_bytes()`
+  --> tests/ui/needless_as_bytes.rs:22:30
+   |
+LL |         println!("len = {}", s.as_bytes().len());
+   |                              ^^^^^^^^^^^^^^^^^^ help: `len()` can be called directly on strings: `s.len()`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/clippy/tests/ui/needless_continue.rs b/src/tools/clippy/tests/ui/needless_continue.rs
index c26a292c8cb..b6d8a8f61ae 100644
--- a/src/tools/clippy/tests/ui/needless_continue.rs
+++ b/src/tools/clippy/tests/ui/needless_continue.rs
@@ -151,3 +151,20 @@ mod issue_2329 {
         }
     }
 }
+
+fn issue_13641() {
+    'a: while std::hint::black_box(true) {
+        #[allow(clippy::never_loop)]
+        loop {
+            continue 'a;
+        }
+    }
+
+    #[allow(clippy::never_loop)]
+    while std::hint::black_box(true) {
+        'b: loop {
+            continue 'b;
+            //~^ ERROR: this `continue` expression is redundant
+        }
+    }
+}
diff --git a/src/tools/clippy/tests/ui/needless_continue.stderr b/src/tools/clippy/tests/ui/needless_continue.stderr
index ec7c9ba39a7..0741ba69248 100644
--- a/src/tools/clippy/tests/ui/needless_continue.stderr
+++ b/src/tools/clippy/tests/ui/needless_continue.stderr
@@ -136,5 +136,13 @@ LL | |                 }
                                println!("bar-5");
                            }
 
-error: aborting due to 8 previous errors
+error: this `continue` expression is redundant
+  --> tests/ui/needless_continue.rs:166:13
+   |
+LL |             continue 'b;
+   |             ^^^^^^^^^^^^
+   |
+   = help: consider dropping the `continue` expression
+
+error: aborting due to 9 previous errors
 
diff --git a/src/tools/clippy/tests/ui/no_lints.rs b/src/tools/clippy/tests/ui/no_lints.rs
deleted file mode 100644
index a8467bb6ef7..00000000000
--- a/src/tools/clippy/tests/ui/no_lints.rs
+++ /dev/null
@@ -1,3 +0,0 @@
-#![deny(clippy::all)]
-
-fn main() {}
\ No newline at end of file
diff --git a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs
index 8c1ea81d2ac..8be149d1863 100644
--- a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs
+++ b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.rs
@@ -2,30 +2,30 @@
 #![allow(unused)]
 #![warn(clippy::no_mangle_with_rust_abi)]
 
-#[no_mangle]
+#[unsafe(no_mangle)]
 fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
-//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+//~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI
 //~| NOTE: `-D clippy::no-mangle-with-rust-abi` implied by `-D warnings`
 
-#[no_mangle]
+#[unsafe(no_mangle)]
 pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
-//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+//~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI
 
 /// # Safety
 /// This function shouldn't be called unless the horsemen are ready
-#[no_mangle]
+#[unsafe(no_mangle)]
 pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
-//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+//~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI
 
 /// # Safety
 /// This function shouldn't be called unless the horsemen are ready
-#[no_mangle]
+#[unsafe(no_mangle)]
 unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
-//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+//~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI
 
-#[no_mangle]
+#[unsafe(no_mangle)]
 fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
-    //~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+    //~^ ERROR: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI
     arg_one: u32,
     arg_two: usize,
 ) -> u32 {
@@ -33,13 +33,13 @@ fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lin
 }
 
 // Must not run on functions that explicitly opt in to using the Rust ABI with `extern "Rust"`
-#[no_mangle]
+#[unsafe(no_mangle)]
 #[rustfmt::skip]
 extern "Rust" fn rust_abi_fn_explicit_opt_in(arg_one: u32, arg_two: usize) {}
 
 fn rust_abi_fn_again(arg_one: u32, arg_two: usize) {}
 
-#[no_mangle]
+#[unsafe(no_mangle)]
 extern "C" fn c_abi_fn(arg_one: u32, arg_two: usize) {}
 
 extern "C" fn c_abi_fn_again(arg_one: u32, arg_two: usize) {}
@@ -48,6 +48,11 @@ extern "C" {
     fn c_abi_in_block(arg_one: u32, arg_two: usize);
 }
 
+mod r#fn {
+    #[unsafe(no_mangle)]
+    pub(in super::r#fn) fn with_some_fn_around() {}
+}
+
 fn main() {
     // test code goes here
 }
diff --git a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.stderr b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.stderr
index 27b8b39a21a..a00ebe5e1ac 100644
--- a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.stderr
+++ b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi.stderr
@@ -1,4 +1,4 @@
-error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI
   --> tests/ui/no_mangle_with_rust_abi.rs:6:1
    |
 LL | fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
@@ -15,7 +15,7 @@ help: or explicitly set the default
 LL | extern "Rust" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
    | +++++++++++++
 
-error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI
   --> tests/ui/no_mangle_with_rust_abi.rs:11:1
    |
 LL | pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
@@ -30,7 +30,7 @@ help: or explicitly set the default
 LL | pub extern "Rust" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
    |     +++++++++++++
 
-error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI
   --> tests/ui/no_mangle_with_rust_abi.rs:17:1
    |
 LL | pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
@@ -45,7 +45,7 @@ help: or explicitly set the default
 LL | pub unsafe extern "Rust" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
    |            +++++++++++++
 
-error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI
   --> tests/ui/no_mangle_with_rust_abi.rs:23:1
    |
 LL | unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
@@ -60,7 +60,7 @@ help: or explicitly set the default
 LL | unsafe extern "Rust" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
    |        +++++++++++++
 
-error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI
   --> tests/ui/no_mangle_with_rust_abi.rs:27:1
    |
 LL | / fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
@@ -79,5 +79,20 @@ help: or explicitly set the default
 LL | extern "Rust" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
    | +++++++++++++
 
-error: aborting due to 5 previous errors
+error: `#[unsafe(no_mangle)]` set on a function with the default (`Rust`) ABI
+  --> tests/ui/no_mangle_with_rust_abi.rs:53:5
+   |
+LL |     pub(in super::r#fn) fn with_some_fn_around() {}
+   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: set an ABI
+   |
+LL |     pub(in super::r#fn) extern "C" fn with_some_fn_around() {}
+   |                         ++++++++++
+help: or explicitly set the default
+   |
+LL |     pub(in super::r#fn) extern "Rust" fn with_some_fn_around() {}
+   |                         +++++++++++++
+
+error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi_2021.rs b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi_2021.rs
new file mode 100644
index 00000000000..c7c97335348
--- /dev/null
+++ b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi_2021.rs
@@ -0,0 +1,57 @@
+//@edition:2021
+//
+// Edition 2024 requires the use of #[unsafe(no_mangle)]
+
+//@no-rustfix: overlapping suggestions
+#![allow(unused)]
+#![warn(clippy::no_mangle_with_rust_abi)]
+
+#[no_mangle]
+fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
+//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+//~| NOTE: `-D clippy::no-mangle-with-rust-abi` implied by `-D warnings`
+
+#[no_mangle]
+pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
+//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+
+/// # Safety
+/// This function shouldn't be called unless the horsemen are ready
+#[no_mangle]
+pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
+//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+
+/// # Safety
+/// This function shouldn't be called unless the horsemen are ready
+#[no_mangle]
+unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
+//~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+
+#[no_mangle]
+fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
+    //~^ ERROR: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+    arg_one: u32,
+    arg_two: usize,
+) -> u32 {
+    0
+}
+
+// Must not run on functions that explicitly opt in to using the Rust ABI with `extern "Rust"`
+#[no_mangle]
+#[rustfmt::skip]
+extern "Rust" fn rust_abi_fn_explicit_opt_in(arg_one: u32, arg_two: usize) {}
+
+fn rust_abi_fn_again(arg_one: u32, arg_two: usize) {}
+
+#[no_mangle]
+extern "C" fn c_abi_fn(arg_one: u32, arg_two: usize) {}
+
+extern "C" fn c_abi_fn_again(arg_one: u32, arg_two: usize) {}
+
+extern "C" {
+    fn c_abi_in_block(arg_one: u32, arg_two: usize);
+}
+
+fn main() {
+    // test code goes here
+}
diff --git a/src/tools/clippy/tests/ui/no_mangle_with_rust_abi_2021.stderr b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi_2021.stderr
new file mode 100644
index 00000000000..15075be72d0
--- /dev/null
+++ b/src/tools/clippy/tests/ui/no_mangle_with_rust_abi_2021.stderr
@@ -0,0 +1,83 @@
+error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+  --> tests/ui/no_mangle_with_rust_abi_2021.rs:10:1
+   |
+LL | fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+   = note: `-D clippy::no-mangle-with-rust-abi` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::no_mangle_with_rust_abi)]`
+help: set an ABI
+   |
+LL | extern "C" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
+   | ++++++++++
+help: or explicitly set the default
+   |
+LL | extern "Rust" fn rust_abi_fn_one(arg_one: u32, arg_two: usize) {}
+   | +++++++++++++
+
+error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+  --> tests/ui/no_mangle_with_rust_abi_2021.rs:15:1
+   |
+LL | pub fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: set an ABI
+   |
+LL | pub extern "C" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
+   |     ++++++++++
+help: or explicitly set the default
+   |
+LL | pub extern "Rust" fn rust_abi_fn_two(arg_one: u32, arg_two: usize) {}
+   |     +++++++++++++
+
+error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+  --> tests/ui/no_mangle_with_rust_abi_2021.rs:21:1
+   |
+LL | pub unsafe fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: set an ABI
+   |
+LL | pub unsafe extern "C" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
+   |            ++++++++++
+help: or explicitly set the default
+   |
+LL | pub unsafe extern "Rust" fn rust_abi_fn_three(arg_one: u32, arg_two: usize) {}
+   |            +++++++++++++
+
+error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+  --> tests/ui/no_mangle_with_rust_abi_2021.rs:27:1
+   |
+LL | unsafe fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |
+help: set an ABI
+   |
+LL | unsafe extern "C" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
+   |        ++++++++++
+help: or explicitly set the default
+   |
+LL | unsafe extern "Rust" fn rust_abi_fn_four(arg_one: u32, arg_two: usize) {}
+   |        +++++++++++++
+
+error: `#[no_mangle]` set on a function with the default (`Rust`) ABI
+  --> tests/ui/no_mangle_with_rust_abi_2021.rs:31:1
+   |
+LL | / fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
+LL | |
+LL | |     arg_one: u32,
+LL | |     arg_two: usize,
+LL | | ) -> u32 {
+   | |________^
+   |
+help: set an ABI
+   |
+LL | extern "C" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
+   | ++++++++++
+help: or explicitly set the default
+   |
+LL | extern "Rust" fn rust_abi_multiline_function_really_long_name_to_overflow_args_to_multiple_lines(
+   | +++++++++++++
+
+error: aborting due to 5 previous errors
+
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool.stderr b/src/tools/clippy/tests/ui/nonminimal_bool.stderr
index eafffdaf8a6..578f918f013 100644
--- a/src/tools/clippy/tests/ui/nonminimal_bool.stderr
+++ b/src/tools/clippy/tests/ui/nonminimal_bool.stderr
@@ -118,25 +118,25 @@ error: this boolean expression can be simplified
   --> tests/ui/nonminimal_bool.rs:161:8
    |
 LL |     if !(12 == a) {}
-   |        ^^^^^^^^^^ help: try: `12 != a`
+   |        ^^^^^^^^^^ help: try: `(12 != a)`
 
 error: this boolean expression can be simplified
   --> tests/ui/nonminimal_bool.rs:162:8
    |
 LL |     if !(a == 12) {}
-   |        ^^^^^^^^^^ help: try: `a != 12`
+   |        ^^^^^^^^^^ help: try: `(a != 12)`
 
 error: this boolean expression can be simplified
   --> tests/ui/nonminimal_bool.rs:163:8
    |
 LL |     if !(12 != a) {}
-   |        ^^^^^^^^^^ help: try: `12 == a`
+   |        ^^^^^^^^^^ help: try: `(12 == a)`
 
 error: this boolean expression can be simplified
   --> tests/ui/nonminimal_bool.rs:164:8
    |
 LL |     if !(a != 12) {}
-   |        ^^^^^^^^^^ help: try: `a == 12`
+   |        ^^^^^^^^^^ help: try: `(a == 12)`
 
 error: this boolean expression can be simplified
   --> tests/ui/nonminimal_bool.rs:168:8
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed b/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed
index a23310c1ad9..65ccaaca891 100644
--- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed
+++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.fixed
@@ -110,9 +110,33 @@ fn dont_warn_for_negated_partial_ord_comparison() {
 fn issue_12625() {
     let a = 0;
     let b = 0;
-    if (a as u64) < b {} //~ ERROR: this boolean expression can be simplified
-    if (a as u64) < b {} //~ ERROR: this boolean expression can be simplified
-    if a as u64 > b {} //~ ERROR: this boolean expression can be simplified
+    if ((a as u64) < b) {} //~ ERROR: this boolean expression can be simplified
+    if ((a as u64) < b) {} //~ ERROR: this boolean expression can be simplified
+    if (a as u64 > b) {} //~ ERROR: this boolean expression can be simplified
+}
+
+fn issue_12761() {
+    let a = 0;
+    let b = 0;
+    let c = 0;
+    if (a < b) as i32 == c {} //~ ERROR: this boolean expression can be simplified
+    if (a < b) | (a > c) {} //~ ERROR: this boolean expression can be simplified
+    let opt: Option<usize> = Some(1);
+    let res: Result<usize, usize> = Ok(1);
+    if res.is_err() as i32 == c {} //~ ERROR: this boolean expression can be simplified
+    if res.is_err() | opt.is_some() {} //~ ERROR: this boolean expression can be simplified
+
+    fn a(a: bool) -> bool {
+        (4 <= 3).b() //~ ERROR: this boolean expression can be simplified
+    }
+
+    trait B {
+        fn b(&self) -> bool {
+            true
+        }
+    }
+
+    impl B for bool {}
 }
 
 fn issue_13436() {
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs
index 6c844373af7..06db3a1d4a5 100644
--- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs
+++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.rs
@@ -115,6 +115,30 @@ fn issue_12625() {
     if !(a as u64 <= b) {} //~ ERROR: this boolean expression can be simplified
 }
 
+fn issue_12761() {
+    let a = 0;
+    let b = 0;
+    let c = 0;
+    if !(a >= b) as i32 == c {} //~ ERROR: this boolean expression can be simplified
+    if !(a >= b) | !(a <= c) {} //~ ERROR: this boolean expression can be simplified
+    let opt: Option<usize> = Some(1);
+    let res: Result<usize, usize> = Ok(1);
+    if !res.is_ok() as i32 == c {} //~ ERROR: this boolean expression can be simplified
+    if !res.is_ok() | !opt.is_none() {} //~ ERROR: this boolean expression can be simplified
+
+    fn a(a: bool) -> bool {
+        (!(4 > 3)).b() //~ ERROR: this boolean expression can be simplified
+    }
+
+    trait B {
+        fn b(&self) -> bool {
+            true
+        }
+    }
+
+    impl B for bool {}
+}
+
 fn issue_13436() {
     fn not_zero(x: i32) -> bool {
         x != 0
diff --git a/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr b/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr
index 52803e828ae..66c50f9ff1e 100644
--- a/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr
+++ b/src/tools/clippy/tests/ui/nonminimal_bool_methods.stderr
@@ -83,127 +83,169 @@ error: this boolean expression can be simplified
   --> tests/ui/nonminimal_bool_methods.rs:113:8
    |
 LL |     if !(a as u64 >= b) {}
-   |        ^^^^^^^^^^^^^^^^ help: try: `(a as u64) < b`
+   |        ^^^^^^^^^^^^^^^^ help: try: `((a as u64) < b)`
 
 error: this boolean expression can be simplified
   --> tests/ui/nonminimal_bool_methods.rs:114:8
    |
 LL |     if !((a as u64) >= b) {}
-   |        ^^^^^^^^^^^^^^^^^^ help: try: `(a as u64) < b`
+   |        ^^^^^^^^^^^^^^^^^^ help: try: `((a as u64) < b)`
 
 error: this boolean expression can be simplified
   --> tests/ui/nonminimal_bool_methods.rs:115:8
    |
 LL |     if !(a as u64 <= b) {}
-   |        ^^^^^^^^^^^^^^^^ help: try: `a as u64 > b`
+   |        ^^^^^^^^^^^^^^^^ help: try: `(a as u64 > b)`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool_methods.rs:131:9
+  --> tests/ui/nonminimal_bool_methods.rs:122:8
+   |
+LL |     if !(a >= b) as i32 == c {}
+   |        ^^^^^^^^^ help: try: `(a < b)`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:123:8
+   |
+LL |     if !(a >= b) | !(a <= c) {}
+   |        ^^^^^^^^^ help: try: `(a < b)`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:123:20
+   |
+LL |     if !(a >= b) | !(a <= c) {}
+   |                    ^^^^^^^^^ help: try: `(a > c)`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:126:8
+   |
+LL |     if !res.is_ok() as i32 == c {}
+   |        ^^^^^^^^^^^^ help: try: `res.is_err()`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:127:8
+   |
+LL |     if !res.is_ok() | !opt.is_none() {}
+   |        ^^^^^^^^^^^^ help: try: `res.is_err()`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:127:23
+   |
+LL |     if !res.is_ok() | !opt.is_none() {}
+   |                       ^^^^^^^^^^^^^^ help: try: `opt.is_some()`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:130:9
+   |
+LL |         (!(4 > 3)).b()
+   |         ^^^^^^^^^^ help: try: `(4 <= 3)`
+
+error: this boolean expression can be simplified
+  --> tests/ui/nonminimal_bool_methods.rs:155:9
    |
 LL |     _ = !opt.is_some_and(|x| x < 1000);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x >= 1000)`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool_methods.rs:132:9
+  --> tests/ui/nonminimal_bool_methods.rs:156:9
    |
 LL |     _ = !opt.is_some_and(|x| x <= 1000);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x > 1000)`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool_methods.rs:133:9
+  --> tests/ui/nonminimal_bool_methods.rs:157:9
    |
 LL |     _ = !opt.is_some_and(|x| x > 1000);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x <= 1000)`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool_methods.rs:134:9
+  --> tests/ui/nonminimal_bool_methods.rs:158:9
    |
 LL |     _ = !opt.is_some_and(|x| x >= 1000);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x < 1000)`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool_methods.rs:135:9
+  --> tests/ui/nonminimal_bool_methods.rs:159:9
    |
 LL |     _ = !opt.is_some_and(|x| x == 1000);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x != 1000)`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool_methods.rs:136:9
+  --> tests/ui/nonminimal_bool_methods.rs:160:9
    |
 LL |     _ = !opt.is_some_and(|x| x != 1000);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x == 1000)`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool_methods.rs:145:9
+  --> tests/ui/nonminimal_bool_methods.rs:169:9
    |
 LL |     _ = !opt.is_none_or(|x| x < 1000);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x >= 1000)`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool_methods.rs:146:9
+  --> tests/ui/nonminimal_bool_methods.rs:170:9
    |
 LL |     _ = !opt.is_none_or(|x| x <= 1000);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x > 1000)`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool_methods.rs:147:9
+  --> tests/ui/nonminimal_bool_methods.rs:171:9
    |
 LL |     _ = !opt.is_none_or(|x| x > 1000);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x <= 1000)`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool_methods.rs:148:9
+  --> tests/ui/nonminimal_bool_methods.rs:172:9
    |
 LL |     _ = !opt.is_none_or(|x| x >= 1000);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x < 1000)`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool_methods.rs:149:9
+  --> tests/ui/nonminimal_bool_methods.rs:173:9
    |
 LL |     _ = !opt.is_none_or(|x| x == 1000);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x != 1000)`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool_methods.rs:150:9
+  --> tests/ui/nonminimal_bool_methods.rs:174:9
    |
 LL |     _ = !opt.is_none_or(|x| x != 1000);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x == 1000)`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool_methods.rs:157:9
+  --> tests/ui/nonminimal_bool_methods.rs:181:9
    |
 LL |     _ = !opt.is_some_and(|x| !x);
    |         ^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x)`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool_methods.rs:161:9
+  --> tests/ui/nonminimal_bool_methods.rs:185:9
    |
 LL |     _ = !opt.is_none_or(|x| !x);
    |         ^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x)`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool_methods.rs:168:9
+  --> tests/ui/nonminimal_bool_methods.rs:192:9
    |
 LL |     _ = !opt.is_some_and(|x| x.is_ok());
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x.is_err())`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool_methods.rs:169:9
+  --> tests/ui/nonminimal_bool_methods.rs:193:9
    |
 LL |     _ = !opt.is_some_and(|x| x.is_err());
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_none_or(|x| x.is_ok())`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool_methods.rs:170:9
+  --> tests/ui/nonminimal_bool_methods.rs:194:9
    |
 LL |     _ = !opt.is_none_or(|x| x.is_ok());
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x.is_err())`
 
 error: this boolean expression can be simplified
-  --> tests/ui/nonminimal_bool_methods.rs:171:9
+  --> tests/ui/nonminimal_bool_methods.rs:195:9
    |
 LL |     _ = !opt.is_none_or(|x| x.is_err());
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try: `opt.is_some_and(|x| x.is_ok())`
 
-error: aborting due to 34 previous errors
+error: aborting due to 41 previous errors
 
diff --git a/src/tools/clippy/tests/ui/question_mark.fixed b/src/tools/clippy/tests/ui/question_mark.fixed
index 679388372e6..b6e148e9f77 100644
--- a/src/tools/clippy/tests/ui/question_mark.fixed
+++ b/src/tools/clippy/tests/ui/question_mark.fixed
@@ -96,12 +96,42 @@ impl MoveStruct {
 }
 
 fn func() -> Option<i32> {
+    macro_rules! opt_none {
+        () => {
+            None
+        };
+    }
+
     fn f() -> Option<String> {
         Some(String::new())
     }
 
     f()?;
 
+    let _val = f()?;
+
+    let s: &str = match &Some(String::new()) {
+        Some(v) => v,
+        None => return None,
+    };
+
+    f()?;
+
+    opt_none!()?;
+
+    match f() {
+        Some(x) => x,
+        None => return opt_none!(),
+    };
+
+    match f() {
+        Some(val) => {
+            println!("{val}");
+            val
+        },
+        None => return None,
+    };
+
     Some(0)
 }
 
@@ -114,6 +144,10 @@ fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
 
     x?;
 
+    let _val = func_returning_result()?;
+
+    func_returning_result()?;
+
     // No warning
     let y = if let Ok(x) = x {
         x
@@ -157,6 +191,28 @@ fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
     Ok(y)
 }
 
+fn infer_check() {
+    let closure = |x: Result<u8, ()>| {
+        // `?` would fail here, as it expands to `Err(val.into())` which is not constrained.
+        let _val = match x {
+            Ok(val) => val,
+            Err(val) => return Err(val),
+        };
+
+        Ok(())
+    };
+
+    let closure = |x: Result<u8, ()>| -> Result<(), _> {
+        // `?` would fail here, as it expands to `Err(val.into())` which is not constrained.
+        let _val = match x {
+            Ok(val) => val,
+            Err(val) => return Err(val),
+        };
+
+        Ok(())
+    };
+}
+
 // see issue #8019
 pub enum NotOption {
     None,
diff --git a/src/tools/clippy/tests/ui/question_mark.rs b/src/tools/clippy/tests/ui/question_mark.rs
index 601ab78bf5a..48dc9eb0a62 100644
--- a/src/tools/clippy/tests/ui/question_mark.rs
+++ b/src/tools/clippy/tests/ui/question_mark.rs
@@ -124,6 +124,12 @@ impl MoveStruct {
 }
 
 fn func() -> Option<i32> {
+    macro_rules! opt_none {
+        () => {
+            None
+        };
+    }
+
     fn f() -> Option<String> {
         Some(String::new())
     }
@@ -132,6 +138,39 @@ fn func() -> Option<i32> {
         return None;
     }
 
+    let _val = match f() {
+        Some(val) => val,
+        None => return None,
+    };
+
+    let s: &str = match &Some(String::new()) {
+        Some(v) => v,
+        None => return None,
+    };
+
+    match f() {
+        Some(val) => val,
+        None => return None,
+    };
+
+    match opt_none!() {
+        Some(x) => x,
+        None => return None,
+    };
+
+    match f() {
+        Some(x) => x,
+        None => return opt_none!(),
+    };
+
+    match f() {
+        Some(val) => {
+            println!("{val}");
+            val
+        },
+        None => return None,
+    };
+
     Some(0)
 }
 
@@ -146,6 +185,16 @@ fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
         return x;
     }
 
+    let _val = match func_returning_result() {
+        Ok(val) => val,
+        Err(err) => return Err(err),
+    };
+
+    match func_returning_result() {
+        Ok(val) => val,
+        Err(err) => return Err(err),
+    };
+
     // No warning
     let y = if let Ok(x) = x {
         x
@@ -189,6 +238,28 @@ fn result_func(x: Result<i32, i32>) -> Result<i32, i32> {
     Ok(y)
 }
 
+fn infer_check() {
+    let closure = |x: Result<u8, ()>| {
+        // `?` would fail here, as it expands to `Err(val.into())` which is not constrained.
+        let _val = match x {
+            Ok(val) => val,
+            Err(val) => return Err(val),
+        };
+
+        Ok(())
+    };
+
+    let closure = |x: Result<u8, ()>| -> Result<(), _> {
+        // `?` would fail here, as it expands to `Err(val.into())` which is not constrained.
+        let _val = match x {
+            Ok(val) => val,
+            Err(val) => return Err(val),
+        };
+
+        Ok(())
+    };
+}
+
 // see issue #8019
 pub enum NotOption {
     None,
diff --git a/src/tools/clippy/tests/ui/question_mark.stderr b/src/tools/clippy/tests/ui/question_mark.stderr
index 5f26a7ea2c3..0a48c4e80cb 100644
--- a/src/tools/clippy/tests/ui/question_mark.stderr
+++ b/src/tools/clippy/tests/ui/question_mark.stderr
@@ -94,29 +94,76 @@ LL | |         };
    | |_________^ help: replace it with: `self.opt?`
 
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:131:5
+  --> tests/ui/question_mark.rs:137:5
    |
 LL | /     if f().is_none() {
 LL | |         return None;
 LL | |     }
    | |_____^ help: replace it with: `f()?;`
 
+error: this `match` expression can be replaced with `?`
+  --> tests/ui/question_mark.rs:141:16
+   |
+LL |       let _val = match f() {
+   |  ________________^
+LL | |         Some(val) => val,
+LL | |         None => return None,
+LL | |     };
+   | |_____^ help: try instead: `f()?`
+
+error: this `match` expression can be replaced with `?`
+  --> tests/ui/question_mark.rs:151:5
+   |
+LL | /     match f() {
+LL | |         Some(val) => val,
+LL | |         None => return None,
+LL | |     };
+   | |_____^ help: try instead: `f()?`
+
+error: this `match` expression can be replaced with `?`
+  --> tests/ui/question_mark.rs:156:5
+   |
+LL | /     match opt_none!() {
+LL | |         Some(x) => x,
+LL | |         None => return None,
+LL | |     };
+   | |_____^ help: try instead: `opt_none!()?`
+
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:143:13
+  --> tests/ui/question_mark.rs:182:13
    |
 LL |     let _ = if let Ok(x) = x { x } else { return x };
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: replace it with: `x?`
 
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:145:5
+  --> tests/ui/question_mark.rs:184:5
    |
 LL | /     if x.is_err() {
 LL | |         return x;
 LL | |     }
    | |_____^ help: replace it with: `x?;`
 
+error: this `match` expression can be replaced with `?`
+  --> tests/ui/question_mark.rs:188:16
+   |
+LL |       let _val = match func_returning_result() {
+   |  ________________^
+LL | |         Ok(val) => val,
+LL | |         Err(err) => return Err(err),
+LL | |     };
+   | |_____^ help: try instead: `func_returning_result()?`
+
+error: this `match` expression can be replaced with `?`
+  --> tests/ui/question_mark.rs:193:5
+   |
+LL | /     match func_returning_result() {
+LL | |         Ok(val) => val,
+LL | |         Err(err) => return Err(err),
+LL | |     };
+   | |_____^ help: try instead: `func_returning_result()?`
+
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:213:5
+  --> tests/ui/question_mark.rs:284:5
    |
 LL | /     if let Err(err) = func_returning_result() {
 LL | |         return Err(err);
@@ -124,7 +171,7 @@ LL | |     }
    | |_____^ help: replace it with: `func_returning_result()?;`
 
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:220:5
+  --> tests/ui/question_mark.rs:291:5
    |
 LL | /     if let Err(err) = func_returning_result() {
 LL | |         return Err(err);
@@ -132,7 +179,7 @@ LL | |     }
    | |_____^ help: replace it with: `func_returning_result()?;`
 
 error: this block may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:297:13
+  --> tests/ui/question_mark.rs:368:13
    |
 LL | /             if a.is_none() {
 LL | |                 return None;
@@ -142,12 +189,12 @@ LL | |             }
    | |_____________^ help: replace it with: `a?;`
 
 error: this `let...else` may be rewritten with the `?` operator
-  --> tests/ui/question_mark.rs:357:5
+  --> tests/ui/question_mark.rs:428:5
    |
 LL | /     let Some(v) = bar.foo.owned.clone() else {
 LL | |         return None;
 LL | |     };
    | |______^ help: replace it with: `let v = bar.foo.owned.clone()?;`
 
-error: aborting due to 17 previous errors
+error: aborting due to 22 previous errors
 
diff --git a/src/tools/clippy/tests/ui/rename.rs b/src/tools/clippy/tests/ui/rename.rs
index e03df1658ee..2ac59718786 100644
--- a/src/tools/clippy/tests/ui/rename.rs
+++ b/src/tools/clippy/tests/ui/rename.rs
@@ -54,7 +54,7 @@
 #![allow(enum_intrinsics_non_enums)]
 #![allow(non_fmt_panics)]
 #![allow(named_arguments_used_positionally)]
-#![allow(temporary_cstring_as_ptr)]
+#![allow(dangling_pointers_from_temporaries)]
 #![allow(undropped_manually_drops)]
 #![allow(unknown_lints)]
 #![allow(unused_labels)]
diff --git a/src/tools/clippy/tests/ui/rename.stderr b/src/tools/clippy/tests/ui/rename.stderr
index b906079d7df..a5ae727b7ee 100644
--- a/src/tools/clippy/tests/ui/rename.stderr
+++ b/src/tools/clippy/tests/ui/rename.stderr
@@ -1,17 +1,11 @@
-error: lint `temporary_cstring_as_ptr` has been renamed to `dangling_pointers_from_temporaries`
-  --> tests/ui/rename.rs:57:10
-   |
-LL | #![allow(temporary_cstring_as_ptr)]
-   |          ^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `dangling_pointers_from_temporaries`
-   |
-   = note: `-D renamed-and-removed-lints` implied by `-D warnings`
-   = help: to override `-D warnings` add `#[allow(renamed_and_removed_lints)]`
-
 error: lint `clippy::almost_complete_letter_range` has been renamed to `clippy::almost_complete_range`
   --> tests/ui/rename.rs:63:9
    |
 LL | #![warn(clippy::almost_complete_letter_range)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::almost_complete_range`
+   |
+   = note: `-D renamed-and-removed-lints` implied by `-D warnings`
+   = 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:64:9
@@ -403,5 +397,5 @@ error: lint `clippy::reverse_range_loop` has been renamed to `clippy::reversed_e
 LL | #![warn(clippy::reverse_range_loop)]
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use the new name: `clippy::reversed_empty_ranges`
 
-error: aborting due to 67 previous errors
+error: aborting due to 66 previous errors
 
diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.fixed b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.fixed
index 2afe2f43325..f72b61b5f6c 100644
--- a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.fixed
+++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.fixed
@@ -1,3 +1,4 @@
+#![allow(clippy::map_with_unused_argument_over_ranges)]
 #![warn(clippy::repeat_vec_with_capacity)]
 
 fn main() {
diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.rs b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.rs
index 659f2a3953d..c0cc81f7843 100644
--- a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.rs
+++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.rs
@@ -1,3 +1,4 @@
+#![allow(clippy::map_with_unused_argument_over_ranges)]
 #![warn(clippy::repeat_vec_with_capacity)]
 
 fn main() {
diff --git a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr
index cec9c6ea84a..43027c9cb89 100644
--- a/src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr
+++ b/src/tools/clippy/tests/ui/repeat_vec_with_capacity.stderr
@@ -1,5 +1,5 @@
 error: repeating `Vec::with_capacity` using `vec![x; n]`, which does not retain capacity
-  --> tests/ui/repeat_vec_with_capacity.rs:5:9
+  --> tests/ui/repeat_vec_with_capacity.rs:6:9
    |
 LL |         vec![Vec::<()>::with_capacity(42); 123];
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -13,7 +13,7 @@ LL |         (0..123).map(|_| Vec::<()>::with_capacity(42)).collect::<Vec<_>>();
    |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: repeating `Vec::with_capacity` using `vec![x; n]`, which does not retain capacity
-  --> tests/ui/repeat_vec_with_capacity.rs:11:9
+  --> tests/ui/repeat_vec_with_capacity.rs:12:9
    |
 LL |         vec![Vec::<()>::with_capacity(42); n];
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -25,7 +25,7 @@ LL |         (0..n).map(|_| Vec::<()>::with_capacity(42)).collect::<Vec<_>>();
    |         ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
 error: repeating `Vec::with_capacity` using `iter::repeat`, which does not retain capacity
-  --> tests/ui/repeat_vec_with_capacity.rs:26:9
+  --> tests/ui/repeat_vec_with_capacity.rs:27:9
    |
 LL |         std::iter::repeat(Vec::<()>::with_capacity(42));
    |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/semicolon_outside_block.fixed b/src/tools/clippy/tests/ui/semicolon_outside_block.fixed
index 148e112e0bc..ac7e86631ca 100644
--- a/src/tools/clippy/tests/ui/semicolon_outside_block.fixed
+++ b/src/tools/clippy/tests/ui/semicolon_outside_block.fixed
@@ -80,5 +80,13 @@ fn main() {
 
     { unit_fn_block(); };
 
+    unsafe {
+        std::arch::asm!("")
+    };
+
+    {
+        line!()
+    };
+
     unit_fn_block()
 }
diff --git a/src/tools/clippy/tests/ui/semicolon_outside_block.rs b/src/tools/clippy/tests/ui/semicolon_outside_block.rs
index c767201469a..68f25339e32 100644
--- a/src/tools/clippy/tests/ui/semicolon_outside_block.rs
+++ b/src/tools/clippy/tests/ui/semicolon_outside_block.rs
@@ -80,5 +80,13 @@ fn main() {
 
     { unit_fn_block(); };
 
+    unsafe {
+        std::arch::asm!("");
+    }
+
+    {
+        line!();
+    }
+
     unit_fn_block()
 }
diff --git a/src/tools/clippy/tests/ui/semicolon_outside_block.stderr b/src/tools/clippy/tests/ui/semicolon_outside_block.stderr
index 68b44c8f980..ff8c00048f6 100644
--- a/src/tools/clippy/tests/ui/semicolon_outside_block.stderr
+++ b/src/tools/clippy/tests/ui/semicolon_outside_block.stderr
@@ -51,5 +51,33 @@ LL -     { m!(()); }
 LL +     { m!(()) };
    |
 
-error: aborting due to 4 previous errors
+error: consider moving the `;` outside the block for consistent formatting
+  --> tests/ui/semicolon_outside_block.rs:83:5
+   |
+LL | /     unsafe {
+LL | |         std::arch::asm!("");
+LL | |     }
+   | |_____^
+   |
+help: put the `;` here
+   |
+LL ~         std::arch::asm!("")
+LL ~     };
+   |
+
+error: consider moving the `;` outside the block for consistent formatting
+  --> tests/ui/semicolon_outside_block.rs:87:5
+   |
+LL | /     {
+LL | |         line!();
+LL | |     }
+   | |_____^
+   |
+help: put the `;` here
+   |
+LL ~         line!()
+LL ~     };
+   |
+
+error: aborting due to 6 previous errors
 
diff --git a/src/tools/clippy/tests/ui/suspicious_map.rs b/src/tools/clippy/tests/ui/suspicious_map.rs
index d4247fcd926..d4a52cb110f 100644
--- a/src/tools/clippy/tests/ui/suspicious_map.rs
+++ b/src/tools/clippy/tests/ui/suspicious_map.rs
@@ -1,3 +1,4 @@
+#![allow(clippy::map_with_unused_argument_over_ranges)]
 #![warn(clippy::suspicious_map)]
 
 fn main() {
diff --git a/src/tools/clippy/tests/ui/suspicious_map.stderr b/src/tools/clippy/tests/ui/suspicious_map.stderr
index 2a5e2bf2a34..769adebaede 100644
--- a/src/tools/clippy/tests/ui/suspicious_map.stderr
+++ b/src/tools/clippy/tests/ui/suspicious_map.stderr
@@ -1,5 +1,5 @@
 error: this call to `map()` won't have an effect on the call to `count()`
-  --> tests/ui/suspicious_map.rs:4:13
+  --> tests/ui/suspicious_map.rs:5:13
    |
 LL |     let _ = (0..3).map(|x| x + 2).count();
    |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -9,7 +9,7 @@ LL |     let _ = (0..3).map(|x| x + 2).count();
    = help: to override `-D warnings` add `#[allow(clippy::suspicious_map)]`
 
 error: this call to `map()` won't have an effect on the call to `count()`
-  --> tests/ui/suspicious_map.rs:8:13
+  --> tests/ui/suspicious_map.rs:9:13
    |
 LL |     let _ = (0..3).map(f).count();
    |             ^^^^^^^^^^^^^^^^^^^^^
diff --git a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.stderr b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.stderr
index 6403265a39c..6ef333f0cfd 100644
--- a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.stderr
+++ b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph-fix.stderr
@@ -6,7 +6,7 @@ LL | | /// A much longer explanation that goes into a lot more detail about
 LL | | /// how the thing works, possibly with doclinks and so one,
 LL | | /// and probably spanning a many rows. Blablabla, it needs to be over
 LL | | /// 200 characters so I needed to write something longeeeeeeer.
-   | |_
+   | |_^
    |
    = note: `-D clippy::too-long-first-doc-paragraph` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::too_long_first_doc_paragraph)]`
diff --git a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr
index 39926647f54..95f42349b9b 100644
--- a/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr
+++ b/src/tools/clippy/tests/ui/too_long_first_doc_paragraph.stderr
@@ -23,7 +23,7 @@ error: first doc comment paragraph is too long
 LL | / /// Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
 LL | | /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
 LL | | /// gravida non lacinia at, rhoncus eu lacus.
-   | |_
+   | |_^
 
 error: first doc comment paragraph is too long
   --> tests/ui/too_long_first_doc_paragraph.rs:36:1
@@ -32,7 +32,7 @@ LL | / /// Lorem
 LL | | /// ipsum dolor sit amet, consectetur adipiscing elit. Nunc turpis nunc, lacinia
 LL | | /// a dolor in, pellentesque aliquet enim. Cras nec maximus sem. Mauris arcu libero,
 LL | | /// gravida non lacinia at, rhoncus eu lacus.
-   | |_
+   | |_^
 
 error: aborting due to 3 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_filter_map.rs b/src/tools/clippy/tests/ui/unnecessary_filter_map.rs
index 1e0d7d12965..8cf102ab0a5 100644
--- a/src/tools/clippy/tests/ui/unnecessary_filter_map.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_filter_map.rs
@@ -1,26 +1,31 @@
+//@no-rustfix
 #![allow(dead_code)]
 
 fn main() {
     let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None });
-    //~^ ERROR: this `.filter_map` can be written more simply using `.filter`
+    //~^ ERROR: this `.filter_map` can be written more simply
     //~| NOTE: `-D clippy::unnecessary-filter-map` implied by `-D warnings`
     let _ = (0..4).filter_map(|x| {
-        //~^ ERROR: this `.filter_map` can be written more simply using `.filter`
+        //~^ ERROR: this `.filter_map` can be written more simply
         if x > 1 {
             return Some(x);
         };
         None
     });
     let _ = (0..4).filter_map(|x| match x {
-        //~^ ERROR: this `.filter_map` can be written more simply using `.filter`
+        //~^ ERROR: this `.filter_map` can be written more simply
         0 | 1 => None,
         _ => Some(x),
     });
 
     let _ = (0..4).filter_map(|x| Some(x + 1));
-    //~^ ERROR: this `.filter_map` can be written more simply using `.map`
+    //~^ ERROR: this `.filter_map` can be written more simply
 
     let _ = (0..4).filter_map(i32::checked_abs);
+
+    let _ = (0..4).filter_map(Some);
+
+    let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x));
 }
 
 fn filter_map_none_changes_item_type() -> impl Iterator<Item = bool> {
diff --git a/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr b/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr
index f32d444ba9e..b21589c5f84 100644
--- a/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_filter_map.stderr
@@ -1,14 +1,14 @@
-error: this `.filter_map` can be written more simply using `.filter`
-  --> tests/ui/unnecessary_filter_map.rs:4:13
+error: this `.filter_map` can be written more simply
+  --> tests/ui/unnecessary_filter_map.rs:5:13
    |
 LL |     let _ = (0..4).filter_map(|x| if x > 1 { Some(x) } else { None });
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `filter`
    |
    = note: `-D clippy::unnecessary-filter-map` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_filter_map)]`
 
-error: this `.filter_map` can be written more simply using `.filter`
-  --> tests/ui/unnecessary_filter_map.rs:7:13
+error: this `.filter_map` can be written more simply
+  --> tests/ui/unnecessary_filter_map.rs:8:13
    |
 LL |       let _ = (0..4).filter_map(|x| {
    |  _____________^
@@ -18,10 +18,10 @@ LL | |             return Some(x);
 LL | |         };
 LL | |         None
 LL | |     });
-   | |______^
+   | |______^ help: try instead: `filter`
 
-error: this `.filter_map` can be written more simply using `.filter`
-  --> tests/ui/unnecessary_filter_map.rs:14:13
+error: this `.filter_map` can be written more simply
+  --> tests/ui/unnecessary_filter_map.rs:15:13
    |
 LL |       let _ = (0..4).filter_map(|x| match x {
    |  _____________^
@@ -29,19 +29,40 @@ LL | |
 LL | |         0 | 1 => None,
 LL | |         _ => Some(x),
 LL | |     });
-   | |______^
+   | |______^ help: try instead: `filter`
 
-error: this `.filter_map` can be written more simply using `.map`
-  --> tests/ui/unnecessary_filter_map.rs:20:13
+error: this `.filter_map` can be written more simply
+  --> tests/ui/unnecessary_filter_map.rs:21:13
    |
 LL |     let _ = (0..4).filter_map(|x| Some(x + 1));
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `map`
 
-error: this `.filter_map` can be written more simply using `.filter`
-  --> tests/ui/unnecessary_filter_map.rs:160:14
+error: redundant closure
+  --> tests/ui/unnecessary_filter_map.rs:28:57
+   |
+LL |     let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x));
+   |                                                         ^^^^^^^^^^^ help: replace the closure with the function itself: `Some`
+   |
+   = note: `-D clippy::redundant-closure` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::redundant_closure)]`
+
+error: filter_map is unnecessary
+  --> tests/ui/unnecessary_filter_map.rs:28:61
+   |
+LL |     let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x));
+   |                                                             ^^^^ help: try removing the filter_map
+
+error: this `.filter_map` can be written more simply
+  --> tests/ui/unnecessary_filter_map.rs:28:13
+   |
+LL |     let _ = vec![Some(10), None].into_iter().filter_map(|x| Some(x));
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `map`
+
+error: this `.filter_map` can be written more simply
+  --> tests/ui/unnecessary_filter_map.rs:165:14
    |
 LL |     let _x = std::iter::once(1).filter_map(|n| (n > 1).then_some(n));
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `filter`
 
-error: aborting due to 5 previous errors
+error: aborting due to 8 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_find_map.rs b/src/tools/clippy/tests/ui/unnecessary_find_map.rs
index 9972b68092a..c357d853248 100644
--- a/src/tools/clippy/tests/ui/unnecessary_find_map.rs
+++ b/src/tools/clippy/tests/ui/unnecessary_find_map.rs
@@ -1,24 +1,25 @@
+//@no-rustfix
 #![allow(dead_code)]
 
 fn main() {
     let _ = (0..4).find_map(|x| if x > 1 { Some(x) } else { None });
-    //~^ ERROR: this `.find_map` can be written more simply using `.find`
+    //~^ ERROR: this `.find_map` can be written more simply
     //~| NOTE: `-D clippy::unnecessary-find-map` implied by `-D warnings`
     let _ = (0..4).find_map(|x| {
-        //~^ ERROR: this `.find_map` can be written more simply using `.find`
+        //~^ ERROR: this `.find_map` can be written more simply
         if x > 1 {
             return Some(x);
         };
         None
     });
     let _ = (0..4).find_map(|x| match x {
-        //~^ ERROR: this `.find_map` can be written more simply using `.find`
+        //~^ ERROR: this `.find_map` can be written more simply
         0 | 1 => None,
         _ => Some(x),
     });
 
     let _ = (0..4).find_map(|x| Some(x + 1));
-    //~^ ERROR: this `.find_map` can be written more simply using `.map(..).next()`
+    //~^ ERROR: this `.find_map` can be written more simply
 
     let _ = (0..4).find_map(i32::checked_abs);
 }
diff --git a/src/tools/clippy/tests/ui/unnecessary_find_map.stderr b/src/tools/clippy/tests/ui/unnecessary_find_map.stderr
index bb939a99214..98a6c3d164a 100644
--- a/src/tools/clippy/tests/ui/unnecessary_find_map.stderr
+++ b/src/tools/clippy/tests/ui/unnecessary_find_map.stderr
@@ -1,14 +1,14 @@
-error: this `.find_map` can be written more simply using `.find`
-  --> tests/ui/unnecessary_find_map.rs:4:13
+error: this `.find_map` can be written more simply
+  --> tests/ui/unnecessary_find_map.rs:5:13
    |
 LL |     let _ = (0..4).find_map(|x| if x > 1 { Some(x) } else { None });
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `find`
    |
    = note: `-D clippy::unnecessary-find-map` implied by `-D warnings`
    = help: to override `-D warnings` add `#[allow(clippy::unnecessary_find_map)]`
 
-error: this `.find_map` can be written more simply using `.find`
-  --> tests/ui/unnecessary_find_map.rs:7:13
+error: this `.find_map` can be written more simply
+  --> tests/ui/unnecessary_find_map.rs:8:13
    |
 LL |       let _ = (0..4).find_map(|x| {
    |  _____________^
@@ -18,10 +18,10 @@ LL | |             return Some(x);
 LL | |         };
 LL | |         None
 LL | |     });
-   | |______^
+   | |______^ help: try instead: `find`
 
-error: this `.find_map` can be written more simply using `.find`
-  --> tests/ui/unnecessary_find_map.rs:14:13
+error: this `.find_map` can be written more simply
+  --> tests/ui/unnecessary_find_map.rs:15:13
    |
 LL |       let _ = (0..4).find_map(|x| match x {
    |  _____________^
@@ -29,19 +29,19 @@ LL | |
 LL | |         0 | 1 => None,
 LL | |         _ => Some(x),
 LL | |     });
-   | |______^
+   | |______^ help: try instead: `find`
 
-error: this `.find_map` can be written more simply using `.map(..).next()`
-  --> tests/ui/unnecessary_find_map.rs:20:13
+error: this `.find_map` can be written more simply
+  --> tests/ui/unnecessary_find_map.rs:21:13
    |
 LL |     let _ = (0..4).find_map(|x| Some(x + 1));
-   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `map(..).next()`
 
-error: this `.find_map` can be written more simply using `.find`
-  --> tests/ui/unnecessary_find_map.rs:32:14
+error: this `.find_map` can be written more simply
+  --> tests/ui/unnecessary_find_map.rs:33:14
    |
 LL |     let _x = std::iter::once(1).find_map(|n| (n > 1).then_some(n));
-   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+   |              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: try instead: `find`
 
 error: aborting due to 5 previous errors
 
diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.fixed b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed
new file mode 100644
index 00000000000..2d932a70e9d
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_map_or.fixed
@@ -0,0 +1,64 @@
+//@aux-build:proc_macros.rs
+#![warn(clippy::unnecessary_map_or)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::eq_op)]
+#![allow(clippy::unnecessary_lazy_evaluations)]
+#[clippy::msrv = "1.70.0"]
+#[macro_use]
+extern crate proc_macros;
+
+fn main() {
+    // should trigger
+    let _ = (Some(5) == Some(5));
+    let _ = (Some(5) != Some(5));
+    let _ = (Some(5) == Some(5));
+    let _ = Some(5).is_some_and(|n| {
+        let _ = n;
+        6 >= 5
+    });
+    let _ = Some(vec![5]).is_some_and(|n| n == [5]);
+    let _ = Some(vec![1]).is_some_and(|n| vec![2] == n);
+    let _ = Some(5).is_some_and(|n| n == n);
+    let _ = Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 });
+    let _ = Ok::<Vec<i32>, i32>(vec![5]).is_ok_and(|n| n == [5]);
+    let _ = (Ok::<i32, i32>(5) == Ok(5));
+    let _ = (Some(5) == Some(5)).then(|| 1);
+
+    // shouldnt trigger
+    let _ = Some(5).map_or(true, |n| n == 5);
+    let _ = Some(5).map_or(true, |n| 5 == n);
+    macro_rules! x {
+        () => {
+            Some(1)
+        };
+    }
+    // methods lints dont fire on macros
+    let _ = x!().map_or(false, |n| n == 1);
+    let _ = x!().map_or(false, |n| n == vec![1][0]);
+
+    msrv_1_69();
+
+    external! {
+        let _ = Some(5).map_or(false, |n| n == 5);
+    }
+
+    with_span! {
+        let _ = Some(5).map_or(false, |n| n == 5);
+    }
+
+    // check for presence of PartialEq, and alter suggestion to use `is_ok_and` if absent
+    struct S;
+    let r: Result<i32, S> = Ok(3);
+    let _ = r.is_ok_and(|x| x == 7);
+
+    #[derive(PartialEq)]
+    struct S2;
+    let r: Result<i32, S2> = Ok(4);
+    let _ = (r == Ok(8));
+}
+
+#[clippy::msrv = "1.69.0"]
+fn msrv_1_69() {
+    // is_some_and added in 1.70.0
+    let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 });
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.rs b/src/tools/clippy/tests/ui/unnecessary_map_or.rs
new file mode 100644
index 00000000000..4a9d69be1e9
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_map_or.rs
@@ -0,0 +1,67 @@
+//@aux-build:proc_macros.rs
+#![warn(clippy::unnecessary_map_or)]
+#![allow(clippy::no_effect)]
+#![allow(clippy::eq_op)]
+#![allow(clippy::unnecessary_lazy_evaluations)]
+#[clippy::msrv = "1.70.0"]
+#[macro_use]
+extern crate proc_macros;
+
+fn main() {
+    // should trigger
+    let _ = Some(5).map_or(false, |n| n == 5);
+    let _ = Some(5).map_or(true, |n| n != 5);
+    let _ = Some(5).map_or(false, |n| {
+        let _ = 1;
+        n == 5
+    });
+    let _ = Some(5).map_or(false, |n| {
+        let _ = n;
+        6 >= 5
+    });
+    let _ = Some(vec![5]).map_or(false, |n| n == [5]);
+    let _ = Some(vec![1]).map_or(false, |n| vec![2] == n);
+    let _ = Some(5).map_or(false, |n| n == n);
+    let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 });
+    let _ = Ok::<Vec<i32>, i32>(vec![5]).map_or(false, |n| n == [5]);
+    let _ = Ok::<i32, i32>(5).map_or(false, |n| n == 5);
+    let _ = Some(5).map_or(false, |n| n == 5).then(|| 1);
+
+    // shouldnt trigger
+    let _ = Some(5).map_or(true, |n| n == 5);
+    let _ = Some(5).map_or(true, |n| 5 == n);
+    macro_rules! x {
+        () => {
+            Some(1)
+        };
+    }
+    // methods lints dont fire on macros
+    let _ = x!().map_or(false, |n| n == 1);
+    let _ = x!().map_or(false, |n| n == vec![1][0]);
+
+    msrv_1_69();
+
+    external! {
+        let _ = Some(5).map_or(false, |n| n == 5);
+    }
+
+    with_span! {
+        let _ = Some(5).map_or(false, |n| n == 5);
+    }
+
+    // check for presence of PartialEq, and alter suggestion to use `is_ok_and` if absent
+    struct S;
+    let r: Result<i32, S> = Ok(3);
+    let _ = r.map_or(false, |x| x == 7);
+
+    #[derive(PartialEq)]
+    struct S2;
+    let r: Result<i32, S2> = Ok(4);
+    let _ = r.map_or(false, |x| x == 8);
+}
+
+#[clippy::msrv = "1.69.0"]
+fn msrv_1_69() {
+    // is_some_and added in 1.70.0
+    let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 });
+}
diff --git a/src/tools/clippy/tests/ui/unnecessary_map_or.stderr b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr
new file mode 100644
index 00000000000..299a4e5da7a
--- /dev/null
+++ b/src/tools/clippy/tests/ui/unnecessary_map_or.stderr
@@ -0,0 +1,99 @@
+error: this `map_or` is redundant
+  --> tests/ui/unnecessary_map_or.rs:12:13
+   |
+LL |     let _ = Some(5).map_or(false, |n| n == 5);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))`
+   |
+   = note: `-D clippy::unnecessary-map-or` implied by `-D warnings`
+   = help: to override `-D warnings` add `#[allow(clippy::unnecessary_map_or)]`
+
+error: this `map_or` is redundant
+  --> tests/ui/unnecessary_map_or.rs:13:13
+   |
+LL |     let _ = Some(5).map_or(true, |n| n != 5);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) != Some(5))`
+
+error: this `map_or` is redundant
+  --> tests/ui/unnecessary_map_or.rs:14:13
+   |
+LL |       let _ = Some(5).map_or(false, |n| {
+   |  _____________^
+LL | |         let _ = 1;
+LL | |         n == 5
+LL | |     });
+   | |______^ help: use a standard comparison instead: `(Some(5) == Some(5))`
+
+error: this `map_or` is redundant
+  --> tests/ui/unnecessary_map_or.rs:18:13
+   |
+LL |       let _ = Some(5).map_or(false, |n| {
+   |  _____________^
+LL | |         let _ = n;
+LL | |         6 >= 5
+LL | |     });
+   | |______^
+   |
+help: use is_some_and instead
+   |
+LL ~     let _ = Some(5).is_some_and(|n| {
+LL +         let _ = n;
+LL +         6 >= 5
+LL ~     });
+   |
+
+error: this `map_or` is redundant
+  --> tests/ui/unnecessary_map_or.rs:22:13
+   |
+LL |     let _ = Some(vec![5]).map_or(false, |n| n == [5]);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![5]).is_some_and(|n| n == [5])`
+
+error: this `map_or` is redundant
+  --> tests/ui/unnecessary_map_or.rs:23:13
+   |
+LL |     let _ = Some(vec![1]).map_or(false, |n| vec![2] == n);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(vec![1]).is_some_and(|n| vec![2] == n)`
+
+error: this `map_or` is redundant
+  --> tests/ui/unnecessary_map_or.rs:24:13
+   |
+LL |     let _ = Some(5).map_or(false, |n| n == n);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == n)`
+
+error: this `map_or` is redundant
+  --> tests/ui/unnecessary_map_or.rs:25:13
+   |
+LL |     let _ = Some(5).map_or(false, |n| n == if 2 > 1 { n } else { 0 });
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_some_and instead: `Some(5).is_some_and(|n| n == if 2 > 1 { n } else { 0 })`
+
+error: this `map_or` is redundant
+  --> tests/ui/unnecessary_map_or.rs:26:13
+   |
+LL |     let _ = Ok::<Vec<i32>, i32>(vec![5]).map_or(false, |n| n == [5]);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `Ok::<Vec<i32>, i32>(vec![5]).is_ok_and(|n| n == [5])`
+
+error: this `map_or` is redundant
+  --> tests/ui/unnecessary_map_or.rs:27:13
+   |
+LL |     let _ = Ok::<i32, i32>(5).map_or(false, |n| n == 5);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Ok::<i32, i32>(5) == Ok(5))`
+
+error: this `map_or` is redundant
+  --> tests/ui/unnecessary_map_or.rs:28:13
+   |
+LL |     let _ = Some(5).map_or(false, |n| n == 5).then(|| 1);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(Some(5) == Some(5))`
+
+error: this `map_or` is redundant
+  --> tests/ui/unnecessary_map_or.rs:55:13
+   |
+LL |     let _ = r.map_or(false, |x| x == 7);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use is_ok_and instead: `r.is_ok_and(|x| x == 7)`
+
+error: this `map_or` is redundant
+  --> tests/ui/unnecessary_map_or.rs:60:13
+   |
+LL |     let _ = r.map_or(false, |x| x == 8);
+   |             ^^^^^^^^^^^^^^^^^^^^^^^^^^^ help: use a standard comparison instead: `(r == Ok(8))`
+
+error: aborting due to 13 previous errors
+
diff --git a/src/tools/clippy/tests/ui/unused_io_amount.rs b/src/tools/clippy/tests/ui/unused_io_amount.rs
index f5b200d5ffe..175c4ca7689 100644
--- a/src/tools/clippy/tests/ui/unused_io_amount.rs
+++ b/src/tools/clippy/tests/ui/unused_io_amount.rs
@@ -277,4 +277,18 @@ fn allow_works<F: std::io::Read>(mut f: F) {
     f.read(&mut data).unwrap();
 }
 
+struct Reader {}
+
+impl Read for Reader {
+    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+        todo!()
+    }
+
+    fn read_exact(&mut self, buf: &mut [u8]) -> io::Result<()> {
+        // We shouldn't recommend using Read::read_exact inside Read::read_exact!
+        self.read(buf).unwrap();
+        Ok(())
+    }
+}
+
 fn main() {}
diff --git a/src/tools/clippy/triagebot.toml b/src/tools/clippy/triagebot.toml
index cd9641eedd8..eadfd7107c7 100644
--- a/src/tools/clippy/triagebot.toml
+++ b/src/tools/clippy/triagebot.toml
@@ -21,13 +21,11 @@ new_pr = true
 contributing_url = "https://github.com/rust-lang/rust-clippy/blob/master/CONTRIBUTING.md"
 users_on_vacation = [
     "matthiaskrgr",
-    "giraffate",
 ]
 
 [assign.owners]
 "/.github" = ["@flip1995"]
 "/book" = ["@flip1995"]
-"/util/gh-pages" = ["@xFrednet"]
 "*" = [
     "@Manishearth",
     "@llogiq",
diff --git a/src/tools/clippy/util/gh-pages/index_template.html b/src/tools/clippy/util/gh-pages/index_template.html
index 2412f0fd181..deb0ef0b499 100644
--- a/src/tools/clippy/util/gh-pages/index_template.html
+++ b/src/tools/clippy/util/gh-pages/index_template.html
@@ -52,12 +52,12 @@ Otherwise, have a great day =^.^=
 
         <noscript> {# #}
             <div class="alert alert-danger" role="alert"> {# #}
-                Sorry, this site only works with JavaScript! :( {# #}
+                Lints search and filtering only works with JS enabled. :( {# #}
             </div> {# #}
         </noscript> {# #}
 
         <div> {# #}
-            <div class="panel panel-default"> {# #}
+            <div class="panel panel-default" id="menu-filters"> {# #}
                 <div class="panel-body row"> {# #}
                     <div id="upper-filters" class="col-12 col-md-5"> {# #}
                         <div class="btn-group" id="lint-levels" tabindex="-1"> {# #}
@@ -104,7 +104,7 @@ Otherwise, have a great day =^.^=
                                 <li role="separator" class="divider"></li> {# #}
                             </ul> {# #}
                         </div> {# #}
-                        <div class="btn-group", id="lint-applicabilities" tabindex="-1"> {# #}
+                        <div class="btn-group" id="lint-applicabilities" tabindex="-1"> {# #}
                             <button type="button" class="btn btn-default dropdown-toggle"> {# #}
                                 Applicability {#+ #}
                                 <span class="badge">4</span> {#+ #}
@@ -143,26 +143,29 @@ Otherwise, have a great day =^.^=
                 </div> {# #}
             </div>
             {% for lint in lints %}
-                <article class="panel panel-default collapsed" id="{{lint.id}}"> {# #}
-                    <header class="panel-heading" onclick="expandLint('{{lint.id}}')"> {# #}
-                        <h2 class="panel-title"> {# #}
-                            <div class="panel-title-name" id="lint-{{lint.id}}"> {# #}
-                                <span>{{lint.id}}</span> {#+ #}
-                                <a href="#{{lint.id}}" class="anchor label label-default" onclick="openLint(event)">&para;</a> {#+ #}
-                                <a href="" class="anchor label label-default" onclick="copyToClipboard(event)"> {# #}
-                                    &#128203; {# #}
-                                </a> {# #}
-                            </div> {# #}
+                <article class="panel panel-default" id="{{lint.id}}"> {# #}
+                    <input id="label-{{lint.id}}" type="checkbox"> {# #}
+                    <label for="label-{{lint.id}}" onclick="highlightIfNeeded('{{lint.id}}')"> {# #}
+                        <header class="panel-heading"> {# #}
+                            <h2 class="panel-title"> {# #}
+                                <div class="panel-title-name" id="lint-{{lint.id}}"> {# #}
+                                    <span>{{lint.id}}</span> {#+ #}
+                                    <a href="#{{lint.id}}" onclick="lintAnchor(event)" class="anchor label label-default">&para;</a> {#+ #}
+                                    <a href="" class="anchor label label-default" onclick="copyToClipboard(event)"> {# #}
+                                        &#128203; {# #}
+                                    </a> {# #}
+                                </div> {# #}
 
-                            <div class="panel-title-addons"> {# #}
-                                <span class="label label-lint-group label-default label-group-{{lint.group}}">{{lint.group}}</span> {#+ #}
+                                <div class="panel-title-addons"> {# #}
+                                    <span class="label label-lint-group label-default label-group-{{lint.group}}">{{lint.group}}</span> {#+ #}
 
-                                <span class="label label-lint-level label-lint-level-{{lint.level}}">{{lint.level}}</span> {#+ #}
+                                    <span class="label label-lint-level label-lint-level-{{lint.level}}">{{lint.level}}</span> {#+ #}
 
-                                <span class="label label-doc-folding">&plus;</span> {# #}
-                            </div> {# #}
-                        </h2> {# #}
-                    </header> {# #}
+                                    <span class="label label-doc-folding"></span> {# #}
+                                </div> {# #}
+                            </h2> {# #}
+                        </header> {# #}
+                    </label> {# #}
 
                     <div class="list-group lint-docs"> {# #}
                         <div class="list-group-item lint-doc-md">{{Self::markdown(lint.docs)}}</div> {# #}
diff --git a/src/tools/clippy/util/gh-pages/script.js b/src/tools/clippy/util/gh-pages/script.js
index cc22a39b3d1..9a5365b2158 100644
--- a/src/tools/clippy/util/gh-pages/script.js
+++ b/src/tools/clippy/util/gh-pages/script.js
@@ -138,25 +138,27 @@ function onEachLazy(lazyArray, func) {
     }
 }
 
-function highlightIfNeeded(elem) {
-    onEachLazy(elem.querySelectorAll("pre > code.language-rust:not(.highlighted)"), el => {
+function highlightIfNeeded(lintId) {
+    onEachLazy(document.querySelectorAll(`#${lintId} pre > code:not(.hljs)`), el => {
         hljs.highlightElement(el.parentElement)
         el.classList.add("highlighted");
     });
 }
 
 function expandLint(lintId) {
-    const lintElem = document.getElementById(lintId);
-    const isCollapsed = lintElem.classList.toggle("collapsed");
-    lintElem.querySelector(".label-doc-folding").innerText = isCollapsed ? "+" : "−";
-    highlightIfNeeded(lintElem);
+    const elem = document.querySelector(`#${lintId} > input[type="checkbox"]`);
+    elem.checked = true;
+    highlightIfNeeded(lintId);
 }
 
-// Show details for one lint
-function openLint(event) {
+function lintAnchor(event) {
     event.preventDefault();
     event.stopPropagation();
-    expandLint(event.target.getAttribute("href").slice(1));
+
+    const id = event.target.getAttribute("href").replace("#", "");
+    window.location.hash = id;
+
+    expandLint(id);
 }
 
 function copyToClipboard(event) {
@@ -520,7 +522,7 @@ function scrollToLint(lintId) {
 
 // If the page we arrive on has link to a given lint, we scroll to it.
 function scrollToLintByURL() {
-    const lintId = window.location.hash.substring(2);
+    const lintId = window.location.hash.substring(1);
     if (lintId.length > 0) {
         scrollToLint(lintId);
     }
diff --git a/src/tools/clippy/util/gh-pages/style.css b/src/tools/clippy/util/gh-pages/style.css
index a68a10b1401..896f2fdac76 100644
--- a/src/tools/clippy/util/gh-pages/style.css
+++ b/src/tools/clippy/util/gh-pages/style.css
@@ -204,7 +204,7 @@ details[open] {
 }
 
 /* Expanding the mdBook theme*/
-.light {
+.light, body:not([class]) {
     --inline-code-bg: #f6f7f6;
 }
 .rust {
@@ -220,6 +220,21 @@ details[open] {
     --inline-code-bg: #191f26;
 }
 
+@media (prefers-color-scheme: dark) {
+    body:not([class]) {
+        /*
+        In case JS is disabled and the user's system is in dark mode, we take "coal" as default
+        dark theme.
+        */
+        --inline-code-bg: #1d1f21;
+    }
+}
+
+html:not(.js) #settings-dropdown,
+html:not(.js)#menu-filters {
+    display: none;
+}
+
 #settings-dropdown {
     position: absolute;
     margin: 0.7em;
@@ -309,12 +324,12 @@ L4.75,12h2.5l0.5393066-2.1572876  c0.2276001-0.1062012,0.4459839-0.2269287,0.649
 .page-header {
     border-color: var(--theme-popup-border);
 }
-.panel-default > .panel-heading {
+.panel-default .panel-heading {
     background: var(--theme-hover);
     color: var(--fg);
     border: 1px solid var(--theme-popup-border);
 }
-.panel-default > .panel-heading:hover {
+.panel-default .panel-heading:hover {
     filter: brightness(90%);
 }
 .list-group-item {
@@ -410,9 +425,25 @@ body {
     color: var(--fg);
 }
 
-article.collapsed .lint-docs {
+article > label {
+    width: 100%;
+    margin: 0;
+}
+article > input[type="checkbox"] {
     display: none;
 }
+article > input[type="checkbox"] + label .label-doc-folding::before {
+    content: "+";
+}
+article > input[type="checkbox"]:checked + label .label-doc-folding::before {
+    content: "−";
+}
+.lint-docs {
+    display: none;
+}
+article > input[type="checkbox"]:checked ~ .lint-docs {
+    display: block;
+}
 
 .github-corner svg {
     fill: var(--fg);
diff --git a/src/tools/clippy/util/gh-pages/theme.js b/src/tools/clippy/util/gh-pages/theme.js
index bc296955ddf..90f57d4469d 100644
--- a/src/tools/clippy/util/gh-pages/theme.js
+++ b/src/tools/clippy/util/gh-pages/theme.js
@@ -45,6 +45,10 @@ function setTheme(theme, store) {
 }
 
 (function() {
+    // This file is loaded first. If so, we add the `js` class on the `<html>`
+    // element.
+    document.documentElement.classList.add("js");
+
     // loading the theme after the initial load
     const prefersDark = window.matchMedia("(prefers-color-scheme: dark)");
     const theme = loadValue("theme");
diff --git a/src/tools/collect-license-metadata/src/main.rs b/src/tools/collect-license-metadata/src/main.rs
index dce36bb17b6..08a30d0b899 100644
--- a/src/tools/collect-license-metadata/src/main.rs
+++ b/src/tools/collect-license-metadata/src/main.rs
@@ -4,7 +4,7 @@ mod reuse;
 
 use std::path::PathBuf;
 
-use anyhow::Error;
+use anyhow::{Context, Error};
 
 use crate::licenses::LicensesInterner;
 
@@ -12,10 +12,12 @@ use crate::licenses::LicensesInterner;
 ///
 /// You should probably let `bootstrap` execute this program instead of running it directly.
 ///
-/// Run `x.py run collect-license-metadata`
+/// * Run `x.py run collect-license-metadata` to re-regenerate the file.
+/// * Run `x.py test collect-license-metadata` to check if the file you have is correct.
 fn main() -> Result<(), Error> {
     let reuse_exe: PathBuf = std::env::var_os("REUSE_EXE").expect("Missing REUSE_EXE").into();
     let dest: PathBuf = std::env::var_os("DEST").expect("Missing DEST").into();
+    let only_check = std::env::var_os("ONLY_CHECK").is_some();
 
     let mut interner = LicensesInterner::new();
     let paths = crate::reuse::collect(&reuse_exe, &mut interner)?;
@@ -23,15 +25,32 @@ fn main() -> Result<(), Error> {
     let mut tree = crate::path_tree::build(paths);
     tree.simplify();
 
-    if let Some(parent) = dest.parent() {
-        std::fs::create_dir_all(parent)?;
+    let output = serde_json::json!({
+        "files": crate::path_tree::expand_interned_licenses(tree, &interner)
+    });
+
+    if only_check {
+        println!("loading existing license information");
+        let existing = std::fs::read_to_string(&dest).with_context(|| {
+            format!("Failed to read existing license JSON at {}", dest.display())
+        })?;
+        let existing_json: serde_json::Value =
+            serde_json::from_str(&existing).with_context(|| {
+                format!("Failed to read existing license JSON at {}", dest.display())
+            })?;
+        if existing_json != output {
+            eprintln!("The existing {} file is out of date.", dest.display());
+            eprintln!("Run ./x run collect-license-metadata to update it.");
+            anyhow::bail!("The existing {} file doesn't match what REUSE reports.", dest.display());
+        }
+        println!("license information matches");
+    } else {
+        if let Some(parent) = dest.parent() {
+            std::fs::create_dir_all(parent)?;
+        }
+        std::fs::write(&dest, &serde_json::to_vec_pretty(&output)?)?;
+        println!("license information written to {}", dest.display());
     }
-    std::fs::write(
-        &dest,
-        &serde_json::to_vec_pretty(&serde_json::json!({
-            "files": crate::path_tree::expand_interned_licenses(tree, &interner),
-        }))?,
-    )?;
 
     Ok(())
 }
diff --git a/src/tools/collect-license-metadata/src/reuse.rs b/src/tools/collect-license-metadata/src/reuse.rs
index e5ee8f0da5e..dbe46781b7c 100644
--- a/src/tools/collect-license-metadata/src/reuse.rs
+++ b/src/tools/collect-license-metadata/src/reuse.rs
@@ -10,10 +10,10 @@ pub(crate) fn collect(
     reuse_exe: &Path,
     interner: &mut LicensesInterner,
 ) -> Result<Vec<(PathBuf, LicenseId)>, Error> {
-    eprintln!("gathering license information from REUSE");
+    println!("gathering license information from REUSE (this might take a minute...)");
     let start = Instant::now();
     let raw = &obtain_spdx_document(reuse_exe)?;
-    eprintln!("finished gathering the license information from REUSE in {:.2?}", start.elapsed());
+    println!("finished gathering the license information from REUSE in {:.2?}", start.elapsed());
 
     let document = spdx_rs::parsers::spdx_from_tag_value(&raw)?;
 
diff --git a/src/tools/compiletest/Cargo.toml b/src/tools/compiletest/Cargo.toml
index cef6b525a7b..b784bdb7139 100644
--- a/src/tools/compiletest/Cargo.toml
+++ b/src/tools/compiletest/Cargo.toml
@@ -14,7 +14,7 @@ unified-diff = "0.2.1"
 getopts = "0.2"
 indexmap = "2.0.0"
 miropt-test-tools = { path = "../miropt-test-tools" }
-build_helper = { path = "../build_helper" }
+build_helper = { path = "../../build_helper" }
 tracing = "0.1"
 tracing-subscriber = { version = "0.3.3", default-features = false, features = ["fmt", "env-filter", "smallvec", "parking_lot", "ansi"] }
 regex = "1.0"
diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs
index e4f2f95a91b..e6fbba943a4 100644
--- a/src/tools/compiletest/src/common.rs
+++ b/src/tools/compiletest/src/common.rs
@@ -236,8 +236,11 @@ pub struct Config {
     /// Run ignored tests
     pub run_ignored: bool,
 
-    /// Whether to run tests with `ignore-debug` header
-    pub with_debug_assertions: bool,
+    /// Whether rustc was built with debug assertions.
+    pub with_rustc_debug_assertions: bool,
+
+    /// Whether std was built with debug assertions.
+    pub with_std_debug_assertions: bool,
 
     /// Only run tests that match these filters
     pub filters: Vec<String>,
@@ -761,7 +764,7 @@ pub const UI_COVERAGE_MAP: &str = "cov-map";
 
 /// Absolute path to the directory where all output for all tests in the given
 /// `relative_dir` group should reside. Example:
-///   /path/to/build/host-triple/test/ui/relative/
+///   /path/to/build/host-tuple/test/ui/relative/
 /// This is created early when tests are collected to avoid race conditions.
 pub fn output_relative_path(config: &Config, relative_dir: &Path) -> PathBuf {
     config.build_base.join(relative_dir)
@@ -784,7 +787,7 @@ pub fn output_testname_unique(
 
 /// Absolute path to the directory where all output for the given
 /// test/revision should reside. Example:
-///   /path/to/build/host-triple/test/ui/relative/testname.revision.mode/
+///   /path/to/build/host-tuple/test/ui/relative/testname.revision.mode/
 pub fn output_base_dir(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
     output_relative_path(config, &testpaths.relative_dir)
         .join(output_testname_unique(config, testpaths, revision))
@@ -792,13 +795,13 @@ pub fn output_base_dir(config: &Config, testpaths: &TestPaths, revision: Option<
 
 /// Absolute path to the base filename used as output for the given
 /// test/revision. Example:
-///   /path/to/build/host-triple/test/ui/relative/testname.revision.mode/testname
+///   /path/to/build/host-tuple/test/ui/relative/testname.revision.mode/testname
 pub fn output_base_name(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
     output_base_dir(config, testpaths, revision).join(testpaths.file.file_stem().unwrap())
 }
 
 /// Absolute path to the directory to use for incremental compilation. Example:
-///   /path/to/build/host-triple/test/ui/relative/testname.mode/testname.inc
+///   /path/to/build/host-tuple/test/ui/relative/testname.mode/testname.inc
 pub fn incremental_dir(config: &Config, testpaths: &TestPaths, revision: Option<&str>) -> PathBuf {
     output_base_name(config, testpaths, revision).with_extension("inc")
 }
diff --git a/src/tools/compiletest/src/directive-list.rs b/src/tools/compiletest/src/directive-list.rs
index 980b3f6829a..0c47ef871d2 100644
--- a/src/tools/compiletest/src/directive-list.rs
+++ b/src/tools/compiletest/src/directive-list.rs
@@ -25,6 +25,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "dont-check-failure-status",
     "edition",
     "error-pattern",
+    "exact-llvm-major-version",
     "exec-env",
     "failure-status",
     "filecheck-flags",
@@ -35,6 +36,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "ignore-64bit",
     "ignore-aarch64",
     "ignore-aarch64-unknown-linux-gnu",
+    "ignore-aix",
     "ignore-android",
     "ignore-apple",
     "ignore-arm",
@@ -46,7 +48,6 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "ignore-coverage-map",
     "ignore-coverage-run",
     "ignore-cross-compile",
-    "ignore-debug",
     "ignore-eabi",
     "ignore-emscripten",
     "ignore-endian-big",
@@ -82,6 +83,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "ignore-powerpc",
     "ignore-remote",
     "ignore-riscv64",
+    "ignore-rustc-debug-assertions",
     "ignore-s390x",
     "ignore-sgx",
     "ignore-sparc64",
@@ -89,6 +91,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "ignore-stable",
     "ignore-stage1",
     "ignore-stage2",
+    "ignore-std-debug-assertions",
     "ignore-test",
     "ignore-thumb",
     "ignore-thumbv8m.base-none-eabi",
@@ -117,6 +120,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "incremental",
     "known-bug",
     "llvm-cov-flags",
+    "max-llvm-major-version",
     "min-cdb-version",
     "min-gdb-version",
     "min-lldb-version",
@@ -135,6 +139,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "needs-relocation-model-pic",
     "needs-run-enabled",
     "needs-rust-lld",
+    "needs-rustc-debug-assertions",
     "needs-sanitizer-address",
     "needs-sanitizer-cfi",
     "needs-sanitizer-dataflow",
@@ -147,6 +152,7 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "needs-sanitizer-shadow-call-stack",
     "needs-sanitizer-support",
     "needs-sanitizer-thread",
+    "needs-std-debug-assertions",
     "needs-symlink",
     "needs-threads",
     "needs-unwind",
@@ -208,7 +214,6 @@ const KNOWN_DIRECTIVE_NAMES: &[&str] = &[
     "only-x86_64-unknown-linux-gnu",
     "pp-exact",
     "pretty-compare-only",
-    "pretty-expanded",
     "pretty-mode",
     "reference",
     "regex-error-pattern",
diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs
index 300a03e5f33..e945797647e 100644
--- a/src/tools/compiletest/src/header.rs
+++ b/src/tools/compiletest/src/header.rs
@@ -124,8 +124,6 @@ pub struct TestProps {
     // a proc-macro and needs `#![crate_type = "proc-macro"]`. This ensures
     // that the aux file is compiled as a `proc-macro` and not as a `dylib`.
     pub no_prefer_dynamic: bool,
-    // Run -Zunpretty expanded when running pretty printing tests
-    pub pretty_expanded: bool,
     // Which pretty mode are we testing with, default to 'normal'
     pub pretty_mode: String,
     // Only compare pretty output and don't try compiling
@@ -218,7 +216,6 @@ mod directives {
     pub const DONT_CHECK_COMPILER_STDOUT: &'static str = "dont-check-compiler-stdout";
     pub const DONT_CHECK_COMPILER_STDERR: &'static str = "dont-check-compiler-stderr";
     pub const NO_PREFER_DYNAMIC: &'static str = "no-prefer-dynamic";
-    pub const PRETTY_EXPANDED: &'static str = "pretty-expanded";
     pub const PRETTY_MODE: &'static str = "pretty-mode";
     pub const PRETTY_COMPARE_ONLY: &'static str = "pretty-compare-only";
     pub const AUX_BIN: &'static str = "aux-bin";
@@ -278,7 +275,6 @@ impl TestProps {
             dont_check_compiler_stderr: false,
             compare_output_lines_by_subset: false,
             no_prefer_dynamic: false,
-            pretty_expanded: false,
             pretty_mode: "normal".to_string(),
             pretty_compare_only: false,
             forbid_output: vec![],
@@ -425,7 +421,6 @@ impl TestProps {
                         &mut self.dont_check_compiler_stderr,
                     );
                     config.set_name_directive(ln, NO_PREFER_DYNAMIC, &mut self.no_prefer_dynamic);
-                    config.set_name_directive(ln, PRETTY_EXPANDED, &mut self.pretty_expanded);
 
                     if let Some(m) = config.parse_name_value_directive(ln, PRETTY_MODE) {
                         self.pretty_mode = m;
@@ -1547,6 +1542,20 @@ fn ignore_llvm(config: &Config, line: &str) -> IgnoreDecision {
                 };
             }
         } else if let Some(version_string) =
+            config.parse_name_value_directive(line, "max-llvm-major-version")
+        {
+            let max_version = extract_llvm_version(&version_string);
+            // Ignore if actual major version is larger than the maximum required major version.
+            if actual_version.major > max_version.major {
+                return IgnoreDecision::Ignore {
+                    reason: format!(
+                        "ignored when the LLVM version ({actual_version}) is newer than major\
+                        version {}",
+                        max_version.major
+                    ),
+                };
+            }
+        } else if let Some(version_string) =
             config.parse_name_value_directive(line, "min-system-llvm-version")
         {
             let min_version = extract_llvm_version(&version_string);
@@ -1585,6 +1594,19 @@ fn ignore_llvm(config: &Config, line: &str) -> IgnoreDecision {
                     };
                 }
             }
+        } else if let Some(version_string) =
+            config.parse_name_value_directive(line, "exact-llvm-major-version")
+        {
+            // Syntax is "exact-llvm-major-version: <version>"
+            let version = extract_llvm_version(&version_string);
+            if actual_version.major != version.major {
+                return IgnoreDecision::Ignore {
+                    reason: format!(
+                        "ignored when the actual LLVM major version is {}, but the test only targets major version {}",
+                        actual_version.major, version.major
+                    ),
+                };
+            }
         }
     }
     IgnoreDecision::Continue
diff --git a/src/tools/compiletest/src/header/cfg.rs b/src/tools/compiletest/src/header/cfg.rs
index b9314f0abbb..3ab552903dc 100644
--- a/src/tools/compiletest/src/header/cfg.rs
+++ b/src/tools/compiletest/src/header/cfg.rs
@@ -202,9 +202,14 @@ pub(super) fn parse_cfg_name_directive<'a>(
         message: "when running tests remotely",
     }
     condition! {
-        name: "debug",
-        condition: config.with_debug_assertions,
-        message: "when running tests with `ignore-debug` header",
+        name: "rustc-debug-assertions",
+        condition: config.with_rustc_debug_assertions,
+        message: "when rustc is built with debug assertions",
+    }
+    condition! {
+        name: "std-debug-assertions",
+        condition: config.with_std_debug_assertions,
+        message: "when std is built with debug assertions",
     }
     condition! {
         name: config.debugger.as_ref().map(|d| d.to_str()),
diff --git a/src/tools/compiletest/src/header/needs.rs b/src/tools/compiletest/src/header/needs.rs
index a744fb61b9c..77570c58db5 100644
--- a/src/tools/compiletest/src/header/needs.rs
+++ b/src/tools/compiletest/src/header/needs.rs
@@ -159,6 +159,16 @@ pub(super) fn handle_needs(
             condition: cache.llvm_zstd,
             ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression",
         },
+        Need {
+            name: "needs-rustc-debug-assertions",
+            condition: config.with_rustc_debug_assertions,
+            ignore_reason: "ignored if rustc wasn't built with debug assertions",
+        },
+        Need {
+            name: "needs-std-debug-assertions",
+            condition: config.with_std_debug_assertions,
+            ignore_reason: "ignored if std wasn't built with debug assertions",
+        },
     ];
 
     let (name, comment) = match ln.split_once([':', ' ']) {
diff --git a/src/tools/compiletest/src/header/tests.rs b/src/tools/compiletest/src/header/tests.rs
index 0e735dc77c4..4d75c38dd32 100644
--- a/src/tools/compiletest/src/header/tests.rs
+++ b/src/tools/compiletest/src/header/tests.rs
@@ -74,6 +74,8 @@ struct ConfigBuilder {
     git_hash: bool,
     system_llvm: bool,
     profiler_runtime: bool,
+    rustc_debug_assertions: bool,
+    std_debug_assertions: bool,
 }
 
 impl ConfigBuilder {
@@ -122,6 +124,16 @@ impl ConfigBuilder {
         self
     }
 
+    fn rustc_debug_assertions(&mut self, is_enabled: bool) -> &mut Self {
+        self.rustc_debug_assertions = is_enabled;
+        self
+    }
+
+    fn std_debug_assertions(&mut self, is_enabled: bool) -> &mut Self {
+        self.std_debug_assertions = is_enabled;
+        self
+    }
+
     fn build(&mut self) -> Config {
         let args = &[
             "compiletest",
@@ -170,6 +182,12 @@ impl ConfigBuilder {
         if self.profiler_runtime {
             args.push("--profiler-runtime".to_owned());
         }
+        if self.rustc_debug_assertions {
+            args.push("--with-rustc-debug-assertions".to_owned());
+        }
+        if self.std_debug_assertions {
+            args.push("--with-std-debug-assertions".to_owned());
+        }
 
         args.push("--rustc-path".to_string());
         // This is a subtle/fragile thing. On rust-lang CI, there is no global
@@ -266,6 +284,30 @@ fn llvm_version() {
 
     let config: Config = cfg().llvm_version("10.0.0").build();
     assert!(!check_ignore(&config, "//@ min-llvm-version: 9.0"));
+
+    let config: Config = cfg().llvm_version("10.0.0").build();
+    assert!(check_ignore(&config, "//@ exact-llvm-major-version: 9.0"));
+
+    let config: Config = cfg().llvm_version("9.0.0").build();
+    assert!(check_ignore(&config, "//@ exact-llvm-major-version: 10.0"));
+
+    let config: Config = cfg().llvm_version("10.0.0").build();
+    assert!(!check_ignore(&config, "//@ exact-llvm-major-version: 10.0"));
+
+    let config: Config = cfg().llvm_version("10.0.0").build();
+    assert!(!check_ignore(&config, "//@ exact-llvm-major-version: 10"));
+
+    let config: Config = cfg().llvm_version("10.6.2").build();
+    assert!(!check_ignore(&config, "//@ exact-llvm-major-version: 10"));
+
+    let config: Config = cfg().llvm_version("19.0.0").build();
+    assert!(!check_ignore(&config, "//@ max-llvm-major-version: 19"));
+
+    let config: Config = cfg().llvm_version("19.1.2").build();
+    assert!(!check_ignore(&config, "//@ max-llvm-major-version: 19"));
+
+    let config: Config = cfg().llvm_version("20.0.0").build();
+    assert!(check_ignore(&config, "//@ max-llvm-major-version: 19"));
 }
 
 #[test]
@@ -315,6 +357,32 @@ fn only_target() {
 }
 
 #[test]
+fn rustc_debug_assertions() {
+    let config: Config = cfg().rustc_debug_assertions(false).build();
+
+    assert!(check_ignore(&config, "//@ needs-rustc-debug-assertions"));
+    assert!(!check_ignore(&config, "//@ ignore-rustc-debug-assertions"));
+
+    let config: Config = cfg().rustc_debug_assertions(true).build();
+
+    assert!(!check_ignore(&config, "//@ needs-rustc-debug-assertions"));
+    assert!(check_ignore(&config, "//@ ignore-rustc-debug-assertions"));
+}
+
+#[test]
+fn std_debug_assertions() {
+    let config: Config = cfg().std_debug_assertions(false).build();
+
+    assert!(check_ignore(&config, "//@ needs-std-debug-assertions"));
+    assert!(!check_ignore(&config, "//@ ignore-std-debug-assertions"));
+
+    let config: Config = cfg().std_debug_assertions(true).build();
+
+    assert!(!check_ignore(&config, "//@ needs-std-debug-assertions"));
+    assert!(check_ignore(&config, "//@ ignore-std-debug-assertions"));
+}
+
+#[test]
 fn stage() {
     let config: Config = cfg().stage_id("stage1-x86_64-unknown-linux-gnu").build();
 
@@ -583,10 +651,6 @@ fn wasm_special() {
         ("wasm32-unknown-emscripten", "emscripten", true),
         ("wasm32-unknown-emscripten", "wasm32", true),
         ("wasm32-unknown-emscripten", "wasm32-bare", false),
-        ("wasm32-wasi", "emscripten", false),
-        ("wasm32-wasi", "wasm32", true),
-        ("wasm32-wasi", "wasm32-bare", false),
-        ("wasm32-wasi", "wasi", true),
         ("wasm32-wasip1", "emscripten", false),
         ("wasm32-wasip1", "wasm32", true),
         ("wasm32-wasip1", "wasm32-bare", false),
diff --git a/src/tools/compiletest/src/lib.rs b/src/tools/compiletest/src/lib.rs
index 5c06a39c477..bf4a3124075 100644
--- a/src/tools/compiletest/src/lib.rs
+++ b/src/tools/compiletest/src/lib.rs
@@ -88,7 +88,8 @@ pub fn parse_config(args: Vec<String>) -> Config {
         .optopt("", "run", "whether to execute run-* tests", "auto | always | never")
         .optflag("", "ignored", "run tests marked as ignored")
         .optflag("", "has-enzyme", "run tests that require enzyme")
-        .optflag("", "with-debug-assertions", "whether to run tests with `ignore-debug` header")
+        .optflag("", "with-rustc-debug-assertions", "whether rustc was built with debug assertions")
+        .optflag("", "with-std-debug-assertions", "whether std was built with debug assertions")
         .optmulti(
             "",
             "skip",
@@ -235,7 +236,8 @@ pub fn parse_config(args: Vec<String>) -> Config {
 
     let src_base = opt_path(matches, "src-base");
     let run_ignored = matches.opt_present("ignored");
-    let with_debug_assertions = matches.opt_present("with-debug-assertions");
+    let with_rustc_debug_assertions = matches.opt_present("with-rustc-debug-assertions");
+    let with_std_debug_assertions = matches.opt_present("with-std-debug-assertions");
     let mode = matches.opt_str("mode").unwrap().parse().expect("invalid mode");
     let has_html_tidy = if mode == Mode::Rustdoc {
         Command::new("tidy")
@@ -293,7 +295,8 @@ pub fn parse_config(args: Vec<String>) -> Config {
         suite: matches.opt_str("suite").unwrap(),
         debugger: None,
         run_ignored,
-        with_debug_assertions,
+        with_rustc_debug_assertions,
+        with_std_debug_assertions,
         filters,
         skip: matches.opt_strs("skip"),
         filter_exact: matches.opt_present("exact"),
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index b337458f943..bc80c8246ad 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1825,7 +1825,7 @@ impl<'test> TestCx<'test> {
 
     /// Gets the absolute path to the directory where all output for the given
     /// test/revision should reside.
-    /// E.g., `/path/to/build/host-triple/test/ui/relative/testname.revision.mode/`.
+    /// E.g., `/path/to/build/host-tuple/test/ui/relative/testname.revision.mode/`.
     fn output_base_dir(&self) -> PathBuf {
         output_base_dir(self.config, self.testpaths, self.safe_revision())
     }
diff --git a/src/tools/compiletest/src/runtest/pretty.rs b/src/tools/compiletest/src/runtest/pretty.rs
index 40e767e84ef..e3b07f1d63d 100644
--- a/src/tools/compiletest/src/runtest/pretty.rs
+++ b/src/tools/compiletest/src/runtest/pretty.rs
@@ -84,21 +84,5 @@ impl TestCx<'_> {
         if !proc_res.status.success() {
             self.fatal_proc_rec("pretty-printed source does not typecheck", &proc_res);
         }
-
-        if !self.props.pretty_expanded {
-            return;
-        }
-
-        // additionally, run `-Zunpretty=expanded` and try to build it.
-        let proc_res = self.print_source(ReadFrom::Path, "expanded");
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("pretty-printing (expanded) failed", &proc_res);
-        }
-
-        let ProcRes { stdout: expanded_src, .. } = proc_res;
-        let proc_res = self.typecheck_source(expanded_src);
-        if !proc_res.status.success() {
-            self.fatal_proc_rec("pretty-printed source (expanded) does not typecheck", &proc_res);
-        }
     }
 }
diff --git a/src/tools/generate-copyright/src/cargo_metadata.rs b/src/tools/generate-copyright/src/cargo_metadata.rs
index 31b18c3dc10..420579372ac 100644
--- a/src/tools/generate-copyright/src/cargo_metadata.rs
+++ b/src/tools/generate-copyright/src/cargo_metadata.rs
@@ -45,21 +45,20 @@ pub struct PackageMetadata {
 
 /// Use `cargo metadata` and `cargo vendor` to get a list of dependencies and their license data.
 ///
-/// This will involve running `cargo vendor` into `${BUILD}/vendor` so we can
+/// This will involve running `cargo vendor` into `vendor_path` so we can
 /// grab the license files.
 ///
 /// Any dependency with a path beginning with `root_path` is ignored, as we
 /// assume `reuse` has covered it already.
 pub fn get_metadata_and_notices(
     cargo: &Path,
-    dest: &Path,
+    vendor_path: &Path,
     root_path: &Path,
     manifest_paths: &[&Path],
 ) -> Result<BTreeMap<Package, PackageMetadata>, Error> {
     let mut output = get_metadata(cargo, root_path, manifest_paths)?;
 
     // Now do a cargo-vendor and grab everything
-    let vendor_path = dest.join("vendor");
     println!("Vendoring deps into {}...", vendor_path.display());
     run_cargo_vendor(cargo, &vendor_path, manifest_paths)?;
 
diff --git a/src/tools/generate-copyright/src/main.rs b/src/tools/generate-copyright/src/main.rs
index afa75d0d671..f83d16d0cab 100644
--- a/src/tools/generate-copyright/src/main.rs
+++ b/src/tools/generate-copyright/src/main.rs
@@ -6,13 +6,6 @@ use rinja::Template;
 
 mod cargo_metadata;
 
-#[derive(Template)]
-#[template(path = "COPYRIGHT.html")]
-struct CopyrightTemplate {
-    in_tree: Node,
-    dependencies: BTreeMap<cargo_metadata::Package, cargo_metadata::PackageMetadata>,
-}
-
 /// The entry point to the binary.
 ///
 /// You should probably let `bootstrap` execute this program instead of running it directly.
@@ -20,49 +13,97 @@ struct CopyrightTemplate {
 /// Run `x.py run generate-copyright`
 fn main() -> Result<(), Error> {
     let dest_file = env_path("DEST")?;
+    let libstd_dest_file = env_path("DEST_LIBSTD")?;
     let out_dir = env_path("OUT_DIR")?;
     let cargo = env_path("CARGO")?;
     let license_metadata = env_path("LICENSE_METADATA")?;
 
-    let collected_tree_metadata: Metadata =
-        serde_json::from_slice(&std::fs::read(&license_metadata)?)?;
-
     let root_path = std::path::absolute(".")?;
-    let workspace_paths = [
-        Path::new("./Cargo.toml"),
-        Path::new("./src/tools/cargo/Cargo.toml"),
-        Path::new("./library/Cargo.toml"),
-    ];
-    let mut collected_cargo_metadata =
-        cargo_metadata::get_metadata_and_notices(&cargo, &out_dir, &root_path, &workspace_paths)?;
 
-    let stdlib_set =
-        cargo_metadata::get_metadata(&cargo, &root_path, &[Path::new("./library/std/Cargo.toml")])?;
+    // Scan Cargo dependencies
+    let mut collected_cargo_metadata =
+        cargo_metadata::get_metadata_and_notices(&cargo, &out_dir.join("vendor"), &root_path, &[
+            Path::new("./Cargo.toml"),
+            Path::new("./src/tools/cargo/Cargo.toml"),
+            Path::new("./library/Cargo.toml"),
+        ])?;
+
+    let library_collected_cargo_metadata = cargo_metadata::get_metadata_and_notices(
+        &cargo,
+        &out_dir.join("library-vendor"),
+        &root_path,
+        &[Path::new("./library/Cargo.toml")],
+    )?;
 
     for (key, value) in collected_cargo_metadata.iter_mut() {
-        value.is_in_libstd = Some(stdlib_set.contains_key(key));
+        value.is_in_libstd = Some(library_collected_cargo_metadata.contains_key(key));
     }
 
+    // Load JSON output by reuse
+    let collected_tree_metadata: Metadata =
+        serde_json::from_slice(&std::fs::read(&license_metadata)?)?;
+
+    // Find libstd sub-set
+    let library_collected_tree_metadata = Metadata {
+        files: collected_tree_metadata
+            .files
+            .trim_clone(&Path::new("./library"), &Path::new("."))
+            .unwrap(),
+    };
+
+    // Output main file
     let template = CopyrightTemplate {
         in_tree: collected_tree_metadata.files,
         dependencies: collected_cargo_metadata,
     };
-
     let output = template.render()?;
-
+    // Git stores text files with \n, but this file may contain \r\n in files
+    // copied from dependencies. Normalise them before we write them out, for
+    // consistency.
+    let output = output.replace("\r\n", "\n");
     std::fs::write(&dest_file, output)?;
 
+    // Output libstd subset file
+    let template = LibraryCopyrightTemplate {
+        in_tree: library_collected_tree_metadata.files,
+        dependencies: library_collected_cargo_metadata,
+    };
+    let output = template.render()?;
+    // Git stores text files with \n, but this file may contain \r\n in files
+    // copied from dependencies. Normalise them before we write them out, for
+    // consistency.
+    let output = output.replace("\r\n", "\n");
+    std::fs::write(&libstd_dest_file, output)?;
+
     Ok(())
 }
 
+/// The HTML template for the toolchain copyright file
+#[derive(Template)]
+#[template(path = "COPYRIGHT.html")]
+struct CopyrightTemplate {
+    in_tree: Node,
+    dependencies: BTreeMap<cargo_metadata::Package, cargo_metadata::PackageMetadata>,
+}
+
+/// The HTML template for the library copyright file
+#[derive(Template)]
+#[template(path = "COPYRIGHT-library.html")]
+struct LibraryCopyrightTemplate {
+    in_tree: Node,
+    dependencies: BTreeMap<cargo_metadata::Package, cargo_metadata::PackageMetadata>,
+}
+
 /// Describes a tree of metadata for our filesystem tree
-#[derive(serde::Deserialize)]
+///
+/// Must match the JSON emitted by the `CollectLicenseMetadata` bootstrap tool.
+#[derive(serde::Deserialize, Clone, Debug, PartialEq, Eq)]
 struct Metadata {
     files: Node,
 }
 
 /// Describes one node in our metadata tree
-#[derive(serde::Deserialize, rinja::Template)]
+#[derive(serde::Deserialize, rinja::Template, Clone, Debug, PartialEq, Eq)]
 #[serde(rename_all = "kebab-case", tag = "type")]
 #[template(path = "Node.html")]
 pub(crate) enum Node {
@@ -72,8 +113,74 @@ pub(crate) enum Node {
     Group { files: Vec<String>, directories: Vec<String>, license: License },
 }
 
+impl Node {
+    /// Clone, this node, but only if the path to the item is within the match path
+    fn trim_clone(&self, match_path: &Path, parent_path: &Path) -> Option<Node> {
+        match self {
+            Node::Root { children } => {
+                let mut filtered_children = Vec::new();
+                for node in children {
+                    if let Some(child_node) = node.trim_clone(match_path, parent_path) {
+                        filtered_children.push(child_node);
+                    }
+                }
+                if filtered_children.is_empty() {
+                    None
+                } else {
+                    Some(Node::Root { children: filtered_children })
+                }
+            }
+            Node::Directory { name, children, license } => {
+                let child_name = parent_path.join(name);
+                if !(child_name.starts_with(match_path) || match_path.starts_with(&child_name)) {
+                    return None;
+                }
+                let mut filtered_children = Vec::new();
+                for node in children {
+                    if let Some(child_node) = node.trim_clone(match_path, &child_name) {
+                        filtered_children.push(child_node);
+                    }
+                }
+                Some(Node::Directory {
+                    name: name.clone(),
+                    children: filtered_children,
+                    license: license.clone(),
+                })
+            }
+            Node::File { name, license } => {
+                let child_name = parent_path.join(name);
+                if !(child_name.starts_with(match_path) || match_path.starts_with(&child_name)) {
+                    return None;
+                }
+                Some(Node::File { name: name.clone(), license: license.clone() })
+            }
+            Node::Group { files, directories, license } => {
+                let mut filtered_child_files = Vec::new();
+                for child in files {
+                    let child_name = parent_path.join(child);
+                    if child_name.starts_with(match_path) || match_path.starts_with(&child_name) {
+                        filtered_child_files.push(child.clone());
+                    }
+                }
+                let mut filtered_child_dirs = Vec::new();
+                for child in directories {
+                    let child_name = parent_path.join(child);
+                    if child_name.starts_with(match_path) || match_path.starts_with(&child_name) {
+                        filtered_child_dirs.push(child.clone());
+                    }
+                }
+                Some(Node::Group {
+                    files: filtered_child_files,
+                    directories: filtered_child_dirs,
+                    license: license.clone(),
+                })
+            }
+        }
+    }
+}
+
 /// A License has an SPDX license name and a list of copyright holders.
-#[derive(serde::Deserialize)]
+#[derive(serde::Deserialize, Clone, Debug, PartialEq, Eq)]
 struct License {
     spdx: String,
     copyright: Vec<String>,
diff --git a/src/tools/generate-copyright/templates/COPYRIGHT-library.html b/src/tools/generate-copyright/templates/COPYRIGHT-library.html
new file mode 100644
index 00000000000..2c1eba741db
--- /dev/null
+++ b/src/tools/generate-copyright/templates/COPYRIGHT-library.html
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <meta charset="UTF-8">
+    <title>Copyright notices for The Rust Standard Library</title>
+</head>
+<body>
+
+<h1>Copyright notices for The Rust Standard Library</h1>
+
+<p>This file describes the copyright and licensing information for the Rust
+Standard Library source code within The Rust Project git tree, and the
+third-party dependencies used when building the Rust Standard Library.</p>
+
+<h2>Table of Contents</h2>
+<ul>
+    <li><a href="#in-tree-files">In-tree files</a></li>
+    <li><a href="#out-of-tree-dependencies">Out-of-tree dependencies</a></li>
+</ul>
+
+<h2 id="in-tree-files">In-tree files</h2>
+
+<p>The following licenses cover the in-tree source files that were used in this
+release:</p>
+
+{{ in_tree|safe }}
+
+<h2 id="out-of-tree-dependencies">Out-of-tree dependencies</h2>
+
+<p>The following licenses cover the out-of-tree crates that were used in the
+Rust Standard Library in this release:</p>
+
+{% for (key, value) in dependencies %}
+    <h3>📦 {{key.name}}-{{key.version}}</h3>
+    <p><b>URL:</b> <a href="https://crates.io/crates/{{ key.name }}/{{ key.version }}">https://crates.io/crates/{{ key.name }}/{{ key.version }}</a></p>
+    <p><b>Authors:</b> {{ value.authors|join(", ") }}</p>
+    <p><b>License:</b> {{ value.license }}</p>
+    {% let len = value.notices.len() %}
+    {% if len > 0 %}
+        <p><b>Notices:</b>
+        {% for (notice_name, notice_text) in value.notices %}
+            <details>
+                <summary><code>{{ notice_name }}</code></summary>
+                <pre>
+{{ notice_text }}
+                </pre>
+            </details>
+        {% endfor %}
+        </p>
+    {% endif %}
+{% endfor %}
+</body>
+</html>
\ No newline at end of file
diff --git a/src/tools/miri/.cargo/config.toml b/src/tools/miri/.cargo/config.toml
deleted file mode 100644
index 42e7c2c4818..00000000000
--- a/src/tools/miri/.cargo/config.toml
+++ /dev/null
@@ -1,9 +0,0 @@
-[unstable]
-profile-rustflags = true
-
-# Add back the containing directory of the packages we have to refer to using --manifest-path.
-# Per-package profiles avoid adding this to build dependencies.
-[profile.dev.package."cargo-miri"]
-rustflags = ["--remap-path-prefix", "=cargo-miri"]
-[profile.dev.package."miri-script"]
-rustflags = ["--remap-path-prefix", "=miri-script"]
diff --git a/src/tools/miri/.github/workflows/ci.yml b/src/tools/miri/.github/workflows/ci.yml
index 2e491319822..209fd622202 100644
--- a/src/tools/miri/.github/workflows/ci.yml
+++ b/src/tools/miri/.github/workflows/ci.yml
@@ -58,11 +58,20 @@ jobs:
       - name: rustdoc
         run: RUSTDOCFLAGS="-Dwarnings" ./miri doc --document-private-items
 
+  coverage:
+    name: coverage report
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v4
+      - uses: ./.github/workflows/setup
+      - name: coverage
+        run: ./miri test --coverage
+
   # Summary job for the merge queue.
   # ALL THE PREVIOUS JOBS NEED TO BE ADDED TO THE `needs` SECTION OF THIS JOB!
   # And they should be added below in `cron-fail-notify` as well.
   conclusion:
-    needs: [build, style]
+    needs: [build, style, coverage]
     # We need to ensure this job does *not* get skipped if its dependencies fail,
     # because a skipped job is considered a success by GitHub. So we have to
     # overwrite `if:`. We use `!cancelled()` to ensure the job does still not get run
@@ -86,7 +95,7 @@ jobs:
         contents: write
         # ... and create a PR.
         pull-requests: write
-    needs: [build, style]
+    needs: [build, style, coverage]
     if: ${{ github.event_name == 'schedule' && failure() }}
     steps:
       # Send a Zulip notification
diff --git a/src/tools/miri/CONTRIBUTING.md b/src/tools/miri/CONTRIBUTING.md
index d0bcf68eacb..e97f4bf86d8 100644
--- a/src/tools/miri/CONTRIBUTING.md
+++ b/src/tools/miri/CONTRIBUTING.md
@@ -13,6 +13,25 @@ for a list of Miri maintainers.
 
 [Rust Zulip]: https://rust-lang.zulipchat.com
 
+### Pull review process
+
+When you get a review, please take care of the requested changes in new commits. Do not amend
+existing commits. Generally avoid force-pushing. The only time you should force push is when there
+is a conflict with the master branch (in that case you should rebase across master, not merge), and
+all the way at the end of the review process when the reviewer tells you that the PR is done and you
+should squash the commits. For the latter case, use `git rebase --keep-base ...` to squash without
+changing the base commit your PR branches off of. Use your own judgment and the reviewer's guidance
+to decide whether the PR should be squashed into a single commit or multiple logically separate
+commits. (All this is to work around the fact that Github is quite bad at dealing with force pushes
+and does not support `git range-diff`. Maybe one day Github will be good at git and then life can
+become easier.)
+
+Most PRs bounce back and forth between the reviewer and the author several times, so it is good to
+keep track of who is expected to take the next step. We are using the `S-waiting-for-review` and
+`S-waiting-for-author` labels for that. If a reviewer asked you to do some changes and you think
+they are all taken care of, post a comment saying `@rustbot ready` to mark a PR as ready for the
+next round of review.
+
 ### Larger-scale contributions
 
 If you are thinking about making a larger-scale contribution -- in particular anything that needs
@@ -45,14 +64,6 @@ process for such contributions:
 This process is largely informal, and its primary goal is to more clearly communicate expectations.
 Please get in touch with us if you have any questions!
 
-### Managing the review state
-
-Most PRs bounce back and forth between the reviewer and the author several times, so it is good to
-keep track of who is expected to take the next step. We are using the `S-waiting-for-review` and
-`S-waiting-for-author` labels for that. If a reviewer asked you to do some changes and you think
-they are all taken care of, post a comment saying `@rustbot ready` to mark a PR as ready for the
-next round of review.
-
 ## Preparing the build environment
 
 Miri heavily relies on internal and unstable rustc interfaces to execute MIR,
diff --git a/src/tools/miri/README.md b/src/tools/miri/README.md
index a73fefaaf34..9a683ae68fd 100644
--- a/src/tools/miri/README.md
+++ b/src/tools/miri/README.md
@@ -548,6 +548,7 @@ Definite bugs found:
 * [Incorrect offset computation for highly-aligned types in `portable-atomic-util`](https://github.com/taiki-e/portable-atomic/pull/138)
 * [Occasional memory leak in `std::mpsc` channels](https://github.com/rust-lang/rust/issues/121582) (original code in [crossbeam](https://github.com/crossbeam-rs/crossbeam/pull/1084))
 * [Weak-memory-induced memory leak in Windows thread-local storage](https://github.com/rust-lang/rust/pull/124281)
+* [A bug in the new `RwLock::downgrade` implementation](https://rust-lang.zulipchat.com/#narrow/channel/269128-miri/topic/Miri.20error.20library.20test) (caught by Miri before it landed in the Rust repo)
 
 Violations of [Stacked Borrows] found that are likely bugs (but Stacked Borrows is currently just an experiment):
 
diff --git a/src/tools/miri/ci/ci.sh b/src/tools/miri/ci/ci.sh
index 4e7cbc50ca0..0356d7ecf10 100755
--- a/src/tools/miri/ci/ci.sh
+++ b/src/tools/miri/ci/ci.sh
@@ -154,7 +154,7 @@ case $HOST_TARGET in
     TEST_TARGET=i686-unknown-freebsd   run_tests_minimal $BASIC $UNIX time hashmap random threadname pthread fs libc-pipe
     TEST_TARGET=x86_64-unknown-illumos run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe
     TEST_TARGET=x86_64-pc-solaris      run_tests_minimal $BASIC $UNIX time hashmap random thread sync available-parallelism tls libc-pipe
-    TEST_TARGET=aarch64-linux-android  run_tests_minimal $BASIC $UNIX time hashmap threadname pthread
+    TEST_TARGET=aarch64-linux-android  run_tests_minimal $BASIC $UNIX time hashmap random sync threadname pthread
     TEST_TARGET=wasm32-wasip2          run_tests_minimal $BASIC wasm
     TEST_TARGET=wasm32-unknown-unknown run_tests_minimal no_std empty_main wasm # this target doesn't really have std
     TEST_TARGET=thumbv7em-none-eabihf  run_tests_minimal no_std
diff --git a/src/tools/miri/clippy.toml b/src/tools/miri/clippy.toml
index 284e18a45a3..504be47459c 100644
--- a/src/tools/miri/clippy.toml
+++ b/src/tools/miri/clippy.toml
@@ -1 +1 @@
-arithmetic-side-effects-allowed = ["rustc_target::abi::Size"]
+arithmetic-side-effects-allowed = ["rustc_abi::Size", "rustc_apfloat::ieee::IeeeFloat"]
diff --git a/src/tools/miri/miri b/src/tools/miri/miri
index 5d07ad9e249..ac1a7211c4e 100755
--- a/src/tools/miri/miri
+++ b/src/tools/miri/miri
@@ -1,13 +1,15 @@
 #!/usr/bin/env bash
 set -e
 # We want to call the binary directly, so we need to know where it ends up.
-MIRI_SCRIPT_TARGET_DIR="$(dirname "$0")"/miri-script/target
+ROOT_DIR="$(dirname "$0")"
+MIRI_SCRIPT_TARGET_DIR="$ROOT_DIR"/miri-script/target
 # If stdout is not a terminal and we are not on CI, assume that we are being invoked by RA, and use JSON output.
 if ! [ -t 1 ] && [ -z "$CI" ]; then
   MESSAGE_FORMAT="--message-format=json"
 fi
-# We need a nightly toolchain, for the `profile-rustflags` cargo feature.
-cargo +nightly build $CARGO_EXTRA_FLAGS --manifest-path "$(dirname "$0")"/miri-script/Cargo.toml \
+# We need a nightly toolchain, for `-Zroot-dir`.
+cargo +nightly build $CARGO_EXTRA_FLAGS --manifest-path "$ROOT_DIR"/miri-script/Cargo.toml \
+  -Zroot-dir="$ROOT_DIR" \
   -q --target-dir "$MIRI_SCRIPT_TARGET_DIR" $MESSAGE_FORMAT || \
   ( echo "Failed to build miri-script. Is the 'nightly' toolchain installed?"; exit 1 )
 # Instead of doing just `cargo run --manifest-path .. $@`, we invoke miri-script binary directly. Invoking `cargo run` goes through
diff --git a/src/tools/miri/miri-script/Cargo.lock b/src/tools/miri/miri-script/Cargo.lock
index 146e613c24b..0c0fe477cdd 100644
--- a/src/tools/miri/miri-script/Cargo.lock
+++ b/src/tools/miri/miri-script/Cargo.lock
@@ -1,6 +1,6 @@
 # This file is automatically @generated by Cargo.
 # It is not intended for manual editing.
-version = 3
+version = 4
 
 [[package]]
 name = "anyhow"
@@ -64,6 +64,12 @@ dependencies = [
 ]
 
 [[package]]
+name = "fastrand"
+version = "2.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8c02a5121d4ea3eb16a80748c74f5549a5665e4c21333c6098f283870fbdea6"
+
+[[package]]
 name = "getrandom"
 version = "0.2.12"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -100,9 +106,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
 
 [[package]]
 name = "libc"
-version = "0.2.153"
+version = "0.2.159"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
+checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
 
 [[package]]
 name = "libredox"
@@ -138,12 +144,19 @@ dependencies = [
  "rustc_version",
  "serde_json",
  "shell-words",
+ "tempfile",
  "walkdir",
  "which",
  "xshell",
 ]
 
 [[package]]
+name = "once_cell"
+version = "1.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
+
+[[package]]
 name = "option-ext"
 version = "0.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -195,9 +208,9 @@ dependencies = [
 
 [[package]]
 name = "rustix"
-version = "0.38.34"
+version = "0.38.37"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f"
+checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
 dependencies = [
  "bitflags",
  "errno",
@@ -277,6 +290,19 @@ dependencies = [
 ]
 
 [[package]]
+name = "tempfile"
+version = "3.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f0f2c9fc62d0beef6951ccffd757e241266a2c833136efbe35af6cd2567dca5b"
+dependencies = [
+ "cfg-if",
+ "fastrand",
+ "once_cell",
+ "rustix",
+ "windows-sys 0.59.0",
+]
+
+[[package]]
 name = "thiserror"
 version = "1.0.57"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -358,6 +384,15 @@ dependencies = [
 ]
 
 [[package]]
+name = "windows-sys"
+version = "0.59.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
+dependencies = [
+ "windows-targets 0.52.6",
+]
+
+[[package]]
 name = "windows-targets"
 version = "0.48.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -486,15 +521,15 @@ checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
 
 [[package]]
 name = "xshell"
-version = "0.2.6"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6db0ab86eae739efd1b054a8d3d16041914030ac4e01cd1dca0cf252fd8b6437"
+checksum = "9e7290c623014758632efe00737145b6867b66292c42167f2ec381eb566a373d"
 dependencies = [
  "xshell-macros",
 ]
 
 [[package]]
 name = "xshell-macros"
-version = "0.2.6"
+version = "0.2.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d422e8e38ec76e2f06ee439ccc765e9c6a9638b9e7c9f2e8255e4d41e8bd852"
+checksum = "32ac00cd3f8ec9c1d33fb3e7958a82df6989c42d747bd326c822b1d625283547"
diff --git a/src/tools/miri/miri-script/Cargo.toml b/src/tools/miri/miri-script/Cargo.toml
index 23b9a625159..5b31d5a6ff9 100644
--- a/src/tools/miri/miri-script/Cargo.toml
+++ b/src/tools/miri/miri-script/Cargo.toml
@@ -24,3 +24,4 @@ rustc_version = "0.4"
 dunce = "1.0.4"
 directories = "5"
 serde_json = "1"
+tempfile = "3.13.0"
diff --git a/src/tools/miri/miri-script/src/commands.rs b/src/tools/miri/miri-script/src/commands.rs
index 36175c8dd2b..21029d0b5b3 100644
--- a/src/tools/miri/miri-script/src/commands.rs
+++ b/src/tools/miri/miri-script/src/commands.rs
@@ -172,7 +172,8 @@ impl Command {
             Command::Install { flags } => Self::install(flags),
             Command::Build { flags } => Self::build(flags),
             Command::Check { flags } => Self::check(flags),
-            Command::Test { bless, flags, target } => Self::test(bless, flags, target),
+            Command::Test { bless, flags, target, coverage } =>
+                Self::test(bless, flags, target, coverage),
             Command::Run { dep, verbose, many_seeds, target, edition, flags } =>
                 Self::run(dep, verbose, many_seeds, target, edition, flags),
             Command::Doc { flags } => Self::doc(flags),
@@ -458,9 +459,20 @@ impl Command {
         Ok(())
     }
 
-    fn test(bless: bool, mut flags: Vec<String>, target: Option<String>) -> Result<()> {
+    fn test(
+        bless: bool,
+        mut flags: Vec<String>,
+        target: Option<String>,
+        coverage: bool,
+    ) -> Result<()> {
         let mut e = MiriEnv::new()?;
 
+        let coverage = coverage.then_some(crate::coverage::CoverageReport::new()?);
+
+        if let Some(report) = &coverage {
+            report.add_env_vars(&mut e)?;
+        }
+
         // Prepare a sysroot. (Also builds cargo-miri, which we need.)
         e.build_miri_sysroot(/* quiet */ false, target.as_deref())?;
 
@@ -479,6 +491,11 @@ impl Command {
         // Then test, and let caller control flags.
         // Only in root project as `cargo-miri` has no tests.
         e.test(".", &flags)?;
+
+        if let Some(coverage) = &coverage {
+            coverage.show_coverage_report(&e)?;
+        }
+
         Ok(())
     }
 
diff --git a/src/tools/miri/miri-script/src/coverage.rs b/src/tools/miri/miri-script/src/coverage.rs
new file mode 100644
index 00000000000..8cafcea0d16
--- /dev/null
+++ b/src/tools/miri/miri-script/src/coverage.rs
@@ -0,0 +1,91 @@
+use std::path::PathBuf;
+
+use anyhow::{Context, Result};
+use path_macro::path;
+use tempfile::TempDir;
+use xshell::cmd;
+
+use crate::util::MiriEnv;
+
+/// CoverageReport can generate code coverage reports for miri.
+pub struct CoverageReport {
+    /// path is a temporary directory where intermediate coverage artifacts will be stored.
+    /// (The final output will be stored in a permanent location.)
+    path: TempDir,
+}
+
+impl CoverageReport {
+    /// Creates a new CoverageReport.
+    ///
+    /// # Errors
+    ///
+    /// An error will be returned if a temporary directory could not be created.
+    pub fn new() -> Result<Self> {
+        Ok(Self { path: TempDir::new()? })
+    }
+
+    /// add_env_vars will add the required environment variables to MiriEnv `e`.
+    pub fn add_env_vars(&self, e: &mut MiriEnv) -> Result<()> {
+        let mut rustflags = e.sh.var("RUSTFLAGS")?;
+        rustflags.push_str(" -C instrument-coverage");
+        e.sh.set_var("RUSTFLAGS", rustflags);
+
+        // Copy-pasting from: https://doc.rust-lang.org/rustc/instrument-coverage.html#instrumentation-based-code-coverage
+        // The format symbols below have the following meaning:
+        // - %p - The process ID.
+        // - %Nm - the instrumented binary’s signature:
+        //   The runtime creates a pool of N raw profiles, used for on-line
+        //   profile merging. The runtime takes care of selecting a raw profile
+        //   from the pool, locking it, and updating it before the program
+        //   exits. N must be between 1 and 9, and defaults to 1 if omitted
+        //   (with simply %m).
+        //
+        // Additionally the default for LLVM_PROFILE_FILE is default_%m_%p.profraw.
+        // So we just use the same template, replacing "default" with "miri".
+        let file_template = self.path.path().join("miri_%m_%p.profraw");
+        e.sh.set_var("LLVM_PROFILE_FILE", file_template);
+        Ok(())
+    }
+
+    /// show_coverage_report will print coverage information using the artifact
+    /// files in `self.path`.
+    pub fn show_coverage_report(&self, e: &MiriEnv) -> Result<()> {
+        let profraw_files = self.profraw_files()?;
+
+        let profdata_bin = path!(e.libdir / ".." / "bin" / "llvm-profdata");
+
+        let merged_file = path!(e.miri_dir / "target" / "coverage.profdata");
+
+        // Merge the profraw files
+        cmd!(e.sh, "{profdata_bin} merge -sparse {profraw_files...} -o {merged_file}")
+            .quiet()
+            .run()?;
+
+        // Create the coverage report.
+        let cov_bin = path!(e.libdir / ".." / "bin" / "llvm-cov");
+        let miri_bin =
+            e.build_get_binary(".").context("failed to get filename of miri executable")?;
+        cmd!(
+            e.sh,
+            "{cov_bin} report --instr-profile={merged_file} --object {miri_bin} --sources src/"
+        )
+        .run()?;
+
+        println!("Profile data saved in {}", merged_file.display());
+        Ok(())
+    }
+
+    /// profraw_files returns the profraw files in `self.path`.
+    ///
+    /// # Errors
+    ///
+    /// An error will be returned if `self.path` can't be read.
+    fn profraw_files(&self) -> Result<Vec<PathBuf>> {
+        Ok(std::fs::read_dir(&self.path)?
+            .filter_map(|r| r.ok())
+            .filter(|e| e.file_type().is_ok_and(|t| t.is_file()))
+            .map(|e| e.path())
+            .filter(|p| p.extension().is_some_and(|e| e == "profraw"))
+            .collect())
+    }
+}
diff --git a/src/tools/miri/miri-script/src/main.rs b/src/tools/miri/miri-script/src/main.rs
index 0620f3aaf09..a329f627903 100644
--- a/src/tools/miri/miri-script/src/main.rs
+++ b/src/tools/miri/miri-script/src/main.rs
@@ -2,6 +2,7 @@
 
 mod args;
 mod commands;
+mod coverage;
 mod util;
 
 use std::ops::Range;
@@ -34,6 +35,8 @@ pub enum Command {
         /// The cross-interpretation target.
         /// If none then the host is the target.
         target: Option<String>,
+        /// Produce coverage report if set.
+        coverage: bool,
         /// Flags that are passed through to the test harness.
         flags: Vec<String>,
     },
@@ -158,9 +161,12 @@ fn main() -> Result<()> {
             let mut target = None;
             let mut bless = false;
             let mut flags = Vec::new();
+            let mut coverage = false;
             loop {
                 if args.get_long_flag("bless")? {
                     bless = true;
+                } else if args.get_long_flag("coverage")? {
+                    coverage = true;
                 } else if let Some(val) = args.get_long_opt("target")? {
                     target = Some(val);
                 } else if let Some(flag) = args.get_other() {
@@ -169,7 +175,7 @@ fn main() -> Result<()> {
                     break;
                 }
             }
-            Command::Test { bless, flags, target }
+            Command::Test { bless, flags, target, coverage }
         }
         Some("run") => {
             let mut dep = false;
diff --git a/src/tools/miri/miri-script/src/util.rs b/src/tools/miri/miri-script/src/util.rs
index f5a6a8188a0..cf6529d8373 100644
--- a/src/tools/miri/miri-script/src/util.rs
+++ b/src/tools/miri/miri-script/src/util.rs
@@ -41,6 +41,8 @@ pub struct MiriEnv {
     pub sysroot: PathBuf,
     /// The shell we use.
     pub sh: Shell,
+    /// The library dir in the sysroot.
+    pub libdir: PathBuf,
 }
 
 impl MiriEnv {
@@ -96,13 +98,14 @@ impl MiriEnv {
         // so that Windows can find the DLLs.
         if cfg!(windows) {
             let old_path = sh.var("PATH")?;
-            let new_path = env::join_paths(iter::once(libdir).chain(env::split_paths(&old_path)))?;
+            let new_path =
+                env::join_paths(iter::once(libdir.clone()).chain(env::split_paths(&old_path)))?;
             sh.set_var("PATH", new_path);
         }
 
         // Get extra flags for cargo.
         let cargo_extra_flags = std::env::var("CARGO_EXTRA_FLAGS").unwrap_or_default();
-        let cargo_extra_flags = flagsplit(&cargo_extra_flags);
+        let mut cargo_extra_flags = flagsplit(&cargo_extra_flags);
         if cargo_extra_flags.iter().any(|a| a == "--release" || a.starts_with("--profile")) {
             // This makes binaries end up in different paths, let's not do that.
             eprintln!(
@@ -110,8 +113,10 @@ impl MiriEnv {
             );
             std::process::exit(1);
         }
+        // Also set `-Zroot-dir` for cargo, to print diagnostics relative to the miri dir.
+        cargo_extra_flags.push(format!("-Zroot-dir={}", miri_dir.display()));
 
-        Ok(MiriEnv { miri_dir, toolchain, sh, sysroot, cargo_extra_flags })
+        Ok(MiriEnv { miri_dir, toolchain, sh, sysroot, cargo_extra_flags, libdir })
     }
 
     pub fn cargo_cmd(&self, crate_dir: impl AsRef<OsStr>, cmd: &str) -> Cmd<'_> {
diff --git a/src/tools/miri/rust-version b/src/tools/miri/rust-version
index 133edd3191d..effed0cd180 100644
--- a/src/tools/miri/rust-version
+++ b/src/tools/miri/rust-version
@@ -1 +1 @@
-814df6e50eaf89b90793e7d9618bb60f1f18377a
+2d0ea7956c45de6e421fd579e2ded27be405dec6
diff --git a/src/tools/miri/src/alloc_addresses/mod.rs b/src/tools/miri/src/alloc_addresses/mod.rs
index 50e55268248..fe7d8db245b 100644
--- a/src/tools/miri/src/alloc_addresses/mod.rs
+++ b/src/tools/miri/src/alloc_addresses/mod.rs
@@ -7,9 +7,9 @@ use std::cell::RefCell;
 use std::cmp::max;
 
 use rand::Rng;
+use rustc_abi::{Align, Size};
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_span::Span;
-use rustc_target::abi::{Align, Size};
 
 use self::reuse_pool::ReusePool;
 use crate::concurrency::VClock;
@@ -111,8 +111,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
     // Returns the exposed `AllocId` that corresponds to the specified addr,
     // or `None` if the addr is out of bounds
     fn alloc_id_from_addr(&self, addr: u64, size: i64) -> Option<AllocId> {
-        let ecx = self.eval_context_ref();
-        let global_state = ecx.machine.alloc_addresses.borrow();
+        let this = self.eval_context_ref();
+        let global_state = this.machine.alloc_addresses.borrow();
         assert!(global_state.provenance_mode != ProvenanceMode::Strict);
 
         // We always search the allocation to the right of this address. So if the size is structly
@@ -134,7 +134,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // entered for addresses that are not the base address, so even zero-sized
                 // allocations will get recognized at their base address -- but all other
                 // allocations will *not* be recognized at their "end" address.
-                let size = ecx.get_alloc_info(alloc_id).0;
+                let size = this.get_alloc_info(alloc_id).size;
                 if offset < size.bytes() { Some(alloc_id) } else { None }
             }
         }?;
@@ -142,7 +142,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // We only use this provenance if it has been exposed.
         if global_state.exposed.contains(&alloc_id) {
             // This must still be live, since we remove allocations from `int_to_ptr_map` when they get freed.
-            debug_assert!(ecx.is_alloc_live(alloc_id));
+            debug_assert!(this.is_alloc_live(alloc_id));
             Some(alloc_id)
         } else {
             None
@@ -155,27 +155,27 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
         alloc_id: AllocId,
         memory_kind: MemoryKind,
     ) -> InterpResult<'tcx, u64> {
-        let ecx = self.eval_context_ref();
-        let mut rng = ecx.machine.rng.borrow_mut();
-        let (size, align, kind) = ecx.get_alloc_info(alloc_id);
+        let this = self.eval_context_ref();
+        let mut rng = this.machine.rng.borrow_mut();
+        let info = this.get_alloc_info(alloc_id);
         // This is either called immediately after allocation (and then cached), or when
         // adjusting `tcx` pointers (which never get freed). So assert that we are looking
         // at a live allocation. This also ensures that we never re-assign an address to an
         // allocation that previously had an address, but then was freed and the address
         // information was removed.
-        assert!(!matches!(kind, AllocKind::Dead));
+        assert!(!matches!(info.kind, AllocKind::Dead));
 
         // This allocation does not have a base address yet, pick or reuse one.
-        if ecx.machine.native_lib.is_some() {
+        if this.machine.native_lib.is_some() {
             // In native lib mode, we use the "real" address of the bytes for this allocation.
             // This ensures the interpreted program and native code have the same view of memory.
-            let base_ptr = match kind {
+            let base_ptr = match info.kind {
                 AllocKind::LiveData => {
-                    if ecx.tcx.try_get_global_alloc(alloc_id).is_some() {
+                    if this.tcx.try_get_global_alloc(alloc_id).is_some() {
                         // For new global allocations, we always pre-allocate the memory to be able use the machine address directly.
-                        let prepared_bytes = MiriAllocBytes::zeroed(size, align)
+                        let prepared_bytes = MiriAllocBytes::zeroed(info.size, info.align)
                             .unwrap_or_else(|| {
-                                panic!("Miri ran out of memory: cannot create allocation of {size:?} bytes")
+                                panic!("Miri ran out of memory: cannot create allocation of {size:?} bytes", size = info.size)
                             });
                         let ptr = prepared_bytes.as_ptr();
                         // Store prepared allocation space to be picked up for use later.
@@ -185,7 +185,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                             .unwrap();
                         ptr
                     } else {
-                        ecx.get_alloc_bytes_unchecked_raw(alloc_id)?
+                        this.get_alloc_bytes_unchecked_raw(alloc_id)?
                     }
                 }
                 AllocKind::Function | AllocKind::VTable => {
@@ -203,11 +203,15 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             return interp_ok(base_ptr.expose_provenance().try_into().unwrap());
         }
         // We are not in native lib mode, so we control the addresses ourselves.
-        if let Some((reuse_addr, clock)) =
-            global_state.reuse.take_addr(&mut *rng, size, align, memory_kind, ecx.active_thread())
-        {
+        if let Some((reuse_addr, clock)) = global_state.reuse.take_addr(
+            &mut *rng,
+            info.size,
+            info.align,
+            memory_kind,
+            this.active_thread(),
+        ) {
             if let Some(clock) = clock {
-                ecx.acquire_clock(&clock);
+                this.acquire_clock(&clock);
             }
             interp_ok(reuse_addr)
         } else {
@@ -220,17 +224,17 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 .next_base_addr
                 .checked_add(slack)
                 .ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
-            let base_addr = align_addr(base_addr, align.bytes());
+            let base_addr = align_addr(base_addr, info.align.bytes());
 
             // Remember next base address.  If this allocation is zero-sized, leave a gap of at
             // least 1 to avoid two allocations having the same base address. (The logic in
             // `alloc_id_from_addr` assumes unique addresses, and different function/vtable pointers
             // need to be distinguishable!)
             global_state.next_base_addr = base_addr
-                .checked_add(max(size.bytes(), 1))
+                .checked_add(max(info.size.bytes(), 1))
                 .ok_or_else(|| err_exhaust!(AddressSpaceFull))?;
             // Even if `Size` didn't overflow, we might still have filled up the address space.
-            if global_state.next_base_addr > ecx.target_usize_max() {
+            if global_state.next_base_addr > this.target_usize_max() {
                 throw_exhaust!(AddressSpaceFull);
             }
 
@@ -243,8 +247,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
         alloc_id: AllocId,
         memory_kind: MemoryKind,
     ) -> InterpResult<'tcx, u64> {
-        let ecx = self.eval_context_ref();
-        let mut global_state = ecx.machine.alloc_addresses.borrow_mut();
+        let this = self.eval_context_ref();
+        let mut global_state = this.machine.alloc_addresses.borrow_mut();
         let global_state = &mut *global_state;
 
         match global_state.base_addr.get(&alloc_id) {
@@ -283,22 +287,22 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
 impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
 pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn expose_ptr(&mut self, alloc_id: AllocId, tag: BorTag) -> InterpResult<'tcx> {
-        let ecx = self.eval_context_mut();
-        let global_state = ecx.machine.alloc_addresses.get_mut();
+        let this = self.eval_context_mut();
+        let global_state = this.machine.alloc_addresses.get_mut();
         // In strict mode, we don't need this, so we can save some cycles by not tracking it.
         if global_state.provenance_mode == ProvenanceMode::Strict {
             return interp_ok(());
         }
         // Exposing a dead alloc is a no-op, because it's not possible to get a dead allocation
         // via int2ptr.
-        if !ecx.is_alloc_live(alloc_id) {
+        if !this.is_alloc_live(alloc_id) {
             return interp_ok(());
         }
         trace!("Exposing allocation id {alloc_id:?}");
-        let global_state = ecx.machine.alloc_addresses.get_mut();
+        let global_state = this.machine.alloc_addresses.get_mut();
         global_state.exposed.insert(alloc_id);
-        if ecx.machine.borrow_tracker.is_some() {
-            ecx.expose_tag(alloc_id, tag)?;
+        if this.machine.borrow_tracker.is_some() {
+            this.expose_tag(alloc_id, tag)?;
         }
         interp_ok(())
     }
@@ -306,8 +310,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn ptr_from_addr_cast(&self, addr: u64) -> InterpResult<'tcx, Pointer> {
         trace!("Casting {:#x} to a pointer", addr);
 
-        let ecx = self.eval_context_ref();
-        let global_state = ecx.machine.alloc_addresses.borrow();
+        let this = self.eval_context_ref();
+        let global_state = this.machine.alloc_addresses.borrow();
 
         // Potentially emit a warning.
         match global_state.provenance_mode {
@@ -319,9 +323,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 }
                 PAST_WARNINGS.with_borrow_mut(|past_warnings| {
                     let first = past_warnings.is_empty();
-                    if past_warnings.insert(ecx.cur_span()) {
+                    if past_warnings.insert(this.cur_span()) {
                         // Newly inserted, so first time we see this span.
-                        ecx.emit_diagnostic(NonHaltingDiagnostic::Int2Ptr { details: first });
+                        this.emit_diagnostic(NonHaltingDiagnostic::Int2Ptr { details: first });
                     }
                 });
             }
@@ -347,19 +351,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         tag: BorTag,
         kind: MemoryKind,
     ) -> InterpResult<'tcx, interpret::Pointer<Provenance>> {
-        let ecx = self.eval_context_ref();
+        let this = self.eval_context_ref();
 
         let (prov, offset) = ptr.into_parts(); // offset is relative (AllocId provenance)
         let alloc_id = prov.alloc_id();
 
         // Get a pointer to the beginning of this allocation.
-        let base_addr = ecx.addr_from_alloc_id(alloc_id, kind)?;
+        let base_addr = this.addr_from_alloc_id(alloc_id, kind)?;
         let base_ptr = interpret::Pointer::new(
             Provenance::Concrete { alloc_id, tag },
             Size::from_bytes(base_addr),
         );
         // Add offset with the right kind of pointer-overflowing arithmetic.
-        interp_ok(base_ptr.wrapping_offset(offset, ecx))
+        interp_ok(base_ptr.wrapping_offset(offset, this))
     }
 
     // This returns some prepared `MiriAllocBytes`, either because `addr_from_alloc_id` reserved
@@ -371,16 +375,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         bytes: &[u8],
         align: Align,
     ) -> InterpResult<'tcx, MiriAllocBytes> {
-        let ecx = self.eval_context_ref();
-        if ecx.machine.native_lib.is_some() {
+        let this = self.eval_context_ref();
+        if this.machine.native_lib.is_some() {
             // In native lib mode, MiriAllocBytes for global allocations are handled via `prepared_alloc_bytes`.
             // This additional call ensures that some `MiriAllocBytes` are always prepared, just in case
             // this function gets called before the first time `addr_from_alloc_id` gets called.
-            ecx.addr_from_alloc_id(id, kind)?;
+            this.addr_from_alloc_id(id, kind)?;
             // The memory we need here will have already been allocated during an earlier call to
             // `addr_from_alloc_id` for this allocation. So don't create a new `MiriAllocBytes` here, instead
             // fetch the previously prepared bytes from `prepared_alloc_bytes`.
-            let mut global_state = ecx.machine.alloc_addresses.borrow_mut();
+            let mut global_state = this.machine.alloc_addresses.borrow_mut();
             let mut prepared_alloc_bytes = global_state
                 .prepared_alloc_bytes
                 .remove(&id)
@@ -403,7 +407,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         ptr: interpret::Pointer<Provenance>,
         size: i64,
     ) -> Option<(AllocId, Size)> {
-        let ecx = self.eval_context_ref();
+        let this = self.eval_context_ref();
 
         let (tag, addr) = ptr.into_parts(); // addr is absolute (Tag provenance)
 
@@ -411,15 +415,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             alloc_id
         } else {
             // A wildcard pointer.
-            ecx.alloc_id_from_addr(addr.bytes(), size)?
+            this.alloc_id_from_addr(addr.bytes(), size)?
         };
 
         // This cannot fail: since we already have a pointer with that provenance, adjust_alloc_root_pointer
         // must have been called in the past, so we can just look up the address in the map.
-        let base_addr = *ecx.machine.alloc_addresses.borrow().base_addr.get(&alloc_id).unwrap();
+        let base_addr = *this.machine.alloc_addresses.borrow().base_addr.get(&alloc_id).unwrap();
 
         // Wrapping "addr - base_addr"
-        let rel_offset = ecx.truncate_to_target_usize(addr.bytes().wrapping_sub(base_addr));
+        let rel_offset = this.truncate_to_target_usize(addr.bytes().wrapping_sub(base_addr));
         Some((alloc_id, Size::from_bytes(rel_offset)))
     }
 }
diff --git a/src/tools/miri/src/alloc_addresses/reuse_pool.rs b/src/tools/miri/src/alloc_addresses/reuse_pool.rs
index b5366d5ce92..b0c7ee7dff5 100644
--- a/src/tools/miri/src/alloc_addresses/reuse_pool.rs
+++ b/src/tools/miri/src/alloc_addresses/reuse_pool.rs
@@ -1,7 +1,7 @@
 //! Manages a pool of addresses that can be reused.
 
 use rand::Rng;
-use rustc_target::abi::{Align, Size};
+use rustc_abi::{Align, Size};
 
 use crate::concurrency::VClock;
 use crate::{MemoryKind, MiriConfig, ThreadId};
diff --git a/src/tools/miri/src/alloc_bytes.rs b/src/tools/miri/src/alloc_bytes.rs
index 6ada8c66fde..69ede279aa9 100644
--- a/src/tools/miri/src/alloc_bytes.rs
+++ b/src/tools/miri/src/alloc_bytes.rs
@@ -2,8 +2,8 @@ use std::alloc::Layout;
 use std::borrow::Cow;
 use std::{alloc, slice};
 
+use rustc_abi::{Align, Size};
 use rustc_middle::mir::interpret::AllocBytes;
-use rustc_target::abi::{Align, Size};
 
 /// Allocation bytes that explicitly handle the layout of the data they're storing.
 /// This is necessary to interface with native code that accesses the program store in Miri.
diff --git a/src/tools/miri/src/bin/miri.rs b/src/tools/miri/src/bin/miri.rs
index 717229ba8b3..c61c62c73da 100644
--- a/src/tools/miri/src/bin/miri.rs
+++ b/src/tools/miri/src/bin/miri.rs
@@ -11,6 +11,7 @@
 extern crate tracing;
 
 // The rustc crates we need
+extern crate rustc_abi;
 extern crate rustc_data_structures;
 extern crate rustc_driver;
 extern crate rustc_hir;
@@ -21,7 +22,6 @@ extern crate rustc_metadata;
 extern crate rustc_middle;
 extern crate rustc_session;
 extern crate rustc_span;
-extern crate rustc_target;
 
 use std::env::{self, VarError};
 use std::num::NonZero;
@@ -29,6 +29,7 @@ use std::path::PathBuf;
 use std::str::FromStr;
 
 use miri::{BacktraceStyle, BorrowTrackerMethod, ProvenanceMode, RetagFields, ValidationMode};
+use rustc_abi::ExternAbi;
 use rustc_data_structures::sync::Lrc;
 use rustc_driver::Compilation;
 use rustc_hir::def_id::LOCAL_CRATE;
@@ -47,7 +48,6 @@ use rustc_session::config::{CrateType, EntryFnType, ErrorOutputType, OptLevel};
 use rustc_session::search_paths::PathKind;
 use rustc_session::{CtfeBacktrace, EarlyDiagCtxt};
 use rustc_span::def_id::DefId;
-use rustc_target::spec::abi::Abi;
 use tracing::debug;
 
 struct MiriCompilerCalls {
@@ -73,51 +73,47 @@ impl rustc_driver::Callbacks for MiriCompilerCalls {
     fn after_analysis<'tcx>(
         &mut self,
         _: &rustc_interface::interface::Compiler,
-        queries: &'tcx rustc_interface::Queries<'tcx>,
+        tcx: TyCtxt<'tcx>,
     ) -> Compilation {
-        queries.global_ctxt().unwrap().enter(|tcx| {
-            if tcx.sess.dcx().has_errors_or_delayed_bugs().is_some() {
-                tcx.dcx().fatal("miri cannot be run on programs that fail compilation");
-            }
+        if tcx.sess.dcx().has_errors_or_delayed_bugs().is_some() {
+            tcx.dcx().fatal("miri cannot be run on programs that fail compilation");
+        }
 
-            let early_dcx = EarlyDiagCtxt::new(tcx.sess.opts.error_format);
-            init_late_loggers(&early_dcx, tcx);
-            if !tcx.crate_types().contains(&CrateType::Executable) {
-                tcx.dcx().fatal("miri only makes sense on bin crates");
-            }
+        let early_dcx = EarlyDiagCtxt::new(tcx.sess.opts.error_format);
+        init_late_loggers(&early_dcx, tcx);
+        if !tcx.crate_types().contains(&CrateType::Executable) {
+            tcx.dcx().fatal("miri only makes sense on bin crates");
+        }
 
-            let (entry_def_id, entry_type) = entry_fn(tcx);
-            let mut config = self.miri_config.clone();
+        let (entry_def_id, entry_type) = entry_fn(tcx);
+        let mut config = self.miri_config.clone();
 
-            // Add filename to `miri` arguments.
-            config.args.insert(0, tcx.sess.io.input.filestem().to_string());
+        // Add filename to `miri` arguments.
+        config.args.insert(0, tcx.sess.io.input.filestem().to_string());
 
-            // Adjust working directory for interpretation.
-            if let Some(cwd) = env::var_os("MIRI_CWD") {
-                env::set_current_dir(cwd).unwrap();
-            }
+        // Adjust working directory for interpretation.
+        if let Some(cwd) = env::var_os("MIRI_CWD") {
+            env::set_current_dir(cwd).unwrap();
+        }
 
-            if tcx.sess.opts.optimize != OptLevel::No {
-                tcx.dcx().warn("Miri does not support optimizations: the opt-level is ignored. The only effect \
+        if tcx.sess.opts.optimize != OptLevel::No {
+            tcx.dcx().warn("Miri does not support optimizations: the opt-level is ignored. The only effect \
                     of selecting a Cargo profile that enables optimizations (such as --release) is to apply \
                     its remaining settings, such as whether debug assertions and overflow checks are enabled.");
-            }
-            if tcx.sess.mir_opt_level() > 0 {
-                tcx.dcx().warn("You have explicitly enabled MIR optimizations, overriding Miri's default \
+        }
+        if tcx.sess.mir_opt_level() > 0 {
+            tcx.dcx().warn("You have explicitly enabled MIR optimizations, overriding Miri's default \
                     which is to completely disable them. Any optimizations may hide UB that Miri would \
                     otherwise detect, and it is not necessarily possible to predict what kind of UB will \
                     be missed. If you are enabling optimizations to make Miri run faster, we advise using \
                     cfg(miri) to shrink your workload instead. The performance benefit of enabling MIR \
                     optimizations is usually marginal at best.");
-            }
+        }
 
-            if let Some(return_code) = miri::eval_entry(tcx, entry_def_id, entry_type, config) {
-                std::process::exit(
-                    i32::try_from(return_code).expect("Return value was too large!"),
-                );
-            }
-            tcx.dcx().abort_if_errors();
-        });
+        if let Some(return_code) = miri::eval_entry(tcx, entry_def_id, entry_type, config) {
+            std::process::exit(i32::try_from(return_code).expect("Return value was too large!"));
+        }
+        tcx.dcx().abort_if_errors();
 
         Compilation::Stop
     }
@@ -193,20 +189,18 @@ impl rustc_driver::Callbacks for MiriBeRustCompilerCalls {
     fn after_analysis<'tcx>(
         &mut self,
         _: &rustc_interface::interface::Compiler,
-        queries: &'tcx rustc_interface::Queries<'tcx>,
+        tcx: TyCtxt<'tcx>,
     ) -> Compilation {
-        queries.global_ctxt().unwrap().enter(|tcx| {
-            if self.target_crate {
-                // cargo-miri has patched the compiler flags to make these into check-only builds,
-                // but we are still emulating regular rustc builds, which would perform post-mono
-                // const-eval during collection. So let's also do that here, even if we might be
-                // running with `--emit=metadata`. In particular this is needed to make
-                // `compile_fail` doc tests trigger post-mono errors.
-                // In general `collect_and_partition_mono_items` is not safe to call in check-only
-                // builds, but we are setting `-Zalways-encode-mir` which avoids those issues.
-                let _ = tcx.collect_and_partition_mono_items(());
-            }
-        });
+        if self.target_crate {
+            // cargo-miri has patched the compiler flags to make these into check-only builds,
+            // but we are still emulating regular rustc builds, which would perform post-mono
+            // const-eval during collection. So let's also do that here, even if we might be
+            // running with `--emit=metadata`. In particular this is needed to make
+            // `compile_fail` doc tests trigger post-mono errors.
+            // In general `collect_and_partition_mono_items` is not safe to call in check-only
+            // builds, but we are setting `-Zalways-encode-mir` which avoids those issues.
+            let _ = tcx.collect_and_partition_mono_items(());
+        }
         Compilation::Continue
     }
 }
@@ -368,7 +362,7 @@ fn entry_fn(tcx: TyCtxt<'_>) -> (DefId, EntryFnType) {
             tcx.types.isize,
             false,
             hir::Safety::Safe,
-            Abi::Rust,
+            ExternAbi::Rust,
         ));
 
         let correct_func_sig = check_function_signature(
diff --git a/src/tools/miri/src/borrow_tracker/mod.rs b/src/tools/miri/src/borrow_tracker/mod.rs
index 5204558f98c..4883613dea5 100644
--- a/src/tools/miri/src/borrow_tracker/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/mod.rs
@@ -2,9 +2,9 @@ use std::cell::RefCell;
 use std::fmt;
 use std::num::NonZero;
 
+use rustc_abi::Size;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_middle::mir::RetagKind;
-use rustc_target::abi::Size;
 use smallvec::SmallVec;
 
 use crate::*;
@@ -363,7 +363,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // If it does exist, then we have the guarantee that the
             // pointer is readable, and the implicit read access inserted
             // will never cause UB on the pointer itself.
-            let (_, _, kind) = this.get_alloc_info(*alloc_id);
+            let kind = this.get_alloc_info(*alloc_id).kind;
             if matches!(kind, AllocKind::LiveData) {
                 let alloc_extra = this.get_alloc_extra(*alloc_id)?; // can still fail for `extern static`
                 let alloc_borrow_tracker = &alloc_extra.borrow_tracker.as_ref().unwrap();
diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
index 5624c4c479e..63b18028a5b 100644
--- a/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
+++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/diagnostics.rs
@@ -1,8 +1,8 @@
 use std::fmt;
 
+use rustc_abi::Size;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_span::{Span, SpanData};
-use rustc_target::abi::Size;
 use smallvec::SmallVec;
 
 use crate::borrow_tracker::{GlobalStateInner, ProtectorKind};
diff --git a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
index 47fe41d9ecd..a855603eeb3 100644
--- a/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/mod.rs
@@ -12,7 +12,7 @@ use std::{cmp, mem};
 use rustc_abi::{BackendRepr, Size};
 use rustc_data_structures::fx::FxHashSet;
 use rustc_middle::mir::{Mutability, RetagKind};
-use rustc_middle::ty::layout::HasParamEnv;
+use rustc_middle::ty::layout::HasTypingEnv;
 use rustc_middle::ty::{self, Ty};
 
 use self::diagnostics::{RetagCause, RetagInfo};
@@ -71,7 +71,7 @@ impl NewPermission {
                         access: None,
                         protector: None,
                     }
-                } else if pointee.is_unpin(*cx.tcx, cx.param_env()) {
+                } else if pointee.is_unpin(*cx.tcx, cx.typing_env()) {
                     // A regular full mutable reference. On `FnEntry` this is `noalias` and `dereferenceable`.
                     NewPermission::Uniform {
                         perm: Permission::Unique,
@@ -129,7 +129,7 @@ impl NewPermission {
     fn from_box_ty<'tcx>(ty: Ty<'tcx>, kind: RetagKind, cx: &crate::MiriInterpCx<'tcx>) -> Self {
         // `ty` is not the `Box` but the field of the Box with this pointer (due to allocator handling).
         let pointee = ty.builtin_deref(true).unwrap();
-        if pointee.is_unpin(*cx.tcx, cx.param_env()) {
+        if pointee.is_unpin(*cx.tcx, cx.typing_env()) {
             // A regular box. On `FnEntry` this is `noalias`, but not `dereferenceable` (hence only
             // a weak protector).
             NewPermission::Uniform {
@@ -608,7 +608,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
                 match new_perm {
                     NewPermission::Uniform { perm, .. } =>
                         write!(kind_str, "{perm:?} permission").unwrap(),
-                    NewPermission::FreezeSensitive { freeze_perm, .. } if ty.is_freeze(*this.tcx, this.param_env()) =>
+                    NewPermission::FreezeSensitive { freeze_perm, .. } if ty.is_freeze(*this.tcx, this.typing_env()) =>
                         write!(kind_str, "{freeze_perm:?} permission").unwrap(),
                     NewPermission::FreezeSensitive { freeze_perm, nonfreeze_perm, .. }  =>
                         write!(kind_str, "{freeze_perm:?}/{nonfreeze_perm:?} permission for frozen/non-frozen parts").unwrap(),
@@ -626,7 +626,7 @@ trait EvalContextPrivExt<'tcx, 'ecx>: crate::MiriInterpCxExt<'tcx> {
                 return interp_ok(())
             };
 
-            let (_size, _align, alloc_kind) = this.get_alloc_info(alloc_id);
+            let alloc_kind = this.get_alloc_info(alloc_id).kind;
             match alloc_kind {
                 AllocKind::LiveData => {
                     // This should have alloc_extra data, but `get_alloc_extra` can still fail
@@ -1017,7 +1017,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Function pointers and dead objects don't have an alloc_extra so we ignore them.
         // This is okay because accessing them is UB anyway, no need for any Stacked Borrows checks.
         // NOT using `get_alloc_extra_mut` since this might be a read-only allocation!
-        let (_size, _align, kind) = this.get_alloc_info(alloc_id);
+        let kind = this.get_alloc_info(alloc_id).kind;
         match kind {
             AllocKind::LiveData => {
                 // This should have alloc_extra data, but `get_alloc_extra` can still fail
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 f024796c0a7..dc3370f1251 100644
--- a/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs
+++ b/src/tools/miri/src/borrow_tracker/stacked_borrows/stack.rs
@@ -354,7 +354,7 @@ impl<'tcx> Stack {
         self.borrows.get(idx).cloned()
     }
 
-    #[allow(clippy::len_without_is_empty)] // Stacks are never empty
+    #[expect(clippy::len_without_is_empty)] // Stacks are never empty
     pub fn len(&self) -> usize {
         self.borrows.len()
     }
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
index 40467aa4bc1..8469744bbc4 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/mod.rs
@@ -1,6 +1,6 @@
 use rustc_abi::{BackendRepr, Size};
 use rustc_middle::mir::{Mutability, RetagKind};
-use rustc_middle::ty::layout::HasParamEnv;
+use rustc_middle::ty::layout::HasTypingEnv;
 use rustc_middle::ty::{self, Ty};
 use rustc_span::def_id::DefId;
 
@@ -132,8 +132,8 @@ impl<'tcx> NewPermission {
         kind: RetagKind,
         cx: &crate::MiriInterpCx<'tcx>,
     ) -> Option<Self> {
-        let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.param_env());
-        let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.param_env());
+        let ty_is_freeze = pointee.is_freeze(*cx.tcx, cx.typing_env());
+        let ty_is_unpin = pointee.is_unpin(*cx.tcx, cx.typing_env());
         let is_protected = kind == RetagKind::FnEntry;
         // As demonstrated by `tests/fail/tree_borrows/reservedim_spurious_write.rs`,
         // interior mutability and protectors interact poorly.
@@ -164,10 +164,10 @@ impl<'tcx> NewPermission {
         zero_size: bool,
     ) -> Option<Self> {
         let pointee = ty.builtin_deref(true).unwrap();
-        pointee.is_unpin(*cx.tcx, cx.param_env()).then_some(()).map(|()| {
+        pointee.is_unpin(*cx.tcx, cx.typing_env()).then_some(()).map(|()| {
             // Regular `Unpin` box, give it `noalias` but only a weak protector
             // because it is valid to deallocate it within the function.
-            let ty_is_freeze = ty.is_freeze(*cx.tcx, cx.param_env());
+            let ty_is_freeze = ty.is_freeze(*cx.tcx, cx.typing_env());
             let protected = kind == RetagKind::FnEntry;
             let initial_state = Permission::new_reserved(ty_is_freeze, protected);
             Self {
@@ -274,7 +274,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 .insert(new_tag, protect);
         }
 
-        let alloc_kind = this.get_alloc_info(alloc_id).2;
+        let alloc_kind = this.get_alloc_info(alloc_id).kind;
         if !matches!(alloc_kind, AllocKind::LiveData) {
             assert_eq!(ptr_size, Size::ZERO); // we did the deref check above, size has to be 0 here
             // There's not actually any bytes here where accesses could even be tracked.
@@ -521,7 +521,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Note: if we were to inline `new_reserved` below we would find out that
         // `ty_is_freeze` is eventually unused because it appears in a `ty_is_freeze || true`.
         // We are nevertheless including it here for clarity.
-        let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.param_env());
+        let ty_is_freeze = place.layout.ty.is_freeze(*this.tcx, this.typing_env());
         // Retag it. With protection! That is the entire point.
         let new_perm = NewPermission {
             initial_state: Permission::new_reserved(ty_is_freeze, /* protected */ true),
@@ -538,7 +538,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Function pointers and dead objects don't have an alloc_extra so we ignore them.
         // This is okay because accessing them is UB anyway, no need for any Tree Borrows checks.
         // NOT using `get_alloc_extra_mut` since this might be a read-only allocation!
-        let (_size, _align, kind) = this.get_alloc_info(alloc_id);
+        let kind = this.get_alloc_info(alloc_id).kind;
         match kind {
             AllocKind::LiveData => {
                 // This should have alloc_extra data, but `get_alloc_extra` can still fail
diff --git a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
index a551b017dfc..61b3338e6a2 100644
--- a/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
+++ b/src/tools/miri/src/borrow_tracker/tree_borrows/tree.rs
@@ -12,9 +12,9 @@
 
 use std::{fmt, mem};
 
+use rustc_abi::Size;
 use rustc_data_structures::fx::FxHashSet;
 use rustc_span::Span;
-use rustc_target::abi::Size;
 use smallvec::SmallVec;
 
 use crate::borrow_tracker::tree_borrows::Permission;
diff --git a/src/tools/miri/src/concurrency/cpu_affinity.rs b/src/tools/miri/src/concurrency/cpu_affinity.rs
index 8df26d718bf..4e6bca93c5a 100644
--- a/src/tools/miri/src/concurrency/cpu_affinity.rs
+++ b/src/tools/miri/src/concurrency/cpu_affinity.rs
@@ -1,5 +1,5 @@
+use rustc_abi::Endian;
 use rustc_middle::ty::layout::LayoutOf;
-use rustc_target::abi::Endian;
 
 use crate::*;
 
diff --git a/src/tools/miri/src/concurrency/data_race.rs b/src/tools/miri/src/concurrency/data_race.rs
index 797b3191d83..f86d1eb1dcb 100644
--- a/src/tools/miri/src/concurrency/data_race.rs
+++ b/src/tools/miri/src/concurrency/data_race.rs
@@ -44,13 +44,13 @@ use std::cell::{Cell, Ref, RefCell, RefMut};
 use std::fmt::Debug;
 use std::mem;
 
+use rustc_abi::{Align, HasDataLayout, Size};
 use rustc_ast::Mutability;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_index::{Idx, IndexVec};
 use rustc_middle::mir;
 use rustc_middle::ty::Ty;
 use rustc_span::Span;
-use rustc_target::abi::{Align, HasDataLayout, Size};
 
 use super::vector_clock::{VClock, VTimestamp, VectorIdx};
 use super::weak_memory::EvalContextExt as _;
diff --git a/src/tools/miri/src/concurrency/range_object_map.rs b/src/tools/miri/src/concurrency/range_object_map.rs
index d36ed36ac1a..4c9cf3dc635 100644
--- a/src/tools/miri/src/concurrency/range_object_map.rs
+++ b/src/tools/miri/src/concurrency/range_object_map.rs
@@ -4,8 +4,8 @@
 
 use std::ops::{Index, IndexMut, Range};
 
+use rustc_abi::Size;
 use rustc_const_eval::interpret::AllocRange;
-use rustc_target::abi::Size;
 
 #[derive(Clone, Debug)]
 struct Elem<T> {
diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs
index 199aedfa6d2..ef4034cc0c1 100644
--- a/src/tools/miri/src/concurrency/sync.rs
+++ b/src/tools/miri/src/concurrency/sync.rs
@@ -1,11 +1,14 @@
+use std::cell::RefCell;
 use std::collections::VecDeque;
 use std::collections::hash_map::Entry;
+use std::default::Default;
 use std::ops::Not;
+use std::rc::Rc;
 use std::time::Duration;
 
+use rustc_abi::Size;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_index::{Idx, IndexVec};
-use rustc_target::abi::Size;
 
 use super::init_once::InitOnce;
 use super::vector_clock::VClock;
@@ -44,8 +47,6 @@ macro_rules! declare_id {
 }
 pub(super) use declare_id;
 
-declare_id!(MutexId);
-
 /// The mutex state.
 #[derive(Default, Debug)]
 struct Mutex {
@@ -59,6 +60,21 @@ struct Mutex {
     clock: VClock,
 }
 
+#[derive(Default, Clone, Debug)]
+pub struct MutexRef(Rc<RefCell<Mutex>>);
+
+impl MutexRef {
+    fn new() -> Self {
+        MutexRef(Rc::new(RefCell::new(Mutex::default())))
+    }
+}
+
+impl VisitProvenance for MutexRef {
+    fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
+        // Mutex contains no provenance.
+    }
+}
+
 declare_id!(RwLockId);
 
 /// The read-write lock state.
@@ -121,6 +137,15 @@ struct Futex {
     clock: VClock,
 }
 
+#[derive(Default, Clone)]
+pub struct FutexRef(Rc<RefCell<Futex>>);
+
+impl VisitProvenance for FutexRef {
+    fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
+        // No provenance in `Futex`.
+    }
+}
+
 /// A thread waiting on a futex.
 #[derive(Debug)]
 struct FutexWaiter {
@@ -133,13 +158,9 @@ struct FutexWaiter {
 /// The state of all synchronization objects.
 #[derive(Default, Debug)]
 pub struct SynchronizationObjects {
-    mutexes: IndexVec<MutexId, Mutex>,
     rwlocks: IndexVec<RwLockId, RwLock>,
     condvars: IndexVec<CondvarId, Condvar>,
     pub(super) init_onces: IndexVec<InitOnceId, InitOnce>,
-
-    /// Futex info for the futex at the given address.
-    futexes: FxHashMap<u64, Futex>,
 }
 
 // Private extension trait for local helper methods
@@ -147,17 +168,17 @@ impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
 pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn condvar_reacquire_mutex(
         &mut self,
-        mutex: MutexId,
+        mutex_ref: &MutexRef,
         retval: Scalar,
         dest: MPlaceTy<'tcx>,
     ) -> InterpResult<'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, Some((retval, dest)));
+        if this.mutex_is_locked(mutex_ref) {
+            assert_ne!(this.mutex_get_owner(mutex_ref), this.active_thread());
+            this.mutex_enqueue_and_block(mutex_ref, Some((retval, dest)));
         } else {
             // We can have it right now!
-            this.mutex_lock(mutex);
+            this.mutex_lock(mutex_ref);
             // Don't forget to write the return value.
             this.write_scalar(retval, &dest)?;
         }
@@ -166,10 +187,9 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
 }
 
 impl SynchronizationObjects {
-    pub fn mutex_create(&mut self) -> MutexId {
-        self.mutexes.push(Default::default())
+    pub fn mutex_create(&mut self) -> MutexRef {
+        MutexRef::new()
     }
-
     pub fn rwlock_create(&mut self) -> RwLockId {
         self.rwlocks.push(Default::default())
     }
@@ -184,7 +204,7 @@ impl SynchronizationObjects {
 }
 
 impl<'tcx> AllocExtra<'tcx> {
-    pub fn get_sync<T: 'static>(&self, offset: Size) -> Option<&T> {
+    fn get_sync<T: 'static>(&self, offset: Size) -> Option<&T> {
         self.sync.get(&offset).and_then(|s| s.downcast_ref::<T>())
     }
 }
@@ -201,12 +221,16 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
 pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     /// Helper for lazily initialized `alloc_extra.sync` data:
     /// this forces an immediate init.
-    fn lazy_sync_init<T: 'static + Copy>(
-        &mut self,
+    /// Return a reference to the data in the machine state.
+    fn lazy_sync_init<'a, T: 'static>(
+        &'a mut self,
         primitive: &MPlaceTy<'tcx>,
         init_offset: Size,
         data: T,
-    ) -> InterpResult<'tcx> {
+    ) -> InterpResult<'tcx, &'a T>
+    where
+        'tcx: 'a,
+    {
         let this = self.eval_context_mut();
 
         let (alloc, offset, _) = this.ptr_get_alloc_id(primitive.ptr(), 0)?;
@@ -219,7 +243,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             &init_field,
             AtomicWriteOrd::Relaxed,
         )?;
-        interp_ok(())
+        interp_ok(this.get_alloc_extra(alloc)?.get_sync::<T>(offset).unwrap())
     }
 
     /// Helper for lazily initialized `alloc_extra.sync` data:
@@ -227,13 +251,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     /// - If yes, fetches the data from `alloc_extra.sync`, or calls `missing_data` if that fails
     ///   and stores that in `alloc_extra.sync`.
     /// - Otherwise, calls `new_data` to initialize the primitive.
-    fn lazy_sync_get_data<T: 'static + Copy>(
-        &mut self,
+    ///
+    /// Return a reference to the data in the machine state.
+    fn lazy_sync_get_data<'a, T: 'static>(
+        &'a mut self,
         primitive: &MPlaceTy<'tcx>,
         init_offset: Size,
         missing_data: impl FnOnce() -> InterpResult<'tcx, T>,
         new_data: impl FnOnce(&mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, T>,
-    ) -> InterpResult<'tcx, T> {
+    ) -> InterpResult<'tcx, &'a T>
+    where
+        'tcx: 'a,
+    {
         let this = self.eval_context_mut();
 
         // Check if this is already initialized. Needs to be atomic because we can race with another
@@ -257,64 +286,65 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // or else it has been moved illegally.
             let (alloc, offset, _) = this.ptr_get_alloc_id(primitive.ptr(), 0)?;
             let (alloc_extra, _machine) = this.get_alloc_extra_mut(alloc)?;
-            if let Some(data) = alloc_extra.get_sync::<T>(offset) {
-                interp_ok(*data)
-            } else {
+            // Due to borrow checker reasons, we have to do the lookup twice.
+            if alloc_extra.get_sync::<T>(offset).is_none() {
                 let data = missing_data()?;
                 alloc_extra.sync.insert(offset, Box::new(data));
-                interp_ok(data)
             }
+            interp_ok(alloc_extra.get_sync::<T>(offset).unwrap())
         } else {
             let data = new_data(this)?;
-            this.lazy_sync_init(primitive, init_offset, data)?;
-            interp_ok(data)
+            this.lazy_sync_init(primitive, init_offset, data)
         }
     }
 
     /// Get the synchronization primitive associated with the given pointer,
     /// or initialize a new one.
+    ///
+    /// Return `None` if this pointer does not point to at least 1 byte of mutable memory.
     fn get_sync_or_init<'a, T: 'static>(
         &'a mut self,
         ptr: Pointer,
-        new: impl FnOnce(&'a mut MiriMachine<'tcx>) -> InterpResult<'tcx, T>,
-    ) -> InterpResult<'tcx, &'a T>
+        new: impl FnOnce(&'a mut MiriMachine<'tcx>) -> T,
+    ) -> Option<&'a T>
     where
         'tcx: 'a,
     {
         let this = self.eval_context_mut();
-        // Ensure there is memory behind this pointer, so that this allocation
-        // is truly the only place where the data could be stored.
-        this.check_ptr_access(ptr, Size::from_bytes(1), CheckInAllocMsg::InboundsTest)?;
-
-        let (alloc, offset, _) = this.ptr_get_alloc_id(ptr, 0)?;
-        let (alloc_extra, machine) = this.get_alloc_extra_mut(alloc)?;
+        if !this.ptr_try_get_alloc_id(ptr, 0).ok().is_some_and(|(alloc_id, offset, ..)| {
+            let info = this.get_alloc_info(alloc_id);
+            info.kind == AllocKind::LiveData && info.mutbl.is_mut() && offset < info.size
+        }) {
+            return None;
+        }
+        // This cannot fail now.
+        let (alloc, offset, _) = this.ptr_get_alloc_id(ptr, 0).unwrap();
+        let (alloc_extra, machine) = this.get_alloc_extra_mut(alloc).unwrap();
         // Due to borrow checker reasons, we have to do the lookup twice.
         if alloc_extra.get_sync::<T>(offset).is_none() {
-            let new = new(machine)?;
+            let new = new(machine);
             alloc_extra.sync.insert(offset, Box::new(new));
         }
-        interp_ok(alloc_extra.get_sync::<T>(offset).unwrap())
+        Some(alloc_extra.get_sync::<T>(offset).unwrap())
     }
 
     #[inline]
     /// Get the id of the thread that currently owns this lock.
-    fn mutex_get_owner(&mut self, id: MutexId) -> ThreadId {
-        let this = self.eval_context_ref();
-        this.machine.sync.mutexes[id].owner.unwrap()
+    fn mutex_get_owner(&self, mutex_ref: &MutexRef) -> ThreadId {
+        mutex_ref.0.borrow().owner.unwrap()
     }
 
     #[inline]
     /// Check if locked.
-    fn mutex_is_locked(&self, id: MutexId) -> bool {
-        let this = self.eval_context_ref();
-        this.machine.sync.mutexes[id].owner.is_some()
+    fn mutex_is_locked(&self, mutex_ref: &MutexRef) -> bool {
+        mutex_ref.0.borrow().owner.is_some()
     }
 
     /// Lock by setting the mutex owner and increasing the lock count.
-    fn mutex_lock(&mut self, id: MutexId) {
+    fn mutex_lock(&mut self, mutex_ref: &MutexRef) {
         let this = self.eval_context_mut();
         let thread = this.active_thread();
-        let mutex = &mut this.machine.sync.mutexes[id];
+        let mut mutex = mutex_ref.0.borrow_mut();
         if let Some(current_owner) = mutex.owner {
             assert_eq!(thread, current_owner, "mutex already locked by another thread");
             assert!(
@@ -334,9 +364,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     /// count. If the lock count reaches 0, release the lock and potentially
     /// give to a new owner. If the lock was not locked by the current thread,
     /// return `None`.
-    fn mutex_unlock(&mut self, id: MutexId) -> InterpResult<'tcx, Option<usize>> {
+    fn mutex_unlock(&mut self, mutex_ref: &MutexRef) -> InterpResult<'tcx, Option<usize>> {
         let this = self.eval_context_mut();
-        let mutex = &mut this.machine.sync.mutexes[id];
+        let mut mutex = mutex_ref.0.borrow_mut();
         interp_ok(if let Some(current_owner) = mutex.owner {
             // Mutex is locked.
             if current_owner != this.machine.threads.active_thread() {
@@ -354,8 +384,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                         mutex.clock.clone_from(clock)
                     });
                 }
-                if let Some(thread) = this.machine.sync.mutexes[id].queue.pop_front() {
-                    this.unblock_thread(thread, BlockReason::Mutex(id))?;
+                let thread_id = mutex.queue.pop_front();
+                // We need to drop our mutex borrow before unblock_thread
+                // because it will be borrowed again in the unblock callback.
+                drop(mutex);
+                if thread_id.is_some() {
+                    this.unblock_thread(thread_id.unwrap(), BlockReason::Mutex)?;
                 }
             }
             Some(old_lock_count)
@@ -372,24 +406,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     #[inline]
     fn mutex_enqueue_and_block(
         &mut self,
-        id: MutexId,
+        mutex_ref: &MutexRef,
         retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>,
     ) {
         let this = self.eval_context_mut();
-        assert!(this.mutex_is_locked(id), "queing on unlocked mutex");
+        assert!(this.mutex_is_locked(mutex_ref), "queuing on unlocked mutex");
         let thread = this.active_thread();
-        this.machine.sync.mutexes[id].queue.push_back(thread);
+        mutex_ref.0.borrow_mut().queue.push_back(thread);
+        let mutex_ref = mutex_ref.clone();
         this.block_thread(
-            BlockReason::Mutex(id),
+            BlockReason::Mutex,
             None,
             callback!(
                 @capture<'tcx> {
-                    id: MutexId,
+                    mutex_ref: MutexRef,
                     retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>,
                 }
                 @unblock = |this| {
-                    assert!(!this.mutex_is_locked(id));
-                    this.mutex_lock(id);
+                    assert!(!this.mutex_is_locked(&mutex_ref));
+                    this.mutex_lock(&mutex_ref);
 
                     if let Some((retval, dest)) = retval_dest {
                         this.write_scalar(retval, &dest)?;
@@ -610,14 +645,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn condvar_wait(
         &mut self,
         condvar: CondvarId,
-        mutex: MutexId,
+        mutex_ref: MutexRef,
         timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>,
         retval_succ: Scalar,
         retval_timeout: Scalar,
         dest: MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
-        if let Some(old_locked_count) = this.mutex_unlock(mutex)? {
+        if let Some(old_locked_count) = this.mutex_unlock(&mutex_ref)? {
             if old_locked_count != 1 {
                 throw_unsup_format!(
                     "awaiting a condvar on a mutex acquired multiple times is not supported"
@@ -637,7 +672,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             callback!(
                 @capture<'tcx> {
                     condvar: CondvarId,
-                    mutex: MutexId,
+                    mutex_ref: MutexRef,
                     retval_succ: Scalar,
                     retval_timeout: Scalar,
                     dest: MPlaceTy<'tcx>,
@@ -652,7 +687,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     }
                     // Try to acquire the mutex.
                     // The timeout only applies to the first wait (until the signal), not for mutex acquisition.
-                    this.condvar_reacquire_mutex(mutex, retval_succ, dest)
+                    this.condvar_reacquire_mutex(&mutex_ref, retval_succ, dest)
                 }
                 @timeout = |this| {
                     // We have to remove the waiter from the queue again.
@@ -660,7 +695,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     let waiters = &mut this.machine.sync.condvars[condvar].waiters;
                     waiters.retain(|waiter| *waiter != thread);
                     // Now get back the lock.
-                    this.condvar_reacquire_mutex(mutex, retval_timeout, dest)
+                    this.condvar_reacquire_mutex(&mutex_ref, retval_timeout, dest)
                 }
             ),
         );
@@ -690,33 +725,35 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     /// On a timeout, `retval_timeout` is written to `dest` and `errno_timeout` is set as the last error.
     fn futex_wait(
         &mut self,
-        addr: u64,
+        futex_ref: FutexRef,
         bitset: u32,
         timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>,
         retval_succ: Scalar,
         retval_timeout: Scalar,
         dest: MPlaceTy<'tcx>,
-        errno_timeout: Scalar,
+        errno_timeout: IoError,
     ) {
         let this = self.eval_context_mut();
         let thread = this.active_thread();
-        let futex = &mut this.machine.sync.futexes.entry(addr).or_default();
+        let mut futex = futex_ref.0.borrow_mut();
         let waiters = &mut futex.waiters;
         assert!(waiters.iter().all(|waiter| waiter.thread != thread), "thread is already waiting");
         waiters.push_back(FutexWaiter { thread, bitset });
+        drop(futex);
+
         this.block_thread(
-            BlockReason::Futex { addr },
+            BlockReason::Futex,
             timeout,
             callback!(
                 @capture<'tcx> {
-                    addr: u64,
+                    futex_ref: FutexRef,
                     retval_succ: Scalar,
                     retval_timeout: Scalar,
                     dest: MPlaceTy<'tcx>,
-                    errno_timeout: Scalar,
+                    errno_timeout: IoError,
                 }
                 @unblock = |this| {
-                    let futex = this.machine.sync.futexes.get(&addr).unwrap();
+                    let futex = futex_ref.0.borrow();
                     // Acquire the clock of the futex.
                     if let Some(data_race) = &this.machine.data_race {
                         data_race.acquire_clock(&futex.clock, &this.machine.threads);
@@ -728,7 +765,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 @timeout = |this| {
                     // Remove the waiter from the futex.
                     let thread = this.active_thread();
-                    let futex = this.machine.sync.futexes.get_mut(&addr).unwrap();
+                    let mut futex = futex_ref.0.borrow_mut();
                     futex.waiters.retain(|waiter| waiter.thread != thread);
                     // Set errno and write return value.
                     this.set_last_error(errno_timeout)?;
@@ -739,12 +776,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         );
     }
 
+    /// Wake up the first thread in the queue that matches any of the bits in the bitset.
     /// Returns whether anything was woken.
-    fn futex_wake(&mut self, addr: u64, bitset: u32) -> InterpResult<'tcx, bool> {
+    fn futex_wake(&mut self, futex_ref: &FutexRef, bitset: u32) -> InterpResult<'tcx, bool> {
         let this = self.eval_context_mut();
-        let Some(futex) = this.machine.sync.futexes.get_mut(&addr) else {
-            return interp_ok(false);
-        };
+        let mut futex = futex_ref.0.borrow_mut();
         let data_race = &this.machine.data_race;
 
         // Each futex-wake happens-before the end of the futex wait
@@ -757,7 +793,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             return interp_ok(false);
         };
         let waiter = futex.waiters.remove(i).unwrap();
-        this.unblock_thread(waiter.thread, BlockReason::Futex { addr })?;
+        drop(futex);
+        this.unblock_thread(waiter.thread, BlockReason::Futex)?;
         interp_ok(true)
     }
 }
diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs
index 3946cb5ee54..59e2fdd4285 100644
--- a/src/tools/miri/src/concurrency/thread.rs
+++ b/src/tools/miri/src/concurrency/thread.rs
@@ -1,12 +1,12 @@
 //! Implements threads.
 
 use std::mem;
-use std::num::TryFromIntError;
 use std::sync::atomic::Ordering::Relaxed;
 use std::task::Poll;
 use std::time::{Duration, SystemTime};
 
 use either::Either;
+use rustc_abi::ExternAbi;
 use rustc_const_eval::CTRL_C_RECEIVED;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_hir::def_id::DefId;
@@ -14,7 +14,6 @@ use rustc_index::{Idx, IndexVec};
 use rustc_middle::mir::Mutability;
 use rustc_middle::ty::layout::TyAndLayout;
 use rustc_span::Span;
-use rustc_target::spec::abi::Abi;
 
 use crate::concurrency::data_race;
 use crate::shims::tls;
@@ -114,6 +113,11 @@ impl ThreadId {
         self.0
     }
 
+    /// Create a new thread id from a `u32` without checking if this thread exists.
+    pub fn new_unchecked(id: u32) -> Self {
+        Self(id)
+    }
+
     pub const MAIN_THREAD: ThreadId = ThreadId(0);
 }
 
@@ -127,26 +131,6 @@ impl Idx for ThreadId {
     }
 }
 
-impl TryFrom<u64> for ThreadId {
-    type Error = TryFromIntError;
-    fn try_from(id: u64) -> Result<Self, Self::Error> {
-        u32::try_from(id).map(Self)
-    }
-}
-
-impl TryFrom<i128> for ThreadId {
-    type Error = TryFromIntError;
-    fn try_from(id: i128) -> Result<Self, Self::Error> {
-        u32::try_from(id).map(Self)
-    }
-}
-
-impl From<u32> for ThreadId {
-    fn from(id: u32) -> Self {
-        Self(id)
-    }
-}
-
 impl From<ThreadId> for u64 {
     fn from(t: ThreadId) -> Self {
         t.0.into()
@@ -162,17 +146,19 @@ pub enum BlockReason {
     /// Waiting for time to pass.
     Sleep,
     /// Blocked on a mutex.
-    Mutex(MutexId),
+    Mutex,
     /// Blocked on a condition variable.
     Condvar(CondvarId),
     /// Blocked on a reader-writer lock.
     RwLock(RwLockId),
     /// Blocked on a Futex variable.
-    Futex { addr: u64 },
+    Futex,
     /// Blocked on an InitOnce.
     InitOnce(InitOnceId),
     /// Blocked on epoll.
     Epoll,
+    /// Blocked on eventfd.
+    Eventfd,
 }
 
 /// The state of a thread.
@@ -448,6 +434,10 @@ pub enum TimeoutAnchor {
     Absolute,
 }
 
+/// An error signaling that the requested thread doesn't exist.
+#[derive(Debug, Copy, Clone)]
+pub struct ThreadNotFound;
+
 /// A set of threads.
 #[derive(Debug)]
 pub struct ThreadManager<'tcx> {
@@ -509,6 +499,16 @@ impl<'tcx> ThreadManager<'tcx> {
         }
     }
 
+    pub fn thread_id_try_from(&self, id: impl TryInto<u32>) -> Result<ThreadId, ThreadNotFound> {
+        if let Ok(id) = id.try_into()
+            && usize::try_from(id).is_ok_and(|id| id < self.threads.len())
+        {
+            Ok(ThreadId(id))
+        } else {
+            Err(ThreadNotFound)
+        }
+    }
+
     /// Check if we have an allocation for the given thread local static for the
     /// active thread.
     fn get_thread_local_alloc_id(&self, def_id: DefId) -> Option<StrictPointer> {
@@ -534,6 +534,7 @@ impl<'tcx> ThreadManager<'tcx> {
     ) -> &mut Vec<Frame<'tcx, Provenance, FrameExtra<'tcx>>> {
         &mut self.threads[self.active_thread].stack
     }
+
     pub fn all_stacks(
         &self,
     ) -> impl Iterator<Item = (ThreadId, &[Frame<'tcx, Provenance, FrameExtra<'tcx>>])> {
@@ -868,6 +869,11 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
 // Public interface to thread management.
 impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
 pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
+    #[inline]
+    fn thread_id_try_from(&self, id: impl TryInto<u32>) -> Result<ThreadId, ThreadNotFound> {
+        self.eval_context_ref().machine.threads.thread_id_try_from(id)
+    }
+
     /// Get a thread-specific allocation id for the given thread-local static.
     /// If needed, allocate a new one.
     fn get_or_create_thread_local_alloc(
@@ -911,7 +917,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         &mut self,
         thread: Option<MPlaceTy<'tcx>>,
         start_routine: Pointer,
-        start_abi: Abi,
+        start_abi: ExternAbi,
         func_arg: ImmTy<'tcx>,
         ret_layout: TyAndLayout<'tcx>,
     ) -> InterpResult<'tcx, ThreadId> {
@@ -1160,8 +1166,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     /// Set the name of the current thread. The buffer must not include the null terminator.
     #[inline]
     fn set_thread_name(&mut self, thread: ThreadId, new_thread_name: Vec<u8>) {
-        let this = self.eval_context_mut();
-        this.machine.threads.set_thread_name(thread, new_thread_name);
+        self.eval_context_mut().machine.threads.set_thread_name(thread, new_thread_name);
     }
 
     #[inline]
diff --git a/src/tools/miri/src/concurrency/weak_memory.rs b/src/tools/miri/src/concurrency/weak_memory.rs
index 800c301a821..c610f1999f7 100644
--- a/src/tools/miri/src/concurrency/weak_memory.rs
+++ b/src/tools/miri/src/concurrency/weak_memory.rs
@@ -300,7 +300,6 @@ impl<'tcx> StoreBuffer {
         interp_ok(())
     }
 
-    #[allow(clippy::if_same_then_else, clippy::needless_bool)]
     /// Selects a valid store element in the buffer.
     fn fetch_store<R: rand::Rng + ?Sized>(
         &self,
diff --git a/src/tools/miri/src/diagnostics.rs b/src/tools/miri/src/diagnostics.rs
index f055662891e..41b7be37c37 100644
--- a/src/tools/miri/src/diagnostics.rs
+++ b/src/tools/miri/src/diagnostics.rs
@@ -1,9 +1,9 @@
 use std::fmt::{self, Write};
 use std::num::NonZero;
 
+use rustc_abi::{Align, Size};
 use rustc_errors::{Diag, DiagMessage, Level};
 use rustc_span::{DUMMY_SP, SpanData, Symbol};
-use rustc_target::abi::{Align, Size};
 
 use crate::borrow_tracker::stacked_borrows::diagnostics::TagHistory;
 use crate::borrow_tracker::tree_borrows::diagnostics as tree_diagnostics;
@@ -195,7 +195,7 @@ pub fn prune_stacktrace<'tcx>(
                 // This len check ensures that we don't somehow remove every frame, as doing so breaks
                 // the primary error message.
                 while stacktrace.len() > 1
-                    && stacktrace.last().map_or(false, |frame| !machine.is_local(frame))
+                    && stacktrace.last().is_some_and(|frame| !machine.is_local(frame))
                 {
                     stacktrace.pop();
                 }
diff --git a/src/tools/miri/src/eval.rs b/src/tools/miri/src/eval.rs
index 9f93f151668..1df1d08802a 100644
--- a/src/tools/miri/src/eval.rs
+++ b/src/tools/miri/src/eval.rs
@@ -6,13 +6,13 @@ use std::path::PathBuf;
 use std::task::Poll;
 use std::{iter, thread};
 
+use rustc_abi::ExternAbi;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_hir::def::Namespace;
 use rustc_hir::def_id::DefId;
 use rustc_middle::ty::layout::{LayoutCx, LayoutOf};
 use rustc_middle::ty::{self, Ty, TyCtxt};
 use rustc_session::config::EntryFnType;
-use rustc_target::spec::abi::Abi;
 
 use crate::concurrency::thread::TlsAllocAction;
 use crate::diagnostics::report_leaks;
@@ -268,10 +268,10 @@ pub fn create_ecx<'tcx>(
     entry_type: EntryFnType,
     config: &MiriConfig,
 ) -> InterpResult<'tcx, InterpCx<'tcx, MiriMachine<'tcx>>> {
-    let param_env = ty::ParamEnv::reveal_all();
-    let layout_cx = LayoutCx::new(tcx, param_env);
+    let typing_env = ty::TypingEnv::fully_monomorphized();
+    let layout_cx = LayoutCx::new(tcx, typing_env);
     let mut ecx =
-        InterpCx::new(tcx, rustc_span::DUMMY_SP, param_env, MiriMachine::new(config, layout_cx));
+        InterpCx::new(tcx, rustc_span::DUMMY_SP, typing_env, MiriMachine::new(config, layout_cx));
 
     // Some parts of initialization require a full `InterpCx`.
     MiriMachine::late_init(&mut ecx, config, {
@@ -376,7 +376,7 @@ pub fn create_ecx<'tcx>(
             let main_ret_ty = main_ret_ty.no_bound_vars().unwrap();
             let start_instance = ty::Instance::try_resolve(
                 tcx,
-                ty::ParamEnv::reveal_all(),
+                typing_env,
                 start_id,
                 tcx.mk_args(&[ty::GenericArg::from(main_ret_ty)]),
             )
@@ -391,7 +391,7 @@ pub fn create_ecx<'tcx>(
 
             ecx.call_function(
                 start_instance,
-                Abi::Rust,
+                ExternAbi::Rust,
                 &[
                     ImmTy::from_scalar(
                         Scalar::from_pointer(main_ptr, &ecx),
@@ -409,7 +409,7 @@ pub fn create_ecx<'tcx>(
         EntryFnType::Start => {
             ecx.call_function(
                 entry_instance,
-                Abi::Rust,
+                ExternAbi::Rust,
                 &[argc, argv],
                 Some(&ret_place),
                 StackPopCleanup::Root { cleanup: true },
@@ -423,7 +423,7 @@ pub fn create_ecx<'tcx>(
 /// Evaluates the entry function specified by `entry_id`.
 /// Returns `Some(return_code)` if program executed completed.
 /// Returns `None` if an evaluation error occurred.
-#[allow(clippy::needless_lifetimes)]
+#[expect(clippy::needless_lifetimes)]
 pub fn eval_entry<'tcx>(
     tcx: TyCtxt<'tcx>,
     entry_id: DefId,
diff --git a/src/tools/miri/src/helpers.rs b/src/tools/miri/src/helpers.rs
index 17f664da853..b57ce4e070c 100644
--- a/src/tools/miri/src/helpers.rs
+++ b/src/tools/miri/src/helpers.rs
@@ -5,6 +5,7 @@ use std::time::Duration;
 use std::{cmp, iter};
 
 use rand::RngCore;
+use rustc_abi::{Align, ExternAbi, FieldIdx, FieldsShape, Size, Variants};
 use rustc_apfloat::Float;
 use rustc_apfloat::ieee::{Double, Half, Quad, Single};
 use rustc_hir::Safety;
@@ -18,8 +19,6 @@ use rustc_middle::ty::layout::{FnAbiOf, LayoutOf, MaybeResult, TyAndLayout};
 use rustc_middle::ty::{self, FloatTy, IntTy, Ty, TyCtxt, UintTy};
 use rustc_session::config::CrateType;
 use rustc_span::{Span, Symbol};
-use rustc_target::abi::{Align, FieldIdx, FieldsShape, Size, Variants};
-use rustc_target::spec::abi::Abi;
 
 use crate::*;
 
@@ -117,8 +116,7 @@ pub fn resolve_path<'tcx>(
 /// 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());
+    let ty = resolve_path(cx.tcx(), path, Namespace::TypeNS).ty(cx.tcx(), cx.typing_env());
     cx.layout_of(ty).to_result().ok().unwrap()
 }
 
@@ -157,7 +155,7 @@ pub fn iter_exported_symbols<'tcx>(
     for cnum in dependency_format.1.iter().enumerate().filter_map(|(num, &linkage)| {
         // We add 1 to the number because that's what rustc also does everywhere it
         // calls `CrateNum::new`...
-        #[allow(clippy::arithmetic_side_effects)]
+        #[expect(clippy::arithmetic_side_effects)]
         (linkage != Linkage::NotLinked).then_some(CrateNum::new(num + 1))
     }) {
         // We can ignore `_export_info` here: we are a Rust crate, and everything is exported
@@ -435,7 +433,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn call_function(
         &mut self,
         f: ty::Instance<'tcx>,
-        caller_abi: Abi,
+        caller_abi: ExternAbi,
         args: &[ImmTy<'tcx>],
         dest: Option<&MPlaceTy<'tcx>>,
         stack_pop: StackPopCleanup,
@@ -917,7 +915,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     }
 
     /// Check that the ABI is what we expect.
-    fn check_abi<'a>(&self, abi: Abi, exp_abi: Abi) -> InterpResult<'a, ()> {
+    fn check_abi<'a>(&self, abi: ExternAbi, exp_abi: ExternAbi) -> InterpResult<'a, ()> {
         if abi != exp_abi {
             throw_ub_format!(
                 "calling a function with ABI {} using caller ABI {}",
@@ -953,8 +951,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
     fn check_abi_and_shim_symbol_clash(
         &mut self,
-        abi: Abi,
-        exp_abi: Abi,
+        abi: ExternAbi,
+        exp_abi: ExternAbi,
         link_name: Symbol,
     ) -> InterpResult<'tcx, ()> {
         self.check_abi(abi, exp_abi)?;
@@ -978,8 +976,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
     fn check_shim<'a, const N: usize>(
         &mut self,
-        abi: Abi,
-        exp_abi: Abi,
+        abi: ExternAbi,
+        exp_abi: ExternAbi,
         link_name: Symbol,
         args: &'a [OpTy<'tcx>],
     ) -> InterpResult<'tcx, &'a [OpTy<'tcx>; N]>
@@ -1010,7 +1008,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let this = self.eval_context_ref();
 
         fn float_to_int_inner<'tcx, F: rustc_apfloat::Float>(
-            this: &MiriInterpCx<'tcx>,
+            ecx: &MiriInterpCx<'tcx>,
             src: F,
             cast_to: TyAndLayout<'tcx>,
             round: rustc_apfloat::Round,
@@ -1030,7 +1028,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // Nothing else
                 _ =>
                     span_bug!(
-                        this.cur_span(),
+                        ecx.cur_span(),
                         "attempted float-to-int conversion with non-int output type {}",
                         cast_to.ty,
                     ),
diff --git a/src/tools/miri/src/intrinsics/mod.rs b/src/tools/miri/src/intrinsics/mod.rs
index 776d2561b43..9eebbc5d363 100644
--- a/src/tools/miri/src/intrinsics/mod.rs
+++ b/src/tools/miri/src/intrinsics/mod.rs
@@ -4,11 +4,11 @@ mod atomic;
 mod simd;
 
 use rand::Rng;
+use rustc_abi::Size;
 use rustc_apfloat::{Float, Round};
 use rustc_middle::mir;
 use rustc_middle::ty::{self, FloatTy};
 use rustc_span::{Symbol, sym};
-use rustc_target::abi::Size;
 
 use self::atomic::EvalContextExt as _;
 use self::helpers::{ToHost, ToSoft, check_arg_count};
@@ -218,20 +218,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             => {
                 let [f] = check_arg_count(args)?;
                 let f = this.read_scalar(f)?.to_f32()?;
-                // Using host floats (but it's fine, these operations do not have guaranteed precision).
-                let f_host = f.to_host();
+                // Using host floats except for sqrt (but it's fine, these operations do not have
+                // guaranteed precision).
                 let res = match intrinsic_name {
-                    "sinf32" => f_host.sin(),
-                    "cosf32" => f_host.cos(),
-                    "sqrtf32" => f_host.sqrt(), // FIXME Using host floats, this should use full-precision soft-floats
-                    "expf32" => f_host.exp(),
-                    "exp2f32" => f_host.exp2(),
-                    "logf32" => f_host.ln(),
-                    "log10f32" => f_host.log10(),
-                    "log2f32" => f_host.log2(),
+                    "sinf32" => f.to_host().sin().to_soft(),
+                    "cosf32" => f.to_host().cos().to_soft(),
+                    "sqrtf32" => math::sqrt(f),
+                    "expf32" => f.to_host().exp().to_soft(),
+                    "exp2f32" => f.to_host().exp2().to_soft(),
+                    "logf32" => f.to_host().ln().to_soft(),
+                    "log10f32" => f.to_host().log10().to_soft(),
+                    "log2f32" => f.to_host().log2().to_soft(),
                     _ => bug!(),
                 };
-                let res = res.to_soft();
                 let res = this.adjust_nan(res, &[f]);
                 this.write_scalar(res, dest)?;
             }
@@ -247,20 +246,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             => {
                 let [f] = check_arg_count(args)?;
                 let f = this.read_scalar(f)?.to_f64()?;
-                // Using host floats (but it's fine, these operations do not have guaranteed precision).
-                let f_host = f.to_host();
+                // Using host floats except for sqrt (but it's fine, these operations do not have
+                // guaranteed precision).
                 let res = match intrinsic_name {
-                    "sinf64" => f_host.sin(),
-                    "cosf64" => f_host.cos(),
-                    "sqrtf64" => f_host.sqrt(), // FIXME Using host floats, this should use full-precision soft-floats
-                    "expf64" => f_host.exp(),
-                    "exp2f64" => f_host.exp2(),
-                    "logf64" => f_host.ln(),
-                    "log10f64" => f_host.log10(),
-                    "log2f64" => f_host.log2(),
+                    "sinf64" => f.to_host().sin().to_soft(),
+                    "cosf64" => f.to_host().cos().to_soft(),
+                    "sqrtf64" => math::sqrt(f),
+                    "expf64" => f.to_host().exp().to_soft(),
+                    "exp2f64" => f.to_host().exp2().to_soft(),
+                    "logf64" => f.to_host().ln().to_soft(),
+                    "log10f64" => f.to_host().log10().to_soft(),
+                    "log2f64" => f.to_host().log2().to_soft(),
                     _ => bug!(),
                 };
-                let res = res.to_soft();
                 let res = this.adjust_nan(res, &[f]);
                 this.write_scalar(res, dest)?;
             }
@@ -292,7 +290,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let b = this.read_scalar(b)?.to_f32()?;
                 let c = this.read_scalar(c)?.to_f32()?;
                 let fuse: bool = this.machine.rng.get_mut().gen();
-                #[allow(clippy::arithmetic_side_effects)] // float ops don't overflow
                 let res = if fuse {
                     // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
                     a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()
@@ -308,7 +305,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let b = this.read_scalar(b)?.to_f64()?;
                 let c = this.read_scalar(c)?.to_f64()?;
                 let fuse: bool = this.machine.rng.get_mut().gen();
-                #[allow(clippy::arithmetic_side_effects)] // float ops don't overflow
                 let res = if fuse {
                     // FIXME: Using host floats, to work around https://github.com/rust-lang/rustc_apfloat/issues/11
                     a.to_host().mul_add(b.to_host(), c.to_host()).to_soft()
diff --git a/src/tools/miri/src/intrinsics/simd.rs b/src/tools/miri/src/intrinsics/simd.rs
index f15b83a054f..075b6f35e0e 100644
--- a/src/tools/miri/src/intrinsics/simd.rs
+++ b/src/tools/miri/src/intrinsics/simd.rs
@@ -1,10 +1,10 @@
 use either::Either;
+use rustc_abi::{Endian, HasDataLayout};
 use rustc_apfloat::{Float, Round};
 use rustc_middle::ty::FloatTy;
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_middle::{mir, ty};
 use rustc_span::{Symbol, sym};
-use rustc_target::abi::{Endian, HasDataLayout};
 
 use crate::helpers::{ToHost, ToSoft, bool_to_simd_element, check_arg_count, simd_element_to_bool};
 use crate::*;
@@ -104,42 +104,39 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                             let ty::Float(float_ty) = op.layout.ty.kind() else {
                                 span_bug!(this.cur_span(), "{} operand is not a float", intrinsic_name)
                             };
-                            // Using host floats (but it's fine, these operations do not have guaranteed precision).
+                            // Using host floats except for sqrt (but it's fine, these operations do not
+                            // have guaranteed precision).
                             match float_ty {
                                 FloatTy::F16 => unimplemented!("f16_f128"),
                                 FloatTy::F32 => {
                                     let f = op.to_scalar().to_f32()?;
-                                    let f_host = f.to_host();
                                     let res = match host_op {
-                                        "fsqrt" => f_host.sqrt(), // FIXME Using host floats, this should use full-precision soft-floats
-                                        "fsin" => f_host.sin(),
-                                        "fcos" => f_host.cos(),
-                                        "fexp" => f_host.exp(),
-                                        "fexp2" => f_host.exp2(),
-                                        "flog" => f_host.ln(),
-                                        "flog2" => f_host.log2(),
-                                        "flog10" => f_host.log10(),
+                                        "fsqrt" => math::sqrt(f),
+                                        "fsin" => f.to_host().sin().to_soft(),
+                                        "fcos" => f.to_host().cos().to_soft(),
+                                        "fexp" => f.to_host().exp().to_soft(),
+                                        "fexp2" => f.to_host().exp2().to_soft(),
+                                        "flog" => f.to_host().ln().to_soft(),
+                                        "flog2" => f.to_host().log2().to_soft(),
+                                        "flog10" => f.to_host().log10().to_soft(),
                                         _ => bug!(),
                                     };
-                                    let res = res.to_soft();
                                     let res = this.adjust_nan(res, &[f]);
                                     Scalar::from(res)
                                 }
                                 FloatTy::F64 => {
                                     let f = op.to_scalar().to_f64()?;
-                                    let f_host = f.to_host();
                                     let res = match host_op {
-                                        "fsqrt" => f_host.sqrt(),
-                                        "fsin" => f_host.sin(),
-                                        "fcos" => f_host.cos(),
-                                        "fexp" => f_host.exp(),
-                                        "fexp2" => f_host.exp2(),
-                                        "flog" => f_host.ln(),
-                                        "flog2" => f_host.log2(),
-                                        "flog10" => f_host.log10(),
+                                        "fsqrt" => math::sqrt(f),
+                                        "fsin" => f.to_host().sin().to_soft(),
+                                        "fcos" => f.to_host().cos().to_soft(),
+                                        "fexp" => f.to_host().exp().to_soft(),
+                                        "fexp2" => f.to_host().exp2().to_soft(),
+                                        "flog" => f.to_host().ln().to_soft(),
+                                        "flog2" => f.to_host().log2().to_soft(),
+                                        "flog10" => f.to_host().log10().to_soft(),
                                         _ => bug!(),
                                     };
-                                    let res = res.to_soft();
                                     let res = this.adjust_nan(res, &[f]);
                                     Scalar::from(res)
                                 }
@@ -750,7 +747,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
                     let val = if simd_element_to_bool(mask)? {
                         // Size * u64 is implemented as always checked
-                        #[allow(clippy::arithmetic_side_effects)]
                         let ptr = ptr.wrapping_offset(dest.layout.size * i, this);
                         let place = this.ptr_to_mplace(ptr, dest.layout);
                         this.read_immediate(&place)?
@@ -774,7 +770,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
                     if simd_element_to_bool(mask)? {
                         // Size * u64 is implemented as always checked
-                        #[allow(clippy::arithmetic_side_effects)]
                         let ptr = ptr.wrapping_offset(val.layout.size * i, this);
                         let place = this.ptr_to_mplace(ptr, val.layout);
                         this.write_immediate(*val, &place)?
@@ -831,7 +826,7 @@ fn simd_bitmask_index(idx: u32, vec_len: u32, endianness: Endian) -> u32 {
     assert!(idx < vec_len);
     match endianness {
         Endian::Little => idx,
-        #[allow(clippy::arithmetic_side_effects)] // idx < vec_len
+        #[expect(clippy::arithmetic_side_effects)] // idx < vec_len
         Endian::Big => vec_len - 1 - idx, // reverse order of bits
     }
 }
diff --git a/src/tools/miri/src/lib.rs b/src/tools/miri/src/lib.rs
index f903ccbc25a..85c896563da 100644
--- a/src/tools/miri/src/lib.rs
+++ b/src/tools/miri/src/lib.rs
@@ -83,6 +83,7 @@ mod eval;
 mod helpers;
 mod intrinsics;
 mod machine;
+mod math;
 mod mono_hash_map;
 mod operator;
 mod provenance_gc;
@@ -122,7 +123,7 @@ pub use crate::concurrency::data_race::{
 };
 pub use crate::concurrency::init_once::{EvalContextExt as _, InitOnceId};
 pub use crate::concurrency::sync::{
-    CondvarId, EvalContextExt as _, MutexId, RwLockId, SynchronizationObjects,
+    CondvarId, EvalContextExt as _, MutexRef, RwLockId, SynchronizationObjects,
 };
 pub use crate::concurrency::thread::{
     BlockReason, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, TimeoutAnchor,
diff --git a/src/tools/miri/src/machine.rs b/src/tools/miri/src/machine.rs
index 60d096b92f2..814dc6d2b01 100644
--- a/src/tools/miri/src/machine.rs
+++ b/src/tools/miri/src/machine.rs
@@ -10,19 +10,20 @@ use std::{fmt, process};
 
 use rand::rngs::StdRng;
 use rand::{Rng, SeedableRng};
+use rustc_abi::{Align, ExternAbi, Size};
 use rustc_attr::InlineAttr;
 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 #[allow(unused)]
 use rustc_data_structures::static_assert_size;
 use rustc_middle::mir;
 use rustc_middle::query::TyCtxtAt;
-use rustc_middle::ty::layout::{HasTyCtxt, LayoutCx, LayoutError, LayoutOf, TyAndLayout};
+use rustc_middle::ty::layout::{
+    HasTyCtxt, HasTypingEnv, LayoutCx, LayoutError, LayoutOf, TyAndLayout,
+};
 use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
 use rustc_session::config::InliningThreshold;
 use rustc_span::def_id::{CrateNum, DefId};
 use rustc_span::{Span, SpanData, Symbol};
-use rustc_target::abi::{Align, Size};
-use rustc_target::spec::abi::Abi;
 
 use crate::concurrency::cpu_affinity::{self, CpuAffinityMask};
 use crate::concurrency::data_race::{self, NaReadType, NaWriteType};
@@ -692,7 +693,7 @@ impl<'tcx> MiriMachine<'tcx> {
             clock: Clock::new(config.isolated_op == IsolatedOp::Allow),
             #[cfg(unix)]
             native_lib: config.native_lib.as_ref().map(|lib_file_path| {
-                let target_triple = tcx.sess.opts.target_triple.triple();
+                let target_triple = tcx.sess.opts.target_triple.tuple();
                 // Check if host target == the session target.
                 if env!("TARGET") != target_triple {
                     panic!(
@@ -731,20 +732,20 @@ impl<'tcx> MiriMachine<'tcx> {
     }
 
     pub(crate) fn late_init(
-        this: &mut MiriInterpCx<'tcx>,
+        ecx: &mut MiriInterpCx<'tcx>,
         config: &MiriConfig,
         on_main_stack_empty: StackEmptyCallback<'tcx>,
     ) -> InterpResult<'tcx> {
-        EnvVars::init(this, config)?;
-        MiriMachine::init_extern_statics(this)?;
-        ThreadManager::init(this, on_main_stack_empty);
+        EnvVars::init(ecx, config)?;
+        MiriMachine::init_extern_statics(ecx)?;
+        ThreadManager::init(ecx, on_main_stack_empty);
         interp_ok(())
     }
 
-    pub(crate) fn add_extern_static(this: &mut MiriInterpCx<'tcx>, name: &str, ptr: Pointer) {
+    pub(crate) fn add_extern_static(ecx: &mut MiriInterpCx<'tcx>, name: &str, ptr: Pointer) {
         // This got just allocated, so there definitely is a pointer here.
         let ptr = ptr.into_pointer_or_addr().unwrap();
-        this.machine.extern_statics.try_insert(Symbol::intern(name), ptr).unwrap();
+        ecx.machine.extern_statics.try_insert(Symbol::intern(name), ptr).unwrap();
     }
 
     pub(crate) fn communicate(&self) -> bool {
@@ -1006,7 +1007,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
     fn find_mir_or_eval_fn(
         ecx: &mut MiriInterpCx<'tcx>,
         instance: ty::Instance<'tcx>,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[FnArg<'tcx, Provenance>],
         dest: &MPlaceTy<'tcx>,
         ret: Option<mir::BasicBlock>,
@@ -1033,7 +1034,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
     fn call_extra_fn(
         ecx: &mut MiriInterpCx<'tcx>,
         fn_val: DynSym,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[FnArg<'tcx, Provenance>],
         dest: &MPlaceTy<'tcx>,
         ret: Option<mir::BasicBlock>,
@@ -1075,7 +1076,7 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
         // Call the lang item.
         let panic = ecx.tcx.lang_items().get(reason.lang_item()).unwrap();
         let panic = ty::Instance::mono(ecx.tcx.tcx, panic);
-        ecx.call_function(panic, Abi::Rust, &[], None, StackPopCleanup::Goto {
+        ecx.call_function(panic, ExternAbi::Rust, &[], None, StackPopCleanup::Goto {
             ret: None,
             unwind: mir::UnwindAction::Unreachable,
         })?;
@@ -1126,10 +1127,11 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
             let Provenance::Concrete { alloc_id, .. } = ptr.provenance else {
                 panic!("extern_statics cannot contain wildcards")
             };
-            let (shim_size, shim_align, _kind) = ecx.get_alloc_info(alloc_id);
+            let info = ecx.get_alloc_info(alloc_id);
             let def_ty = ecx.tcx.type_of(def_id).instantiate_identity();
-            let extern_decl_layout = ecx.tcx.layout_of(ty::ParamEnv::empty().and(def_ty)).unwrap();
-            if extern_decl_layout.size != shim_size || extern_decl_layout.align.abi != shim_align {
+            let extern_decl_layout =
+                ecx.tcx.layout_of(ecx.typing_env().as_query_input(def_ty)).unwrap();
+            if extern_decl_layout.size != info.size || extern_decl_layout.align.abi != info.align {
                 throw_unsup_format!(
                     "extern static `{link_name}` has been declared as `{krate}::{name}` \
                     with a size of {decl_size} bytes and alignment of {decl_align} bytes, \
@@ -1139,8 +1141,8 @@ impl<'tcx> Machine<'tcx> for MiriMachine<'tcx> {
                     krate = ecx.tcx.crate_name(def_id.krate),
                     decl_size = extern_decl_layout.size.bytes(),
                     decl_align = extern_decl_layout.align.abi.bytes(),
-                    shim_size = shim_size.bytes(),
-                    shim_align = shim_align.bytes(),
+                    shim_size = info.size.bytes(),
+                    shim_align = info.align.bytes(),
                 )
             }
             interp_ok(ptr)
diff --git a/src/tools/miri/src/math.rs b/src/tools/miri/src/math.rs
new file mode 100644
index 00000000000..ed3d2d55678
--- /dev/null
+++ b/src/tools/miri/src/math.rs
@@ -0,0 +1,164 @@
+use rand::Rng as _;
+use rand::distributions::Distribution as _;
+use rustc_apfloat::Float as _;
+use rustc_apfloat::ieee::IeeeFloat;
+
+/// Disturbes a floating-point result by a relative error on the order of (-2^scale, 2^scale).
+pub(crate) fn apply_random_float_error<F: rustc_apfloat::Float>(
+    ecx: &mut crate::MiriInterpCx<'_>,
+    val: F,
+    err_scale: i32,
+) -> F {
+    let rng = ecx.machine.rng.get_mut();
+    // Generate a random integer in the range [0, 2^PREC).
+    let dist = rand::distributions::Uniform::new(0, 1 << F::PRECISION);
+    let err = F::from_u128(dist.sample(rng))
+        .value
+        .scalbn(err_scale.strict_sub(F::PRECISION.try_into().unwrap()));
+    // give it a random sign
+    let err = if rng.gen::<bool>() { -err } else { err };
+    // multiple the value with (1+err)
+    (val * (F::from_u128(1).value + err).value).value
+}
+
+pub(crate) fn sqrt<S: rustc_apfloat::ieee::Semantics>(x: IeeeFloat<S>) -> IeeeFloat<S> {
+    match x.category() {
+        // preserve zero sign
+        rustc_apfloat::Category::Zero => x,
+        // propagate NaN
+        rustc_apfloat::Category::NaN => x,
+        // sqrt of negative number is NaN
+        _ if x.is_negative() => IeeeFloat::NAN,
+        // sqrt(∞) = ∞
+        rustc_apfloat::Category::Infinity => IeeeFloat::INFINITY,
+        rustc_apfloat::Category::Normal => {
+            // Floating point precision, excluding the integer bit
+            let prec = i32::try_from(S::PRECISION).unwrap() - 1;
+
+            // x = 2^(exp - prec) * mant
+            // where mant is an integer with prec+1 bits
+            // mant is a u128, which should be large enough for the largest prec (112 for f128)
+            let mut exp = x.ilogb();
+            let mut mant = x.scalbn(prec - exp).to_u128(128).value;
+
+            if exp % 2 != 0 {
+                // Make exponent even, so it can be divided by 2
+                exp -= 1;
+                mant <<= 1;
+            }
+
+            // Bit-by-bit (base-2 digit-by-digit) sqrt of mant.
+            // mant is treated here as a fixed point number with prec fractional bits.
+            // mant will be shifted left by one bit to have an extra fractional bit, which
+            // will be used to determine the rounding direction.
+
+            // res is the truncated sqrt of mant, where one bit is added at each iteration.
+            let mut res = 0u128;
+            // rem is the remainder with the current res
+            // rem_i = 2^i * ((mant<<1) - res_i^2)
+            // starting with res = 0, rem = mant<<1
+            let mut rem = mant << 1;
+            // s_i = 2*res_i
+            let mut s = 0u128;
+            // d is used to iterate over bits, from high to low (d_i = 2^(-i))
+            let mut d = 1u128 << (prec + 1);
+
+            // For iteration j=i+1, we need to find largest b_j = 0 or 1 such that
+            //  (res_i + b_j * 2^(-j))^2 <= mant<<1
+            // Expanding (a + b)^2 = a^2 + b^2 + 2*a*b:
+            //  res_i^2 + (b_j * 2^(-j))^2 + 2 * res_i * b_j * 2^(-j) <= mant<<1
+            // And rearranging the terms:
+            //  b_j^2 * 2^(-j) + 2 * res_i * b_j <= 2^j * (mant<<1 - res_i^2)
+            //  b_j^2 * 2^(-j) + 2 * res_i * b_j <= rem_i
+
+            while d != 0 {
+                // Probe b_j^2 * 2^(-j) + 2 * res_i * b_j <= rem_i with b_j = 1:
+                // t = 2*res_i + 2^(-j)
+                let t = s + d;
+                if rem >= t {
+                    // b_j should be 1, so make res_j = res_i + 2^(-j) and adjust rem
+                    res += d;
+                    s += d + d;
+                    rem -= t;
+                }
+                // Adjust rem for next iteration
+                rem <<= 1;
+                // Shift iterator
+                d >>= 1;
+            }
+
+            // Remove extra fractional bit from result, rounding to nearest.
+            // If the last bit is 0, then the nearest neighbor is definitely the lower one.
+            // If the last bit is 1, it sounds like this may either be a tie (if there's
+            // infinitely many 0s after this 1), or the nearest neighbor is the upper one.
+            // However, since square roots are either exact or irrational, and an exact root
+            // would lead to the last "extra" bit being 0, we can exclude a tie in this case.
+            // We therefore always round up if the last bit is 1. When the last bit is 0,
+            // adding 1 will not do anything since the shift will discard it.
+            res = (res + 1) >> 1;
+
+            // Build resulting value with res as mantissa and exp/2 as exponent
+            IeeeFloat::from_u128(res).value.scalbn(exp / 2 - prec)
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use rustc_apfloat::ieee::{DoubleS, HalfS, IeeeFloat, QuadS, SingleS};
+
+    use super::sqrt;
+
+    #[test]
+    fn test_sqrt() {
+        #[track_caller]
+        fn test<S: rustc_apfloat::ieee::Semantics>(x: &str, expected: &str) {
+            let x: IeeeFloat<S> = x.parse().unwrap();
+            let expected: IeeeFloat<S> = expected.parse().unwrap();
+            let result = sqrt(x);
+            assert_eq!(result, expected);
+        }
+
+        fn exact_tests<S: rustc_apfloat::ieee::Semantics>() {
+            test::<S>("0", "0");
+            test::<S>("1", "1");
+            test::<S>("1.5625", "1.25");
+            test::<S>("2.25", "1.5");
+            test::<S>("4", "2");
+            test::<S>("5.0625", "2.25");
+            test::<S>("9", "3");
+            test::<S>("16", "4");
+            test::<S>("25", "5");
+            test::<S>("36", "6");
+            test::<S>("49", "7");
+            test::<S>("64", "8");
+            test::<S>("81", "9");
+            test::<S>("100", "10");
+
+            test::<S>("0.5625", "0.75");
+            test::<S>("0.25", "0.5");
+            test::<S>("0.0625", "0.25");
+            test::<S>("0.00390625", "0.0625");
+        }
+
+        exact_tests::<HalfS>();
+        exact_tests::<SingleS>();
+        exact_tests::<DoubleS>();
+        exact_tests::<QuadS>();
+
+        test::<SingleS>("2", "1.4142135");
+        test::<DoubleS>("2", "1.4142135623730951");
+
+        test::<SingleS>("1.1", "1.0488088");
+        test::<DoubleS>("1.1", "1.0488088481701516");
+
+        test::<SingleS>("2.2", "1.4832398");
+        test::<DoubleS>("2.2", "1.4832396974191326");
+
+        test::<SingleS>("1.22101e-40", "1.10499205e-20");
+        test::<DoubleS>("1.22101e-310", "1.1049932126488395e-155");
+
+        test::<SingleS>("3.4028235e38", "1.8446743e19");
+        test::<DoubleS>("1.7976931348623157e308", "1.3407807929942596e154");
+    }
+}
diff --git a/src/tools/miri/src/operator.rs b/src/tools/miri/src/operator.rs
index 608e23fc110..0017a3991b5 100644
--- a/src/tools/miri/src/operator.rs
+++ b/src/tools/miri/src/operator.rs
@@ -2,9 +2,9 @@ use std::iter;
 
 use rand::Rng;
 use rand::seq::IteratorRandom;
+use rustc_abi::Size;
 use rustc_apfloat::{Float, FloatConvert};
 use rustc_middle::mir;
-use rustc_target::abi::Size;
 
 use crate::*;
 
diff --git a/src/tools/miri/src/provenance_gc.rs b/src/tools/miri/src/provenance_gc.rs
index c5a35bc14f5..b3d715db9cd 100644
--- a/src/tools/miri/src/provenance_gc.rs
+++ b/src/tools/miri/src/provenance_gc.rs
@@ -89,6 +89,18 @@ impl VisitProvenance for Scalar {
     }
 }
 
+impl VisitProvenance for IoError {
+    fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
+        use crate::shims::io_error::IoError::*;
+        match self {
+            LibcError(_name) => (),
+            WindowsError(_name) => (),
+            HostError(_io_error) => (),
+            Raw(scalar) => scalar.visit_provenance(visit),
+        }
+    }
+}
+
 impl VisitProvenance for Immediate<Provenance> {
     fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
         match self {
@@ -183,10 +195,10 @@ impl LiveAllocs<'_, '_> {
     }
 }
 
-fn remove_unreachable_tags<'tcx>(this: &mut MiriInterpCx<'tcx>, tags: FxHashSet<BorTag>) {
+fn remove_unreachable_tags<'tcx>(ecx: &mut MiriInterpCx<'tcx>, tags: FxHashSet<BorTag>) {
     // Avoid iterating all allocations if there's no borrow tracker anyway.
-    if this.machine.borrow_tracker.is_some() {
-        this.memory.alloc_map().iter(|it| {
+    if ecx.machine.borrow_tracker.is_some() {
+        ecx.memory.alloc_map().iter(|it| {
             for (_id, (_kind, alloc)) in it {
                 alloc.extra.borrow_tracker.as_ref().unwrap().remove_unreachable_tags(&tags);
             }
@@ -194,16 +206,16 @@ fn remove_unreachable_tags<'tcx>(this: &mut MiriInterpCx<'tcx>, tags: FxHashSet<
     }
 }
 
-fn remove_unreachable_allocs<'tcx>(this: &mut MiriInterpCx<'tcx>, allocs: FxHashSet<AllocId>) {
-    let allocs = LiveAllocs { ecx: this, collected: allocs };
-    this.machine.allocation_spans.borrow_mut().retain(|id, _| allocs.is_live(*id));
-    this.machine.symbolic_alignment.borrow_mut().retain(|id, _| allocs.is_live(*id));
-    this.machine.alloc_addresses.borrow_mut().remove_unreachable_allocs(&allocs);
-    if let Some(borrow_tracker) = &this.machine.borrow_tracker {
+fn remove_unreachable_allocs<'tcx>(ecx: &mut MiriInterpCx<'tcx>, allocs: FxHashSet<AllocId>) {
+    let allocs = LiveAllocs { ecx, collected: allocs };
+    ecx.machine.allocation_spans.borrow_mut().retain(|id, _| allocs.is_live(*id));
+    ecx.machine.symbolic_alignment.borrow_mut().retain(|id, _| allocs.is_live(*id));
+    ecx.machine.alloc_addresses.borrow_mut().remove_unreachable_allocs(&allocs);
+    if let Some(borrow_tracker) = &ecx.machine.borrow_tracker {
         borrow_tracker.borrow_mut().remove_unreachable_allocs(&allocs);
     }
     // Clean up core (non-Miri-specific) state.
-    this.remove_unreachable_allocs(&allocs.collected);
+    ecx.remove_unreachable_allocs(&allocs.collected);
 }
 
 impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
diff --git a/src/tools/miri/src/range_map.rs b/src/tools/miri/src/range_map.rs
index 4a3670b76ac..2c2484cd0bc 100644
--- a/src/tools/miri/src/range_map.rs
+++ b/src/tools/miri/src/range_map.rs
@@ -7,7 +7,7 @@
 
 use std::ops;
 
-use rustc_target::abi::Size;
+use rustc_abi::Size;
 
 #[derive(Clone, Debug)]
 struct Elem<T> {
diff --git a/src/tools/miri/src/shims/alloc.rs b/src/tools/miri/src/shims/alloc.rs
index e73344367ec..25c0b52d061 100644
--- a/src/tools/miri/src/shims/alloc.rs
+++ b/src/tools/miri/src/shims/alloc.rs
@@ -1,7 +1,7 @@
 use std::iter;
 
+use rustc_abi::{Align, Size};
 use rustc_ast::expand::allocator::AllocatorKind;
-use rustc_target::abi::{Align, Size};
 
 use crate::*;
 
diff --git a/src/tools/miri/src/shims/backtrace.rs b/src/tools/miri/src/shims/backtrace.rs
index ae2cdaa8d57..64bd546458d 100644
--- a/src/tools/miri/src/shims/backtrace.rs
+++ b/src/tools/miri/src/shims/backtrace.rs
@@ -1,9 +1,8 @@
+use rustc_abi::{ExternAbi, Size};
 use rustc_ast::ast::Mutability;
 use rustc_middle::ty::layout::LayoutOf as _;
 use rustc_middle::ty::{self, Instance, Ty};
 use rustc_span::{BytePos, Loc, Symbol, hygiene};
-use rustc_target::abi::Size;
-use rustc_target::spec::abi::Abi;
 
 use crate::helpers::check_min_arg_count;
 use crate::*;
@@ -12,13 +11,13 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
 pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn handle_miri_backtrace_size(
         &mut self,
-        abi: Abi,
+        abi: ExternAbi,
         link_name: Symbol,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
-        let [flags] = this.check_shim(abi, Abi::Rust, link_name, args)?;
+        let [flags] = this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
 
         let flags = this.read_scalar(flags)?.to_u64()?;
         if flags != 0 {
@@ -32,7 +31,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
     fn handle_miri_get_backtrace(
         &mut self,
-        abi: Abi,
+        abi: ExternAbi,
         link_name: Symbol,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
@@ -73,7 +72,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // storage for pointers is allocated by miri
             // deallocating the slice is undefined behavior with a custom global allocator
             0 => {
-                let [_flags] = this.check_shim(abi, Abi::Rust, link_name, args)?;
+                let [_flags] = this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
 
                 let alloc = this.allocate(array_layout, MiriMemoryKind::Rust.into())?;
 
@@ -88,7 +87,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             // storage for pointers is allocated by the caller
             1 => {
-                let [_flags, buf] = this.check_shim(abi, Abi::Rust, link_name, args)?;
+                let [_flags, buf] = this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
 
                 let buf_place = this.deref_pointer(buf)?;
 
@@ -138,13 +137,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
     fn handle_miri_resolve_frame(
         &mut self,
-        abi: Abi,
+        abi: ExternAbi,
         link_name: Symbol,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
-        let [ptr, flags] = this.check_shim(abi, Abi::Rust, link_name, args)?;
+        let [ptr, flags] = this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
 
         let flags = this.read_scalar(flags)?.to_u64()?;
 
@@ -216,14 +215,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
     fn handle_miri_resolve_frame_names(
         &mut self,
-        abi: Abi,
+        abi: ExternAbi,
         link_name: Symbol,
         args: &[OpTy<'tcx>],
     ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
         let [ptr, flags, name_ptr, filename_ptr] =
-            this.check_shim(abi, Abi::Rust, link_name, args)?;
+            this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
 
         let flags = this.read_scalar(flags)?.to_u64()?;
         if flags != 0 {
diff --git a/src/tools/miri/src/shims/extern_static.rs b/src/tools/miri/src/shims/extern_static.rs
index 5559ea2750b..f0aebfe1693 100644
--- a/src/tools/miri/src/shims/extern_static.rs
+++ b/src/tools/miri/src/shims/extern_static.rs
@@ -4,13 +4,13 @@ use crate::*;
 
 impl<'tcx> MiriMachine<'tcx> {
     fn alloc_extern_static(
-        this: &mut MiriInterpCx<'tcx>,
+        ecx: &mut MiriInterpCx<'tcx>,
         name: &str,
         val: ImmTy<'tcx>,
     ) -> InterpResult<'tcx> {
-        let place = this.allocate(val.layout, MiriMemoryKind::ExternStatic.into())?;
-        this.write_immediate(*val, &place)?;
-        Self::add_extern_static(this, name, place.ptr());
+        let place = ecx.allocate(val.layout, MiriMemoryKind::ExternStatic.into())?;
+        ecx.write_immediate(*val, &place)?;
+        Self::add_extern_static(ecx, name, place.ptr());
         interp_ok(())
     }
 
@@ -18,72 +18,69 @@ impl<'tcx> MiriMachine<'tcx> {
     /// Most of them are for weak symbols, which we all set to null (indicating that the
     /// symbol is not supported, and triggering fallback code which ends up calling
     /// some other shim that we do support).
-    fn null_ptr_extern_statics(
-        this: &mut MiriInterpCx<'tcx>,
-        names: &[&str],
-    ) -> InterpResult<'tcx> {
+    fn null_ptr_extern_statics(ecx: &mut MiriInterpCx<'tcx>, names: &[&str]) -> InterpResult<'tcx> {
         for name in names {
-            let val = ImmTy::from_int(0, this.machine.layouts.usize);
-            Self::alloc_extern_static(this, name, val)?;
+            let val = ImmTy::from_int(0, ecx.machine.layouts.usize);
+            Self::alloc_extern_static(ecx, name, val)?;
         }
         interp_ok(())
     }
 
     /// Extern statics that are initialized with function pointers to the symbols of the same name.
     fn weak_symbol_extern_statics(
-        this: &mut MiriInterpCx<'tcx>,
+        ecx: &mut MiriInterpCx<'tcx>,
         names: &[&str],
     ) -> InterpResult<'tcx> {
         for name in names {
-            assert!(this.is_dyn_sym(name), "{name} is not a dynamic symbol");
-            let layout = this.machine.layouts.const_raw_ptr;
-            let ptr = this.fn_ptr(FnVal::Other(DynSym::from_str(name)));
-            let val = ImmTy::from_scalar(Scalar::from_pointer(ptr, this), layout);
-            Self::alloc_extern_static(this, name, val)?;
+            assert!(ecx.is_dyn_sym(name), "{name} is not a dynamic symbol");
+            let layout = ecx.machine.layouts.const_raw_ptr;
+            let ptr = ecx.fn_ptr(FnVal::Other(DynSym::from_str(name)));
+            let val = ImmTy::from_scalar(Scalar::from_pointer(ptr, ecx), layout);
+            Self::alloc_extern_static(ecx, name, val)?;
         }
         interp_ok(())
     }
 
     /// Sets up the "extern statics" for this machine.
-    pub fn init_extern_statics(this: &mut MiriInterpCx<'tcx>) -> InterpResult<'tcx> {
+    pub fn init_extern_statics(ecx: &mut MiriInterpCx<'tcx>) -> InterpResult<'tcx> {
         // "__rust_no_alloc_shim_is_unstable"
-        let val = ImmTy::from_int(0, this.machine.layouts.u8); // always 0, value does not matter
-        Self::alloc_extern_static(this, "__rust_no_alloc_shim_is_unstable", val)?;
+        let val = ImmTy::from_int(0, ecx.machine.layouts.u8); // always 0, value does not matter
+        Self::alloc_extern_static(ecx, "__rust_no_alloc_shim_is_unstable", val)?;
 
         // "__rust_alloc_error_handler_should_panic"
-        let val = this.tcx.sess.opts.unstable_opts.oom.should_panic();
-        let val = ImmTy::from_int(val, this.machine.layouts.u8);
-        Self::alloc_extern_static(this, "__rust_alloc_error_handler_should_panic", val)?;
+        let val = ecx.tcx.sess.opts.unstable_opts.oom.should_panic();
+        let val = ImmTy::from_int(val, ecx.machine.layouts.u8);
+        Self::alloc_extern_static(ecx, "__rust_alloc_error_handler_should_panic", val)?;
 
-        if this.target_os_is_unix() {
+        if ecx.target_os_is_unix() {
             // "environ" is mandated by POSIX.
-            let environ = this.machine.env_vars.unix().environ();
-            Self::add_extern_static(this, "environ", environ);
+            let environ = ecx.machine.env_vars.unix().environ();
+            Self::add_extern_static(ecx, "environ", environ);
         }
 
-        match this.tcx.sess.target.os.as_ref() {
+        match ecx.tcx.sess.target.os.as_ref() {
             "linux" => {
-                Self::null_ptr_extern_statics(this, &[
+                Self::null_ptr_extern_statics(ecx, &[
                     "__cxa_thread_atexit_impl",
                     "__clock_gettime64",
                 ])?;
-                Self::weak_symbol_extern_statics(this, &["getrandom", "statx"])?;
+                Self::weak_symbol_extern_statics(ecx, &["getrandom", "statx"])?;
             }
             "freebsd" => {
-                Self::null_ptr_extern_statics(this, &["__cxa_thread_atexit_impl"])?;
+                Self::null_ptr_extern_statics(ecx, &["__cxa_thread_atexit_impl"])?;
             }
             "android" => {
-                Self::null_ptr_extern_statics(this, &["bsd_signal"])?;
-                Self::weak_symbol_extern_statics(this, &["signal", "getrandom"])?;
+                Self::null_ptr_extern_statics(ecx, &["bsd_signal"])?;
+                Self::weak_symbol_extern_statics(ecx, &["signal", "getrandom"])?;
             }
             "windows" => {
                 // "_tls_used"
                 // This is some obscure hack that is part of the Windows TLS story. It's a `u8`.
-                let val = ImmTy::from_int(0, this.machine.layouts.u8);
-                Self::alloc_extern_static(this, "_tls_used", val)?;
+                let val = ImmTy::from_int(0, ecx.machine.layouts.u8);
+                Self::alloc_extern_static(ecx, "_tls_used", val)?;
             }
             "illumos" | "solaris" => {
-                Self::weak_symbol_extern_statics(this, &["pthread_setname_np"])?;
+                Self::weak_symbol_extern_statics(ecx, &["pthread_setname_np"])?;
             }
             _ => {} // No "extern statics" supported on this target
         }
diff --git a/src/tools/miri/src/shims/foreign_items.rs b/src/tools/miri/src/shims/foreign_items.rs
index 12f8facfd02..4dc857ef30b 100644
--- a/src/tools/miri/src/shims/foreign_items.rs
+++ b/src/tools/miri/src/shims/foreign_items.rs
@@ -3,6 +3,7 @@ use std::io::Write;
 use std::iter;
 use std::path::Path;
 
+use rustc_abi::{Align, AlignFromBytesError, ExternAbi, Size};
 use rustc_apfloat::Float;
 use rustc_ast::expand::allocator::alloc_error_handler_name;
 use rustc_hir::def::DefKind;
@@ -10,8 +11,6 @@ use rustc_hir::def_id::CrateNum;
 use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
 use rustc_middle::{mir, ty};
 use rustc_span::Symbol;
-use rustc_target::abi::{Align, AlignFromBytesError, Size};
-use rustc_target::spec::abi::Abi;
 
 use self::helpers::{ToHost, ToSoft};
 use super::alloc::EvalContextExt as _;
@@ -22,7 +21,7 @@ use crate::*;
 #[derive(Debug, Copy, Clone)]
 pub struct DynSym(Symbol);
 
-#[allow(clippy::should_implement_trait)]
+#[expect(clippy::should_implement_trait)]
 impl DynSym {
     pub fn from_str(name: &str) -> Self {
         DynSym(Symbol::intern(name))
@@ -40,7 +39,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_foreign_item(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
         ret: Option<mir::BasicBlock>,
@@ -107,7 +106,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_dyn_sym(
         &mut self,
         sym: DynSym,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
         ret: Option<mir::BasicBlock>,
@@ -219,7 +218,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_foreign_item_inner(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -240,7 +239,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // When adding a new shim, you should follow the following pattern:
         // ```
         // "shim_name" => {
-        //     let [arg1, arg2, arg3] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+        //     let [arg1, arg2, arg3] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
         //     let result = this.shim_name(arg1, arg2, arg3)?;
         //     this.write_scalar(result, dest)?;
         // }
@@ -278,16 +277,16 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
         match link_name.as_str() {
             // Miri-specific extern functions
             "miri_start_unwind" => {
-                let [payload] = this.check_shim(abi, Abi::Rust, link_name, args)?;
+                let [payload] = this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
                 this.handle_miri_start_unwind(payload)?;
                 return interp_ok(EmulateItemResult::NeedsUnwind);
             }
             "miri_run_provenance_gc" => {
-                let [] = this.check_shim(abi, Abi::Rust, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
                 this.run_provenance_gc();
             }
             "miri_get_alloc_id" => {
-                let [ptr] = this.check_shim(abi, Abi::Rust, link_name, args)?;
+                let [ptr] = this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
                 let (alloc_id, _, _) = this.ptr_get_alloc_id(ptr, 0).map_err_kind(|_e| {
                     err_machine_stop!(TerminationInfo::Abort(format!(
@@ -297,11 +296,11 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_scalar(Scalar::from_u64(alloc_id.0.get()), dest)?;
             }
             "miri_print_borrow_state" => {
-                let [id, show_unnamed] = this.check_shim(abi, Abi::Rust, link_name, args)?;
+                let [id, show_unnamed] = this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
                 let id = this.read_scalar(id)?.to_u64()?;
                 let show_unnamed = this.read_scalar(show_unnamed)?.to_bool()?;
                 if let Some(id) = std::num::NonZero::new(id).map(AllocId)
-                    && this.get_alloc_info(id).2 == AllocKind::LiveData
+                    && this.get_alloc_info(id).kind == AllocKind::LiveData
                 {
                     this.print_borrow_state(id, show_unnamed)?;
                 } else {
@@ -311,7 +310,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             "miri_pointer_name" => {
                 // This associates a name to a tag. Very useful for debugging, and also makes
                 // tests more strict.
-                let [ptr, nth_parent, name] = this.check_shim(abi, Abi::Rust, link_name, args)?;
+                let [ptr, nth_parent, name] =
+                    this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
                 let nth_parent = this.read_scalar(nth_parent)?.to_u8()?;
                 let name = this.read_immediate(name)?;
@@ -324,7 +324,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.give_pointer_debug_name(ptr, nth_parent, &name)?;
             }
             "miri_static_root" => {
-                let [ptr] = this.check_shim(abi, Abi::Rust, link_name, args)?;
+                let [ptr] = this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
                 let (alloc_id, offset, _) = this.ptr_get_alloc_id(ptr, 0)?;
                 if offset != Size::ZERO {
@@ -335,7 +335,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.machine.static_roots.push(alloc_id);
             }
             "miri_host_to_target_path" => {
-                let [ptr, out, out_size] = this.check_shim(abi, Abi::Rust, link_name, args)?;
+                let [ptr, out, out_size] =
+                    this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
                 let out = this.read_pointer(out)?;
                 let out_size = this.read_scalar(out_size)?.to_target_usize(this)?;
@@ -371,7 +372,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Writes some bytes to the interpreter's stdout/stderr. See the
             // README for details.
             "miri_write_to_stdout" | "miri_write_to_stderr" => {
-                let [msg] = this.check_shim(abi, Abi::Rust, link_name, args)?;
+                let [msg] = this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
                 let msg = this.read_immediate(msg)?;
                 let msg = this.read_byte_slice(&msg)?;
                 // Note: we're ignoring errors writing to host stdout/stderr.
@@ -383,9 +384,9 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             // Promises that a pointer has a given symbolic alignment.
             "miri_promise_symbolic_alignment" => {
-                use rustc_target::abi::AlignFromBytesError;
+                use rustc_abi::AlignFromBytesError;
 
-                let [ptr, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
+                let [ptr, align] = this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
                 let align = this.read_target_usize(align)?;
                 if !align.is_power_of_two() {
@@ -408,7 +409,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     );
                 }
                 if let Ok((alloc_id, offset, ..)) = this.ptr_try_get_alloc_id(ptr, 0) {
-                    let (_size, alloc_align, _kind) = this.get_alloc_info(alloc_id);
+                    let alloc_align = this.get_alloc_info(alloc_id).align;
                     // If the newly promised alignment is bigger than the native alignment of this
                     // allocation, and bigger than the previously promised alignment, then set it.
                     if align > alloc_align
@@ -426,12 +427,13 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
             // Aborting the process.
             "exit" => {
-                let [code] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [code] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let code = this.read_scalar(code)?.to_i32()?;
                 throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false });
             }
             "abort" => {
-                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 throw_machine_stop!(TerminationInfo::Abort(
                     "the program aborted execution".to_owned()
                 ))
@@ -439,7 +441,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
             // Standard C allocation
             "malloc" => {
-                let [size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [size] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let size = this.read_target_usize(size)?;
                 if size <= this.max_size_of_val().bytes() {
                     let res = this.malloc(size, /*zero_init:*/ false)?;
@@ -454,7 +457,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             "calloc" => {
                 let [items, elem_size] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let items = this.read_target_usize(items)?;
                 let elem_size = this.read_target_usize(elem_size)?;
                 if let Some(size) = this.compute_size_in_bytes(Size::from_bytes(elem_size), items) {
@@ -469,13 +472,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 }
             }
             "free" => {
-                let [ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [ptr] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
                 this.free(ptr)?;
             }
             "realloc" => {
                 let [old_ptr, new_size] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let old_ptr = this.read_pointer(old_ptr)?;
                 let new_size = this.read_target_usize(new_size)?;
                 if new_size <= this.max_size_of_val().bytes() {
@@ -492,14 +496,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
             // Rust allocation
             "__rust_alloc" | "miri_alloc" => {
-                let default = |this: &mut MiriInterpCx<'tcx>| {
+                let default = |ecx: &mut MiriInterpCx<'tcx>| {
                     // Only call `check_shim` when `#[global_allocator]` isn't used. When that
                     // macro is used, we act like no shim exists, so that the exported function can run.
-                    let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
-                    let size = this.read_target_usize(size)?;
-                    let align = this.read_target_usize(align)?;
+                    let [size, align] = ecx.check_shim(abi, ExternAbi::Rust, link_name, args)?;
+                    let size = ecx.read_target_usize(size)?;
+                    let align = ecx.read_target_usize(align)?;
 
-                    this.check_rustc_alloc_request(size, align)?;
+                    ecx.check_rustc_alloc_request(size, align)?;
 
                     let memory_kind = match link_name.as_str() {
                         "__rust_alloc" => MiriMemoryKind::Rust,
@@ -507,13 +511,13 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                         _ => unreachable!(),
                     };
 
-                    let ptr = this.allocate_ptr(
+                    let ptr = ecx.allocate_ptr(
                         Size::from_bytes(size),
                         Align::from_bytes(align).unwrap(),
                         memory_kind.into(),
                     )?;
 
-                    this.write_pointer(ptr, dest)
+                    ecx.write_pointer(ptr, dest)
                 };
 
                 match link_name.as_str() {
@@ -529,7 +533,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 return this.emulate_allocator(|this| {
                     // See the comment for `__rust_alloc` why `check_shim` is only called in the
                     // default case.
-                    let [size, align] = this.check_shim(abi, Abi::Rust, link_name, args)?;
+                    let [size, align] = this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
                     let size = this.read_target_usize(size)?;
                     let align = this.read_target_usize(align)?;
 
@@ -551,14 +555,14 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 });
             }
             "__rust_dealloc" | "miri_dealloc" => {
-                let default = |this: &mut MiriInterpCx<'tcx>| {
+                let default = |ecx: &mut MiriInterpCx<'tcx>| {
                     // See the comment for `__rust_alloc` why `check_shim` is only called in the
                     // default case.
                     let [ptr, old_size, align] =
-                        this.check_shim(abi, Abi::Rust, link_name, args)?;
-                    let ptr = this.read_pointer(ptr)?;
-                    let old_size = this.read_target_usize(old_size)?;
-                    let align = this.read_target_usize(align)?;
+                        ecx.check_shim(abi, ExternAbi::Rust, link_name, args)?;
+                    let ptr = ecx.read_pointer(ptr)?;
+                    let old_size = ecx.read_target_usize(old_size)?;
+                    let align = ecx.read_target_usize(align)?;
 
                     let memory_kind = match link_name.as_str() {
                         "__rust_dealloc" => MiriMemoryKind::Rust,
@@ -567,7 +571,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     };
 
                     // No need to check old_size/align; we anyway check that they match the allocation.
-                    this.deallocate_ptr(
+                    ecx.deallocate_ptr(
                         ptr,
                         Some((Size::from_bytes(old_size), Align::from_bytes(align).unwrap())),
                         memory_kind.into(),
@@ -590,7 +594,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     // See the comment for `__rust_alloc` why `check_shim` is only called in the
                     // default case.
                     let [ptr, old_size, align, new_size] =
-                        this.check_shim(abi, Abi::Rust, link_name, args)?;
+                        this.check_shim(abi, ExternAbi::Rust, link_name, args)?;
                     let ptr = this.read_pointer(ptr)?;
                     let old_size = this.read_target_usize(old_size)?;
                     let align = this.read_target_usize(align)?;
@@ -614,7 +618,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // C memory handling functions
             "memcmp" => {
                 let [left, right, n] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let left = this.read_pointer(left)?;
                 let right = this.read_pointer(right)?;
                 let n = Size::from_bytes(this.read_target_usize(n)?);
@@ -639,12 +643,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             "memrchr" => {
                 let [ptr, val, num] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
                 let val = this.read_scalar(val)?.to_i32()?;
                 let num = this.read_target_usize(num)?;
                 // The docs say val is "interpreted as unsigned char".
-                #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
+                #[expect(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
                 let val = val as u8;
 
                 // C requires that this must always be a valid pointer (C18 §7.1.4).
@@ -657,7 +661,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     .position(|&c| c == val)
                 {
                     let idx = u64::try_from(idx).unwrap();
-                    #[allow(clippy::arithmetic_side_effects)] // idx < num, so this never wraps
+                    #[expect(clippy::arithmetic_side_effects)] // idx < num, so this never wraps
                     let new_ptr = ptr.wrapping_offset(Size::from_bytes(num - idx - 1), this);
                     this.write_pointer(new_ptr, dest)?;
                 } else {
@@ -666,12 +670,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             "memchr" => {
                 let [ptr, val, num] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
                 let val = this.read_scalar(val)?.to_i32()?;
                 let num = this.read_target_usize(num)?;
                 // The docs say val is "interpreted as unsigned char".
-                #[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
+                #[expect(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
                 let val = val as u8;
 
                 // C requires that this must always be a valid pointer (C18 §7.1.4).
@@ -689,7 +693,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 }
             }
             "strlen" => {
-                let [ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [ptr] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
                 // This reads at least 1 byte, so we are already enforcing that this is a valid pointer.
                 let n = this.read_c_str(ptr)?.len();
@@ -699,7 +704,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 )?;
             }
             "wcslen" => {
-                let [ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [ptr] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
                 // This reads at least 1 byte, so we are already enforcing that this is a valid pointer.
                 let n = this.read_wchar_t_str(ptr)?.len();
@@ -710,7 +716,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             "memcpy" => {
                 let [ptr_dest, ptr_src, n] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let ptr_dest = this.read_pointer(ptr_dest)?;
                 let ptr_src = this.read_pointer(ptr_src)?;
                 let n = this.read_target_usize(n)?;
@@ -725,7 +731,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             "strcpy" => {
                 let [ptr_dest, ptr_src] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let ptr_dest = this.read_pointer(ptr_dest)?;
                 let ptr_src = this.read_pointer(ptr_src)?;
 
@@ -754,7 +760,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             | "expm1f"
             | "tgammaf"
             => {
-                let [f] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [f] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let f = this.read_scalar(f)?.to_f32()?;
                 // Using host floats (but it's fine, these operations do not have guaranteed precision).
                 let f_host = f.to_host();
@@ -782,7 +788,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             | "atan2f"
             | "fdimf"
             => {
-                let [f1, f2] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [f1, f2] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let f1 = this.read_scalar(f1)?.to_f32()?;
                 let f2 = this.read_scalar(f2)?.to_f32()?;
                 // underscore case for windows, here and below
@@ -811,7 +817,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             | "expm1"
             | "tgamma"
             => {
-                let [f] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [f] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let f = this.read_scalar(f)?.to_f64()?;
                 // Using host floats (but it's fine, these operations do not have guaranteed precision).
                 let f_host = f.to_host();
@@ -839,7 +845,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             | "atan2"
             | "fdim"
             => {
-                let [f1, f2] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [f1, f2] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let f1 = this.read_scalar(f1)?.to_f64()?;
                 let f2 = this.read_scalar(f2)?.to_f64()?;
                 // underscore case for windows, here and below
@@ -860,7 +866,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             | "ldexp"
             | "scalbn"
             => {
-                let [x, exp] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [x, exp] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 // For radix-2 (binary) systems, `ldexp` and `scalbn` are the same.
                 let x = this.read_scalar(x)?.to_f64()?;
                 let exp = this.read_scalar(exp)?.to_i32()?;
@@ -870,7 +876,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_scalar(res, dest)?;
             }
             "lgammaf_r" => {
-                let [x, signp] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [x, signp] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let x = this.read_scalar(x)?.to_f32()?;
                 let signp = this.deref_pointer(signp)?;
 
@@ -881,7 +888,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_scalar(res, dest)?;
             }
             "lgamma_r" => {
-                let [x, signp] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [x, signp] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let x = this.read_scalar(x)?.to_f64()?;
                 let signp = this.deref_pointer(signp)?;
 
@@ -895,7 +903,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // LLVM intrinsics
             "llvm.prefetch" => {
                 let [p, rw, loc, ty] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let _ = this.read_pointer(p)?;
                 let rw = this.read_scalar(rw)?.to_i32()?;
@@ -922,7 +930,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Used to implement the x86 `_mm{,256,512}_popcnt_epi{8,16,32,64}` and wasm
             // `{i,u}8x16_popcnt` functions.
             name if name.starts_with("llvm.ctpop.v") => {
-                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [op] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (op, op_len) = this.project_to_simd(op)?;
                 let (dest, dest_len) = this.project_to_simd(dest)?;
@@ -953,7 +961,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             // FIXME: Move these to an `arm` submodule.
             "llvm.aarch64.isb" if this.tcx.sess.target.arch == "aarch64" => {
-                let [arg] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?;
+                let [arg] = this.check_shim(abi, ExternAbi::Unadjusted, link_name, args)?;
                 let arg = this.read_scalar(arg)?.to_i32()?;
                 match arg {
                     // SY ("full system scope")
@@ -966,7 +974,7 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 }
             }
             "llvm.arm.hint" if this.tcx.sess.target.arch == "arm" => {
-                let [arg] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?;
+                let [arg] = this.check_shim(abi, ExternAbi::Unadjusted, link_name, args)?;
                 let arg = this.read_scalar(arg)?.to_i32()?;
                 // Note that different arguments might have different target feature requirements.
                 match arg {
diff --git a/src/tools/miri/src/shims/io_error.rs b/src/tools/miri/src/shims/io_error.rs
index 04491f0542b..0cbb4850b7f 100644
--- a/src/tools/miri/src/shims/io_error.rs
+++ b/src/tools/miri/src/shims/io_error.rs
@@ -7,6 +7,7 @@ use crate::*;
 #[derive(Debug)]
 pub enum IoError {
     LibcError(&'static str),
+    WindowsError(&'static str),
     HostError(io::Error),
     Raw(Scalar),
 }
@@ -113,6 +114,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let errno = match err.into() {
             HostError(err) => this.io_error_to_errnum(err)?,
             LibcError(name) => this.eval_libc(name),
+            WindowsError(name) => this.eval_windows("c", name),
             Raw(val) => val,
         };
         let errno_place = this.last_error_place()?;
@@ -186,7 +188,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     }
 
     /// The inverse of `io_error_to_errnum`.
-    #[allow(clippy::needless_return)]
+    #[expect(clippy::needless_return)]
     fn try_errnum_to_io_error(
         &self,
         errnum: Scalar,
diff --git a/src/tools/miri/src/shims/native_lib.rs b/src/tools/miri/src/shims/native_lib.rs
index 525bcd381d5..e7a4251242e 100644
--- a/src/tools/miri/src/shims/native_lib.rs
+++ b/src/tools/miri/src/shims/native_lib.rs
@@ -3,9 +3,9 @@ use std::ops::Deref;
 
 use libffi::high::call as ffi;
 use libffi::low::CodePtr;
+use rustc_abi::{BackendRepr, HasDataLayout};
 use rustc_middle::ty::{self as ty, IntTy, UintTy};
 use rustc_span::Symbol;
-use rustc_abi::{BackendRepr, HasDataLayout};
 
 use crate::*;
 
diff --git a/src/tools/miri/src/shims/panic.rs b/src/tools/miri/src/shims/panic.rs
index 9bb0d7d0ce6..722c3a2f0c5 100644
--- a/src/tools/miri/src/shims/panic.rs
+++ b/src/tools/miri/src/shims/panic.rs
@@ -11,10 +11,10 @@
 //!   gets popped *during unwinding*, we take the panic payload and store it according to the extra
 //!   metadata we remembered when pushing said frame.
 
+use rustc_abi::ExternAbi;
 use rustc_ast::Mutability;
 use rustc_middle::{mir, ty};
 use rustc_target::spec::PanicStrategy;
-use rustc_target::spec::abi::Abi;
 
 use self::helpers::check_arg_count;
 use crate::*;
@@ -88,7 +88,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         trace!("try_fn: {:?}", f_instance);
         this.call_function(
             f_instance,
-            Abi::Rust,
+            ExternAbi::Rust,
             &[data.clone()],
             None,
             // Directly return to caller.
@@ -139,7 +139,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             trace!("catch_fn: {:?}", f_instance);
             this.call_function(
                 f_instance,
-                Abi::Rust,
+                ExternAbi::Rust,
                 &[catch_unwind.data, payload],
                 None,
                 // Directly return to caller of `try`.
@@ -168,7 +168,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let panic = ty::Instance::mono(this.tcx.tcx, panic);
         this.call_function(
             panic,
-            Abi::Rust,
+            ExternAbi::Rust,
             &[this.mplace_to_ref(&msg)?],
             None,
             StackPopCleanup::Goto { ret: None, unwind },
@@ -187,7 +187,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let panic = ty::Instance::mono(this.tcx.tcx, panic);
         this.call_function(
             panic,
-            Abi::Rust,
+            ExternAbi::Rust,
             &[this.mplace_to_ref(&msg)?],
             None,
             StackPopCleanup::Goto { ret: None, unwind: mir::UnwindAction::Unreachable },
@@ -216,7 +216,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 let panic_bounds_check = ty::Instance::mono(this.tcx.tcx, panic_bounds_check);
                 this.call_function(
                     panic_bounds_check,
-                    Abi::Rust,
+                    ExternAbi::Rust,
                     &[index, len],
                     None,
                     StackPopCleanup::Goto { ret: None, unwind },
@@ -237,7 +237,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     ty::Instance::mono(this.tcx.tcx, panic_misaligned_pointer_dereference);
                 this.call_function(
                     panic_misaligned_pointer_dereference,
-                    Abi::Rust,
+                    ExternAbi::Rust,
                     &[required, found],
                     None,
                     StackPopCleanup::Goto { ret: None, unwind },
@@ -248,7 +248,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // Call the lang item associated with this message.
                 let fn_item = this.tcx.require_lang_item(msg.panic_function(), None);
                 let instance = ty::Instance::mono(this.tcx.tcx, fn_item);
-                this.call_function(instance, Abi::Rust, &[], None, StackPopCleanup::Goto {
+                this.call_function(instance, ExternAbi::Rust, &[], None, StackPopCleanup::Goto {
                     ret: None,
                     unwind,
                 })?;
diff --git a/src/tools/miri/src/shims/tls.rs b/src/tools/miri/src/shims/tls.rs
index 94b2d1bc782..46a417689a2 100644
--- a/src/tools/miri/src/shims/tls.rs
+++ b/src/tools/miri/src/shims/tls.rs
@@ -4,9 +4,8 @@ use std::collections::BTreeMap;
 use std::collections::btree_map::Entry as BTreeEntry;
 use std::task::Poll;
 
+use rustc_abi::{ExternAbi, HasDataLayout, Size};
 use rustc_middle::ty;
-use rustc_target::abi::{HasDataLayout, Size};
-use rustc_target::spec::abi::Abi;
 
 use crate::*;
 
@@ -54,7 +53,7 @@ impl<'tcx> Default for TlsData<'tcx> {
 impl<'tcx> TlsData<'tcx> {
     /// Generate a new TLS key with the given destructor.
     /// `max_size` determines the integer size the key has to fit in.
-    #[allow(clippy::arithmetic_side_effects)]
+    #[expect(clippy::arithmetic_side_effects)]
     pub fn create_tls_key(
         &mut self,
         dtor: Option<ty::Instance<'tcx>>,
@@ -323,7 +322,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // but both are ignored by std.
         this.call_function(
             thread_callback,
-            Abi::System { unwind: false },
+            ExternAbi::System { unwind: false },
             &[null_ptr.clone(), ImmTy::from_scalar(reason, this.machine.layouts.u32), null_ptr],
             None,
             StackPopCleanup::Root { cleanup: true },
@@ -344,7 +343,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
             this.call_function(
                 instance,
-                Abi::C { unwind: false },
+                ExternAbi::C { unwind: false },
                 &[ImmTy::from_scalar(data, this.machine.layouts.mut_raw_ptr)],
                 None,
                 StackPopCleanup::Root { cleanup: true },
@@ -381,7 +380,7 @@ trait EvalContextPrivExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
             this.call_function(
                 instance,
-                Abi::C { unwind: false },
+                ExternAbi::C { unwind: false },
                 &[ImmTy::from_scalar(ptr, this.machine.layouts.mut_raw_ptr)],
                 None,
                 StackPopCleanup::Root { cleanup: true },
diff --git a/src/tools/miri/src/shims/unix/android/foreign_items.rs b/src/tools/miri/src/shims/unix/android/foreign_items.rs
index b6f04951fc7..80ad40e1624 100644
--- a/src/tools/miri/src/shims/unix/android/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/android/foreign_items.rs
@@ -1,7 +1,8 @@
+use rustc_abi::ExternAbi;
 use rustc_span::Symbol;
-use rustc_target::spec::abi::Abi;
 
 use crate::shims::unix::android::thread::prctl;
+use crate::shims::unix::linux::syscall::syscall;
 use crate::*;
 
 pub fn is_dyn_sym(_name: &str) -> bool {
@@ -13,7 +14,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_foreign_item_inner(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -21,11 +22,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         match link_name.as_str() {
             // Miscellaneous
             "__errno" => {
-                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let errno_place = this.last_error_place()?;
                 this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
             }
 
+            // Dynamically invoked syscalls
+            "syscall" => syscall(this, link_name, abi, args, dest)?,
+
             // Threading
             "prctl" => prctl(this, link_name, abi, args, dest)?,
 
diff --git a/src/tools/miri/src/shims/unix/android/thread.rs b/src/tools/miri/src/shims/unix/android/thread.rs
index 6f5f0f74a22..f8a0b3a85a2 100644
--- a/src/tools/miri/src/shims/unix/android/thread.rs
+++ b/src/tools/miri/src/shims/unix/android/thread.rs
@@ -1,57 +1,56 @@
+use rustc_abi::{ExternAbi, Size};
 use rustc_span::Symbol;
-use rustc_target::abi::Size;
-use rustc_target::spec::abi::Abi;
 
 use crate::helpers::check_min_arg_count;
-use crate::shims::unix::thread::EvalContextExt as _;
+use crate::shims::unix::thread::{EvalContextExt as _, ThreadNameResult};
 use crate::*;
 
 const TASK_COMM_LEN: usize = 16;
 
 pub fn prctl<'tcx>(
-    this: &mut MiriInterpCx<'tcx>,
+    ecx: &mut MiriInterpCx<'tcx>,
     link_name: Symbol,
-    abi: Abi,
+    abi: ExternAbi,
     args: &[OpTy<'tcx>],
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx> {
     // We do not use `check_shim` here because `prctl` is variadic. The argument
     // count is checked bellow.
-    this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?;
+    ecx.check_abi_and_shim_symbol_clash(abi, ExternAbi::C { unwind: false }, link_name)?;
 
     // FIXME: Use constants once https://github.com/rust-lang/libc/pull/3941 backported to the 0.2 branch.
     let pr_set_name = 15;
     let pr_get_name = 16;
 
     let [op] = check_min_arg_count("prctl", args)?;
-    let res = match this.read_scalar(op)?.to_i32()? {
+    let res = match ecx.read_scalar(op)?.to_i32()? {
         op if op == pr_set_name => {
             let [_, name] = check_min_arg_count("prctl(PR_SET_NAME, ...)", args)?;
-            let name = this.read_scalar(name)?;
-            let thread = this.pthread_self()?;
+            let name = ecx.read_scalar(name)?;
+            let thread = ecx.pthread_self()?;
             // The Linux kernel silently truncates long names.
             // https://www.man7.org/linux/man-pages/man2/PR_SET_NAME.2const.html
             let res =
-                this.pthread_setname_np(thread, name, TASK_COMM_LEN, /* truncate */ true)?;
-            assert!(res);
+                ecx.pthread_setname_np(thread, name, TASK_COMM_LEN, /* truncate */ true)?;
+            assert_eq!(res, ThreadNameResult::Ok);
             Scalar::from_u32(0)
         }
         op if op == pr_get_name => {
             let [_, name] = check_min_arg_count("prctl(PR_GET_NAME, ...)", args)?;
-            let name = this.read_scalar(name)?;
-            let thread = this.pthread_self()?;
-            let len = Scalar::from_target_usize(TASK_COMM_LEN as u64, this);
-            this.check_ptr_access(
-                name.to_pointer(this)?,
+            let name = ecx.read_scalar(name)?;
+            let thread = ecx.pthread_self()?;
+            let len = Scalar::from_target_usize(TASK_COMM_LEN as u64, ecx);
+            ecx.check_ptr_access(
+                name.to_pointer(ecx)?,
                 Size::from_bytes(TASK_COMM_LEN),
                 CheckInAllocMsg::MemoryAccessTest,
             )?;
-            let res = this.pthread_getname_np(thread, name, len, /* truncate*/ false)?;
-            assert!(res);
+            let res = ecx.pthread_getname_np(thread, name, len, /* truncate*/ false)?;
+            assert_eq!(res, ThreadNameResult::Ok);
             Scalar::from_u32(0)
         }
         op => throw_unsup_format!("Miri does not support `prctl` syscall with op={}", op),
     };
-    this.write_scalar(res, dest)?;
+    ecx.write_scalar(res, dest)?;
     interp_ok(())
 }
diff --git a/src/tools/miri/src/shims/unix/env.rs b/src/tools/miri/src/shims/unix/env.rs
index 96c5a9fad9b..aebb5757aec 100644
--- a/src/tools/miri/src/shims/unix/env.rs
+++ b/src/tools/miri/src/shims/unix/env.rs
@@ -2,10 +2,10 @@ use std::ffi::{OsStr, OsString};
 use std::io::ErrorKind;
 use std::{env, mem};
 
+use rustc_abi::Size;
 use rustc_data_structures::fx::FxHashMap;
 use rustc_middle::ty::Ty;
 use rustc_middle::ty::layout::LayoutOf;
-use rustc_target::abi::Size;
 
 use crate::*;
 
diff --git a/src/tools/miri/src/shims/unix/fd.rs b/src/tools/miri/src/shims/unix/fd.rs
index f3db56695fc..27bdd508f77 100644
--- a/src/tools/miri/src/shims/unix/fd.rs
+++ b/src/tools/miri/src/shims/unix/fd.rs
@@ -7,7 +7,7 @@ use std::io::{self, ErrorKind, IsTerminal, Read, SeekFrom, Write};
 use std::ops::Deref;
 use std::rc::{Rc, Weak};
 
-use rustc_target::abi::Size;
+use rustc_abi::Size;
 
 use crate::helpers::check_min_arg_count;
 use crate::shims::unix::linux::epoll::EpollReadyEvents;
diff --git a/src/tools/miri/src/shims/unix/foreign_items.rs b/src/tools/miri/src/shims/unix/foreign_items.rs
index 355c93c444c..55202a08149 100644
--- a/src/tools/miri/src/shims/unix/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/foreign_items.rs
@@ -1,10 +1,9 @@
 use std::ffi::OsStr;
 use std::str;
 
+use rustc_abi::{ExternAbi, Size};
 use rustc_middle::ty::layout::LayoutOf;
 use rustc_span::Symbol;
-use rustc_target::abi::Size;
-use rustc_target::spec::abi::Abi;
 
 use self::shims::unix::android::foreign_items as android;
 use self::shims::unix::freebsd::foreign_items as freebsd;
@@ -43,7 +42,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_foreign_item_inner(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -54,47 +53,47 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         match link_name.as_str() {
             // Environment related shims
             "getenv" => {
-                let [name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [name] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.getenv(name)?;
                 this.write_pointer(result, dest)?;
             }
             "unsetenv" => {
-                let [name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [name] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.unsetenv(name)?;
                 this.write_scalar(result, dest)?;
             }
             "setenv" => {
-                let [name, value, overwrite] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [name, value, overwrite] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.read_scalar(overwrite)?.to_i32()?;
                 let result = this.setenv(name, value)?;
                 this.write_scalar(result, dest)?;
             }
             "getcwd" => {
-                let [buf, size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [buf, size] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.getcwd(buf, size)?;
                 this.write_pointer(result, dest)?;
             }
             "chdir" => {
-                let [path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [path] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.chdir(path)?;
                 this.write_scalar(result, dest)?;
             }
             "getpid" => {
-                let [] = this.check_shim(abi, Abi::C { unwind: false}, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::C { unwind: false}, link_name, args)?;
                 let result = this.getpid()?;
                 this.write_scalar(result, dest)?;
             }
 
             // File descriptors
             "read" => {
-                let [fd, buf, count] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [fd, buf, count] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let fd = this.read_scalar(fd)?.to_i32()?;
                 let buf = this.read_pointer(buf)?;
                 let count = this.read_target_usize(count)?;
                 this.read(fd, buf, count, None, dest)?;
             }
             "write" => {
-                let [fd, buf, n] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [fd, buf, n] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let fd = this.read_scalar(fd)?.to_i32()?;
                 let buf = this.read_pointer(buf)?;
                 let count = this.read_target_usize(n)?;
@@ -102,7 +101,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write(fd, buf, count, None, dest)?;
             }
             "pread" => {
-                let [fd, buf, count, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [fd, buf, count, offset] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let fd = this.read_scalar(fd)?.to_i32()?;
                 let buf = this.read_pointer(buf)?;
                 let count = this.read_target_usize(count)?;
@@ -110,7 +109,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.read(fd, buf, count, Some(offset), dest)?;
             }
             "pwrite" => {
-                let [fd, buf, n, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [fd, buf, n, offset] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let fd = this.read_scalar(fd)?.to_i32()?;
                 let buf = this.read_pointer(buf)?;
                 let count = this.read_target_usize(n)?;
@@ -119,7 +118,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write(fd, buf, count, Some(offset), dest)?;
             }
             "pread64" => {
-                let [fd, buf, count, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [fd, buf, count, offset] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let fd = this.read_scalar(fd)?.to_i32()?;
                 let buf = this.read_pointer(buf)?;
                 let count = this.read_target_usize(count)?;
@@ -127,7 +126,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.read(fd, buf, count, Some(offset), dest)?;
             }
             "pwrite64" => {
-                let [fd, buf, n, offset] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [fd, buf, n, offset] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let fd = this.read_scalar(fd)?.to_i32()?;
                 let buf = this.read_pointer(buf)?;
                 let count = this.read_target_usize(n)?;
@@ -136,32 +135,32 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write(fd, buf, count, Some(offset), dest)?;
             }
             "close" => {
-                let [fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [fd] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.close(fd)?;
                 this.write_scalar(result, dest)?;
             }
             "fcntl" => {
                 // `fcntl` is variadic. The argument count is checked based on the first argument
                 // in `this.fcntl()`, so we do not use `check_shim` here.
-                this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?;
+                this.check_abi_and_shim_symbol_clash(abi, ExternAbi::C { unwind: false }, link_name)?;
                 let result = this.fcntl(args)?;
                 this.write_scalar(result, dest)?;
             }
             "dup" => {
-                let [old_fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [old_fd] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let old_fd = this.read_scalar(old_fd)?.to_i32()?;
                 let new_fd = this.dup(old_fd)?;
                 this.write_scalar(new_fd, dest)?;
             }
             "dup2" => {
-                let [old_fd, new_fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [old_fd, new_fd] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let old_fd = this.read_scalar(old_fd)?.to_i32()?;
                 let new_fd = this.read_scalar(new_fd)?.to_i32()?;
                 let result = this.dup2(old_fd, new_fd)?;
                 this.write_scalar(result, dest)?;
             }
             "flock" => {
-                let [fd, op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [fd, op] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let fd = this.read_scalar(fd)?.to_i32()?;
                 let op = this.read_scalar(op)?.to_i32()?;
                 let result = this.flock(fd, op)?;
@@ -171,47 +170,47 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // File and file system access
             "open" | "open64" => {
                 // `open` is variadic, the third argument is only present when the second argument has O_CREAT (or on linux O_TMPFILE, but miri doesn't support that) set
-                this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?;
+                this.check_abi_and_shim_symbol_clash(abi, ExternAbi::C { unwind: false }, link_name)?;
                 let result = this.open(args)?;
                 this.write_scalar(result, dest)?;
             }
             "unlink" => {
-                let [path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [path] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.unlink(path)?;
                 this.write_scalar(result, dest)?;
             }
             "symlink" => {
-                let [target, linkpath] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [target, linkpath] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.symlink(target, linkpath)?;
                 this.write_scalar(result, dest)?;
             }
             "rename" => {
-                let [oldpath, newpath] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [oldpath, newpath] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.rename(oldpath, newpath)?;
                 this.write_scalar(result, dest)?;
             }
             "mkdir" => {
-                let [path, mode] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [path, mode] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.mkdir(path, mode)?;
                 this.write_scalar(result, dest)?;
             }
             "rmdir" => {
-                let [path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [path] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.rmdir(path)?;
                 this.write_scalar(result, dest)?;
             }
             "opendir" => {
-                let [name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [name] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.opendir(name)?;
                 this.write_scalar(result, dest)?;
             }
             "closedir" => {
-                let [dirp] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [dirp] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.closedir(dirp)?;
                 this.write_scalar(result, dest)?;
             }
             "lseek64" => {
-                let [fd, offset, whence] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [fd, offset, whence] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let fd = this.read_scalar(fd)?.to_i32()?;
                 let offset = this.read_scalar(offset)?.to_i64()?;
                 let whence = this.read_scalar(whence)?.to_i32()?;
@@ -219,7 +218,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_scalar(result, dest)?;
             }
             "lseek" => {
-                let [fd, offset, whence] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [fd, offset, whence] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let fd = this.read_scalar(fd)?.to_i32()?;
                 let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
                 let whence = this.read_scalar(whence)?.to_i32()?;
@@ -228,7 +227,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             "ftruncate64" => {
                 let [fd, length] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let fd = this.read_scalar(fd)?.to_i32()?;
                 let length = this.read_scalar(length)?.to_i64()?;
                 let result = this.ftruncate64(fd, length.into())?;
@@ -236,30 +235,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             "ftruncate" => {
                 let [fd, length] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let fd = this.read_scalar(fd)?.to_i32()?;
                 let length = this.read_scalar(length)?.to_int(this.libc_ty_layout("off_t").size)?;
                 let result = this.ftruncate64(fd, length)?;
                 this.write_scalar(result, dest)?;
             }
             "fsync" => {
-                let [fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [fd] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.fsync(fd)?;
                 this.write_scalar(result, dest)?;
             }
             "fdatasync" => {
-                let [fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [fd] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.fdatasync(fd)?;
                 this.write_scalar(result, dest)?;
             }
             "readlink" => {
-                let [pathname, buf, bufsize] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [pathname, buf, bufsize] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.readlink(pathname, buf, bufsize)?;
                 this.write_scalar(Scalar::from_target_isize(result, this), dest)?;
             }
             "posix_fadvise" => {
                 let [fd, offset, len, advice] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.read_scalar(fd)?.to_i32()?;
                 this.read_target_isize(offset)?;
                 this.read_target_isize(len)?;
@@ -268,12 +267,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_null(dest)?;
             }
             "realpath" => {
-                let [path, resolved_path] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [path, resolved_path] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.realpath(path, resolved_path)?;
                 this.write_scalar(result, dest)?;
             }
             "mkstemp" => {
-                let [template] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [template] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.mkstemp(template)?;
                 this.write_scalar(result, dest)?;
             }
@@ -281,13 +280,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Unnamed sockets and pipes
             "socketpair" => {
                 let [domain, type_, protocol, sv] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.socketpair(domain, type_, protocol, sv)?;
                 this.write_scalar(result, dest)?;
             }
             "pipe" => {
                 let [pipefd] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.pipe2(pipefd, /*flags*/ None)?;
                 this.write_scalar(result, dest)?;
             }
@@ -300,44 +299,44 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     );
                 }
                 let [pipefd, flags] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.pipe2(pipefd, Some(flags))?;
                 this.write_scalar(result, dest)?;
             }
 
             // Time
             "gettimeofday" => {
-                let [tv, tz] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [tv, tz] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.gettimeofday(tv, tz)?;
                 this.write_scalar(result, dest)?;
             }
             "localtime_r" => {
-                let [timep, result_op] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?;
+                let [timep, result_op] = this.check_shim(abi, ExternAbi::C {unwind: false}, link_name, args)?;
                 let result = this.localtime_r(timep, result_op)?;
                 this.write_pointer(result, dest)?;
             }
             "clock_gettime" => {
                 let [clk_id, tp] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.clock_gettime(clk_id, tp)?;
                 this.write_scalar(result, dest)?;
             }
 
             // Allocation
             "posix_memalign" => {
-                let [memptr, align, size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [memptr, align, size] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.posix_memalign(memptr, align, size)?;
                 this.write_scalar(result, dest)?;
             }
 
             "mmap" => {
-                let [addr, length, prot, flags, fd, offset] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?;
+                let [addr, length, prot, flags, fd, offset] = this.check_shim(abi, ExternAbi::C {unwind: false}, link_name, args)?;
                 let offset = this.read_scalar(offset)?.to_int(this.libc_ty_layout("off_t").size)?;
                 let ptr = this.mmap(addr, length, prot, flags, fd, offset)?;
                 this.write_scalar(ptr, dest)?;
             }
             "munmap" => {
-                let [addr, length] = this.check_shim(abi, Abi::C {unwind: false}, link_name, args)?;
+                let [addr, length] = this.check_shim(abi, ExternAbi::C {unwind: false}, link_name, args)?;
                 let result = this.munmap(addr, length)?;
                 this.write_scalar(result, dest)?;
             }
@@ -351,7 +350,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     );
                 }
                 let [ptr, nmemb, size] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
                 let nmemb = this.read_target_usize(nmemb)?;
                 let size = this.read_target_usize(size)?;
@@ -375,14 +374,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // This is a C11 function, we assume all Unixes have it.
                 // (MSVC explicitly does not support this.)
                 let [align, size] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let res = this.aligned_alloc(align, size)?;
                 this.write_pointer(res, dest)?;
             }
 
             // Dynamic symbol loading
             "dlsym" => {
-                let [handle, symbol] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [handle, symbol] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.read_target_usize(handle)?;
                 let symbol = this.read_pointer(symbol)?;
                 let name = this.read_c_str(symbol)?;
@@ -396,7 +395,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
             // Querying system information
             "sysconf" => {
-                let [name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [name] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let name = this.read_scalar(name)?.to_i32()?;
                 // FIXME: Which of these are POSIX, and which are GNU/Linux?
                 // At least the names seem to all also exist on macOS.
@@ -425,7 +424,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
             // Thread-local storage
             "pthread_key_create" => {
-                let [key, dtor] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [key, dtor] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let key_place = this.deref_pointer_as(key, this.libc_ty_layout("pthread_key_t"))?;
                 let dtor = this.read_pointer(dtor)?;
 
@@ -453,21 +452,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_null(dest)?;
             }
             "pthread_key_delete" => {
-                let [key] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [key] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let key = this.read_scalar(key)?.to_bits(key.layout.size)?;
                 this.machine.tls.delete_tls_key(key)?;
                 // Return success (0)
                 this.write_null(dest)?;
             }
             "pthread_getspecific" => {
-                let [key] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [key] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let key = this.read_scalar(key)?.to_bits(key.layout.size)?;
                 let active_thread = this.active_thread();
                 let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
                 this.write_scalar(ptr, dest)?;
             }
             "pthread_setspecific" => {
-                let [key, new_ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [key, new_ptr] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let key = this.read_scalar(key)?.to_bits(key.layout.size)?;
                 let active_thread = this.active_thread();
                 let new_data = this.read_scalar(new_ptr)?;
@@ -479,151 +478,151 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
             // Synchronization primitives
             "pthread_mutexattr_init" => {
-                let [attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [attr] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.pthread_mutexattr_init(attr)?;
                 this.write_null(dest)?;
             }
             "pthread_mutexattr_settype" => {
-                let [attr, kind] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [attr, kind] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.pthread_mutexattr_settype(attr, kind)?;
                 this.write_scalar(result, dest)?;
             }
             "pthread_mutexattr_destroy" => {
-                let [attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [attr] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.pthread_mutexattr_destroy(attr)?;
                 this.write_null(dest)?;
             }
             "pthread_mutex_init" => {
-                let [mutex, attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [mutex, attr] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.pthread_mutex_init(mutex, attr)?;
                 this.write_null(dest)?;
             }
             "pthread_mutex_lock" => {
-                let [mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [mutex] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.pthread_mutex_lock(mutex, dest)?;
             }
             "pthread_mutex_trylock" => {
-                let [mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [mutex] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.pthread_mutex_trylock(mutex)?;
                 this.write_scalar(result, dest)?;
             }
             "pthread_mutex_unlock" => {
-                let [mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [mutex] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.pthread_mutex_unlock(mutex)?;
                 this.write_scalar(result, dest)?;
             }
             "pthread_mutex_destroy" => {
-                let [mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [mutex] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.pthread_mutex_destroy(mutex)?;
                 this.write_int(0, dest)?;
             }
             "pthread_rwlock_rdlock" => {
-                let [rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [rwlock] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.pthread_rwlock_rdlock(rwlock, dest)?;
             }
             "pthread_rwlock_tryrdlock" => {
-                let [rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [rwlock] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.pthread_rwlock_tryrdlock(rwlock)?;
                 this.write_scalar(result, dest)?;
             }
             "pthread_rwlock_wrlock" => {
-                let [rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [rwlock] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.pthread_rwlock_wrlock(rwlock, dest)?;
             }
             "pthread_rwlock_trywrlock" => {
-                let [rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [rwlock] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.pthread_rwlock_trywrlock(rwlock)?;
                 this.write_scalar(result, dest)?;
             }
             "pthread_rwlock_unlock" => {
-                let [rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [rwlock] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.pthread_rwlock_unlock(rwlock)?;
                 this.write_null(dest)?;
             }
             "pthread_rwlock_destroy" => {
-                let [rwlock] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [rwlock] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.pthread_rwlock_destroy(rwlock)?;
                 this.write_null(dest)?;
             }
             "pthread_condattr_init" => {
-                let [attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [attr] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.pthread_condattr_init(attr)?;
                 this.write_null(dest)?;
             }
             "pthread_condattr_setclock" => {
                 let [attr, clock_id] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.pthread_condattr_setclock(attr, clock_id)?;
                 this.write_scalar(result, dest)?;
             }
             "pthread_condattr_getclock" => {
                 let [attr, clock_id] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.pthread_condattr_getclock(attr, clock_id)?;
                 this.write_null(dest)?;
             }
             "pthread_condattr_destroy" => {
-                let [attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [attr] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.pthread_condattr_destroy(attr)?;
                 this.write_null(dest)?;
             }
             "pthread_cond_init" => {
-                let [cond, attr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [cond, attr] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.pthread_cond_init(cond, attr)?;
                 this.write_null(dest)?;
             }
             "pthread_cond_signal" => {
-                let [cond] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [cond] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.pthread_cond_signal(cond)?;
                 this.write_null(dest)?;
             }
             "pthread_cond_broadcast" => {
-                let [cond] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [cond] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.pthread_cond_broadcast(cond)?;
                 this.write_null(dest)?;
             }
             "pthread_cond_wait" => {
-                let [cond, mutex] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [cond, mutex] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.pthread_cond_wait(cond, mutex, dest)?;
             }
             "pthread_cond_timedwait" => {
-                let [cond, mutex, abstime] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [cond, mutex, abstime] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.pthread_cond_timedwait(cond, mutex, abstime, dest)?;
             }
             "pthread_cond_destroy" => {
-                let [cond] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [cond] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.pthread_cond_destroy(cond)?;
                 this.write_null(dest)?;
             }
 
             // Threading
             "pthread_create" => {
-                let [thread, attr, start, arg] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [thread, attr, start, arg] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.pthread_create(thread, attr, start, arg)?;
                 this.write_null(dest)?;
             }
             "pthread_join" => {
-                let [thread, retval] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
-                this.pthread_join(thread, retval)?;
-                this.write_null(dest)?;
+                let [thread, retval] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
+                let res = this.pthread_join(thread, retval)?;
+                this.write_scalar(res, dest)?;
             }
             "pthread_detach" => {
-                let [thread] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
-                this.pthread_detach(thread)?;
-                this.write_null(dest)?;
+                let [thread] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
+                let res = this.pthread_detach(thread)?;
+                this.write_scalar(res, dest)?;
             }
             "pthread_self" => {
-                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let res = this.pthread_self()?;
                 this.write_scalar(res, dest)?;
             }
             "sched_yield" => {
-                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.sched_yield()?;
                 this.write_null(dest)?;
             }
             "nanosleep" => {
-                let [req, rem] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [req, rem] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.nanosleep(req, rem)?;
                 this.write_scalar(result, dest)?;
             }
@@ -637,7 +636,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 }
 
                 let [pid, cpusetsize, mask] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::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)?;
@@ -677,7 +676,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 }
 
                 let [pid, cpusetsize, mask] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::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)?;
@@ -713,12 +712,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
             // Miscellaneous
             "isatty" => {
-                let [fd] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [fd] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.isatty(fd)?;
                 this.write_scalar(result, dest)?;
             }
             "pthread_atfork" => {
-                let [prepare, parent, child] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [prepare, parent, child] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.read_pointer(prepare)?;
                 this.read_pointer(parent)?;
                 this.read_pointer(child)?;
@@ -726,7 +725,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_null(dest)?;
             }
             "strerror_r" | "__xpg_strerror_r" => {
-                let [errnum, buf, buflen] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [errnum, buf, buflen] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let errnum = this.read_scalar(errnum)?;
                 let buf = this.read_pointer(buf)?;
                 let buflen = this.read_target_usize(buflen)?;
@@ -751,7 +750,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 }
 
                 let [buf, bufsize] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let buf = this.read_pointer(buf)?;
                 let bufsize = this.read_target_usize(bufsize)?;
 
@@ -777,7 +776,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     );
                 }
                 let [ptr, len, flags] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
                 let len = this.read_target_usize(len)?;
                 let _flags = this.read_scalar(flags)?.to_i32()?;
@@ -794,7 +793,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                         this.tcx.sess.target.os
                     );
                 }
-                let [ptr, len] = this.check_shim(abi, Abi::C { unwind: false}, link_name, args)?;
+                let [ptr, len] = this.check_shim(abi, ExternAbi::C { unwind: false}, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
                 let len = this.read_target_usize(len)?;
                 this.gen_random(ptr, len)?;
@@ -820,12 +819,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     );
                 }
                 // This function looks and behaves excatly like miri_start_unwind.
-                let [payload] = this.check_shim(abi, Abi::C { unwind: true }, link_name, args)?;
+                let [payload] = this.check_shim(abi, ExternAbi::C { unwind: true }, link_name, args)?;
                 this.handle_miri_start_unwind(payload)?;
                 return interp_ok(EmulateItemResult::NeedsUnwind);
             }
             "getuid" => {
-                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 // For now, just pretend we always have this fixed UID.
                 this.write_int(UID, dest)?;
             }
@@ -834,7 +833,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // These shims are enabled only when the caller is in the standard library.
             "pthread_attr_getguardsize"
             if this.frame_in_std() => {
-                let [_attr, guard_size] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [_attr, guard_size] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let guard_size = this.deref_pointer(guard_size)?;
                 let guard_size_layout = this.libc_ty_layout("size_t");
                 this.write_scalar(Scalar::from_uint(this.machine.page_size, guard_size_layout.size), &guard_size)?;
@@ -846,12 +845,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             | "pthread_attr_init"
             | "pthread_attr_destroy"
             if this.frame_in_std() => {
-                let [_] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [_] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.write_null(dest)?;
             }
             | "pthread_attr_setstacksize"
             if this.frame_in_std() => {
-                let [_, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [_, _] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.write_null(dest)?;
             }
 
@@ -860,7 +859,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // We don't support "pthread_attr_setstack", so we just pretend all stacks have the same values here.
                 // Hence we can mostly ignore the input `attr_place`.
                 let [attr_place, addr_place, size_place] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let _attr_place = this.deref_pointer_as(attr_place, this.libc_ty_layout("pthread_attr_t"))?;
                 let addr_place = this.deref_pointer(addr_place)?;
                 let size_place = this.deref_pointer(size_place)?;
@@ -881,13 +880,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             | "signal"
             | "sigaltstack"
             if this.frame_in_std() => {
-                let [_, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [_, _] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.write_null(dest)?;
             }
             | "sigaction"
             | "mprotect"
             if this.frame_in_std() => {
-                let [_, _, _] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [_, _, _] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.write_null(dest)?;
             }
 
@@ -895,7 +894,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             if this.frame_in_std() => {
                 // getpwuid_r is the standard name, __posix_getpwuid_r is used on solarish
                 let [uid, pwd, buf, buflen, result] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.check_no_isolation("`getpwuid_r`")?;
 
                 let uid = this.read_scalar(uid)?.to_u32()?;
diff --git a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
index 71953aca989..1346d8de7ea 100644
--- a/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/freebsd/foreign_items.rs
@@ -1,5 +1,5 @@
+use rustc_abi::ExternAbi;
 use rustc_span::Symbol;
-use rustc_target::spec::abi::Abi;
 
 use crate::shims::unix::*;
 use crate::*;
@@ -13,7 +13,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_foreign_item_inner(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -22,7 +22,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Threading
             "pthread_set_name_np" => {
                 let [thread, name] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let max_len = usize::MAX; // FreeBSD does not seem to have a limit.
                 // FreeBSD's pthread_set_name_np does not return anything.
                 this.pthread_setname_np(
@@ -34,7 +34,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             "pthread_get_name_np" => {
                 let [thread, name, len] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 // FreeBSD's pthread_get_name_np does not return anything
                 // and uses strlcpy, which truncates the resulting value,
                 // but always adds a null terminator (except for zero-sized buffers).
@@ -52,31 +52,32 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // since freebsd 12 the former form can be expected.
             "stat" | "stat@FBSD_1.0" => {
                 let [path, buf] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.macos_fbsd_stat(path, buf)?;
                 this.write_scalar(result, dest)?;
             }
             "lstat" | "lstat@FBSD_1.0" => {
                 let [path, buf] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.macos_fbsd_lstat(path, buf)?;
                 this.write_scalar(result, dest)?;
             }
             "fstat" | "fstat@FBSD_1.0" => {
-                let [fd, buf] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [fd, buf] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.macos_fbsd_fstat(fd, buf)?;
                 this.write_scalar(result, dest)?;
             }
             "readdir_r" | "readdir_r@FBSD_1.0" => {
                 let [dirp, entry, result] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
                 this.write_scalar(result, dest)?;
             }
 
             // Miscellaneous
             "__error" => {
-                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let errno_place = this.last_error_place()?;
                 this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
             }
@@ -85,7 +86,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // These shims are enabled only when the caller is in the standard library.
             "pthread_attr_get_np" if this.frame_in_std() => {
                 let [_thread, _attr] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.write_null(dest)?;
             }
 
diff --git a/src/tools/miri/src/shims/unix/fs.rs b/src/tools/miri/src/shims/unix/fs.rs
index f7436d7f089..091def7ac65 100644
--- a/src/tools/miri/src/shims/unix/fs.rs
+++ b/src/tools/miri/src/shims/unix/fs.rs
@@ -8,8 +8,8 @@ use std::io::{self, ErrorKind, IsTerminal, Read, Seek, SeekFrom, Write};
 use std::path::{Path, PathBuf};
 use std::time::SystemTime;
 
+use rustc_abi::Size;
 use rustc_data_structures::fx::FxHashMap;
-use rustc_target::abi::Size;
 
 use self::fd::FlockOp;
 use self::shims::time::system_time_to_duration;
@@ -385,7 +385,7 @@ pub struct DirTable {
 }
 
 impl DirTable {
-    #[allow(clippy::arithmetic_side_effects)]
+    #[expect(clippy::arithmetic_side_effects)]
     fn insert_new(&mut self, read_dir: ReadDir) -> u64 {
         let id = self.next_id;
         self.next_id += 1;
diff --git a/src/tools/miri/src/shims/unix/linux/eventfd.rs b/src/tools/miri/src/shims/unix/linux/eventfd.rs
index 35bc933885c..63b7d37b13e 100644
--- a/src/tools/miri/src/shims/unix/linux/eventfd.rs
+++ b/src/tools/miri/src/shims/unix/linux/eventfd.rs
@@ -4,7 +4,7 @@ use std::io;
 use std::io::ErrorKind;
 
 use crate::concurrency::VClock;
-use crate::shims::unix::fd::FileDescriptionRef;
+use crate::shims::unix::fd::{FileDescriptionRef, WeakFileDescriptionRef};
 use crate::shims::unix::linux::epoll::{EpollReadyEvents, EvalContextExt as _};
 use crate::shims::unix::*;
 use crate::*;
@@ -26,6 +26,10 @@ struct Event {
     counter: Cell<u64>,
     is_nonblock: bool,
     clock: RefCell<VClock>,
+    /// A list of thread ids blocked on eventfd::read.
+    blocked_read_tid: RefCell<Vec<ThreadId>>,
+    /// A list of thread ids blocked on eventfd::write.
+    blocked_write_tid: RefCell<Vec<ThreadId>>,
 }
 
 impl FileDescription for Event {
@@ -72,31 +76,8 @@ impl FileDescription for Event {
         // eventfd read at the size of u64.
         let buf_place = ecx.ptr_to_mplace_unaligned(ptr, ty);
 
-        // Block when counter == 0.
-        let counter = self.counter.get();
-        if counter == 0 {
-            if self.is_nonblock {
-                return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
-            }
-
-            throw_unsup_format!("eventfd: blocking is unsupported");
-        } else {
-            // Synchronize with all prior `write` calls to this FD.
-            ecx.acquire_clock(&self.clock.borrow());
-
-            // Give old counter value to userspace, and set counter value to 0.
-            ecx.write_int(counter, &buf_place)?;
-            self.counter.set(0);
-
-            // When any of the event happened, we check and update the status of all supported event
-            // types for current file description.
-            ecx.check_and_update_readiness(self_ref)?;
-
-            // Tell userspace how many bytes we wrote.
-            ecx.write_int(buf_place.layout.size.bytes(), dest)?;
-        }
-
-        interp_ok(())
+        let weak_eventfd = self_ref.downgrade();
+        eventfd_read(buf_place, dest, weak_eventfd, ecx)
     }
 
     /// A write call adds the 8-byte integer value supplied in
@@ -127,7 +108,7 @@ impl FileDescription for Event {
             return ecx.set_last_error_and_return(ErrorKind::InvalidInput, dest);
         }
 
-        // Read the user supplied value from the pointer.
+        // Read the user-supplied value from the pointer.
         let buf_place = ecx.ptr_to_mplace_unaligned(ptr, ty);
         let num = ecx.read_scalar(&buf_place)?.to_u64()?;
 
@@ -137,27 +118,8 @@ impl FileDescription for Event {
         }
         // If the addition does not let the counter to exceed the maximum value, update the counter.
         // Else, block.
-        match self.counter.get().checked_add(num) {
-            Some(new_count @ 0..=MAX_COUNTER) => {
-                // Future `read` calls will synchronize with this write, so update the FD clock.
-                ecx.release_clock(|clock| {
-                    self.clock.borrow_mut().join(clock);
-                });
-                self.counter.set(new_count);
-            }
-            None | Some(u64::MAX) =>
-                if self.is_nonblock {
-                    return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
-                } else {
-                    throw_unsup_format!("eventfd: blocking is unsupported");
-                },
-        };
-        // When any of the event happened, we check and update the status of all supported event
-        // types for current file description.
-        ecx.check_and_update_readiness(self_ref)?;
-
-        // Return how many bytes we read.
-        ecx.write_int(buf_place.layout.size.bytes(), dest)
+        let weak_eventfd = self_ref.downgrade();
+        eventfd_write(num, buf_place, dest, weak_eventfd, ecx)
     }
 }
 
@@ -217,8 +179,151 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             counter: Cell::new(val.into()),
             is_nonblock,
             clock: RefCell::new(VClock::default()),
+            blocked_read_tid: RefCell::new(Vec::new()),
+            blocked_write_tid: RefCell::new(Vec::new()),
         });
 
         interp_ok(Scalar::from_i32(fd_value))
     }
 }
+
+/// Block thread if the value addition will exceed u64::MAX -1,
+/// else just add the user-supplied value to current counter.
+fn eventfd_write<'tcx>(
+    num: u64,
+    buf_place: MPlaceTy<'tcx>,
+    dest: &MPlaceTy<'tcx>,
+    weak_eventfd: WeakFileDescriptionRef,
+    ecx: &mut MiriInterpCx<'tcx>,
+) -> InterpResult<'tcx> {
+    let Some(eventfd_ref) = weak_eventfd.upgrade() else {
+        throw_unsup_format!("eventfd FD got closed while blocking.")
+    };
+
+    // Since we pass the weak file description ref, it is guaranteed to be
+    // an eventfd file description.
+    let eventfd = eventfd_ref.downcast::<Event>().unwrap();
+
+    match eventfd.counter.get().checked_add(num) {
+        Some(new_count @ 0..=MAX_COUNTER) => {
+            // Future `read` calls will synchronize with this write, so update the FD clock.
+            ecx.release_clock(|clock| {
+                eventfd.clock.borrow_mut().join(clock);
+            });
+
+            // When this function is called, the addition is guaranteed to not exceed u64::MAX - 1.
+            eventfd.counter.set(new_count);
+
+            // When any of the event happened, we check and update the status of all supported event
+            // types for current file description.
+            ecx.check_and_update_readiness(&eventfd_ref)?;
+
+            // Unblock *all* threads previously blocked on `read`.
+            // We need to take out the blocked thread ids and unblock them together,
+            // because `unblock_threads` may block them again and end up re-adding the
+            // thread to the blocked list.
+            let waiting_threads = std::mem::take(&mut *eventfd.blocked_read_tid.borrow_mut());
+            // FIXME: We can randomize the order of unblocking.
+            for thread_id in waiting_threads {
+                ecx.unblock_thread(thread_id, BlockReason::Eventfd)?;
+            }
+
+            // Return how many bytes we wrote.
+            return ecx.write_int(buf_place.layout.size.bytes(), dest);
+        }
+        None | Some(u64::MAX) => {
+            if eventfd.is_nonblock {
+                return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
+            }
+
+            let dest = dest.clone();
+
+            eventfd.blocked_write_tid.borrow_mut().push(ecx.active_thread());
+
+            ecx.block_thread(
+                BlockReason::Eventfd,
+                None,
+                callback!(
+                    @capture<'tcx> {
+                        num: u64,
+                        buf_place: MPlaceTy<'tcx>,
+                        dest: MPlaceTy<'tcx>,
+                        weak_eventfd: WeakFileDescriptionRef,
+                    }
+                    @unblock = |this| {
+                        eventfd_write(num, buf_place, &dest, weak_eventfd, this)
+                    }
+                ),
+            );
+        }
+    };
+    interp_ok(())
+}
+
+/// Block thread if the current counter is 0,
+/// else just return the current counter value to the caller and set the counter to 0.
+fn eventfd_read<'tcx>(
+    buf_place: MPlaceTy<'tcx>,
+    dest: &MPlaceTy<'tcx>,
+    weak_eventfd: WeakFileDescriptionRef,
+    ecx: &mut MiriInterpCx<'tcx>,
+) -> InterpResult<'tcx> {
+    let Some(eventfd_ref) = weak_eventfd.upgrade() else {
+        throw_unsup_format!("eventfd FD got closed while blocking.")
+    };
+
+    // Since we pass the weak file description ref to the callback function, it is guaranteed to be
+    // an eventfd file description.
+    let eventfd = eventfd_ref.downcast::<Event>().unwrap();
+
+    // Block when counter == 0.
+    let counter = eventfd.counter.replace(0);
+
+    if counter == 0 {
+        if eventfd.is_nonblock {
+            return ecx.set_last_error_and_return(ErrorKind::WouldBlock, dest);
+        }
+        let dest = dest.clone();
+
+        eventfd.blocked_read_tid.borrow_mut().push(ecx.active_thread());
+
+        ecx.block_thread(
+            BlockReason::Eventfd,
+            None,
+            callback!(
+                @capture<'tcx> {
+                    buf_place: MPlaceTy<'tcx>,
+                    dest: MPlaceTy<'tcx>,
+                    weak_eventfd: WeakFileDescriptionRef,
+                }
+                @unblock = |this| {
+                    eventfd_read(buf_place, &dest, weak_eventfd, this)
+                }
+            ),
+        );
+    } else {
+        // Synchronize with all prior `write` calls to this FD.
+        ecx.acquire_clock(&eventfd.clock.borrow());
+
+        // Give old counter value to userspace, and set counter value to 0.
+        ecx.write_int(counter, &buf_place)?;
+
+        // When any of the events happened, we check and update the status of all supported event
+        // types for current file description.
+        ecx.check_and_update_readiness(&eventfd_ref)?;
+
+        // Unblock *all* threads previously blocked on `write`.
+        // We need to take out the blocked thread ids and unblock them together,
+        // because `unblock_threads` may block them again and end up re-adding the
+        // thread to the blocked list.
+        let waiting_threads = std::mem::take(&mut *eventfd.blocked_write_tid.borrow_mut());
+        // FIXME: We can randomize the order of unblocking.
+        for thread_id in waiting_threads {
+            ecx.unblock_thread(thread_id, BlockReason::Eventfd)?;
+        }
+
+        // Tell userspace how many bytes we read.
+        return ecx.write_int(buf_place.layout.size.bytes(), dest);
+    }
+    interp_ok(())
+}
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 6616a9845c0..85f0d6e1330 100644
--- a/src/tools/miri/src/shims/unix/linux/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/linux/foreign_items.rs
@@ -1,11 +1,10 @@
+use rustc_abi::ExternAbi;
 use rustc_span::Symbol;
-use rustc_target::spec::abi::Abi;
 
 use self::shims::unix::linux::epoll::EvalContextExt as _;
 use self::shims::unix::linux::eventfd::EvalContextExt as _;
 use self::shims::unix::linux::mem::EvalContextExt as _;
-use self::shims::unix::linux::sync::futex;
-use crate::helpers::check_min_arg_count;
+use self::shims::unix::linux::syscall::syscall;
 use crate::machine::{SIGRTMAX, SIGRTMIN};
 use crate::shims::unix::*;
 use crate::*;
@@ -24,7 +23,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_foreign_item_inner(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -35,43 +34,45 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         match link_name.as_str() {
             // File related shims
             "readdir64" => {
-                let [dirp] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [dirp] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.linux_readdir64(dirp)?;
                 this.write_scalar(result, dest)?;
             }
             "sync_file_range" => {
                 let [fd, offset, nbytes, flags] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.sync_file_range(fd, offset, nbytes, flags)?;
                 this.write_scalar(result, dest)?;
             }
             "statx" => {
                 let [dirfd, pathname, flags, mask, statxbuf] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?;
                 this.write_scalar(result, dest)?;
             }
 
             // epoll, eventfd
             "epoll_create1" => {
-                let [flag] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [flag] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.epoll_create1(flag)?;
                 this.write_scalar(result, dest)?;
             }
             "epoll_ctl" => {
                 let [epfd, op, fd, event] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.epoll_ctl(epfd, op, fd, event)?;
                 this.write_scalar(result, dest)?;
             }
             "epoll_wait" => {
                 let [epfd, events, maxevents, timeout] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.epoll_wait(epfd, events, maxevents, timeout, dest)?;
             }
             "eventfd" => {
                 let [val, flag] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.eventfd(val, flag)?;
                 this.write_scalar(result, dest)?;
             }
@@ -79,119 +80,81 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Threading
             "pthread_setname_np" => {
                 let [thread, name] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
-                let res = this.pthread_setname_np(
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
+                let res = match this.pthread_setname_np(
                     this.read_scalar(thread)?,
                     this.read_scalar(name)?,
                     TASK_COMM_LEN,
                     /* truncate */ false,
-                )?;
-                let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
+                )? {
+                    ThreadNameResult::Ok => Scalar::from_u32(0),
+                    ThreadNameResult::NameTooLong => this.eval_libc("ERANGE"),
+                    // Act like we faild to open `/proc/self/task/$tid/comm`.
+                    ThreadNameResult::ThreadNotFound => this.eval_libc("ENOENT"),
+                };
                 this.write_scalar(res, dest)?;
             }
             "pthread_getname_np" => {
                 let [thread, name, len] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 // The function's behavior isn't portable between platforms.
                 // In case of glibc, the length of the output buffer must
                 // be not shorter than TASK_COMM_LEN.
                 let len = this.read_scalar(len)?;
-                let res = if len.to_target_usize(this)? >= TASK_COMM_LEN as u64
-                    && this.pthread_getname_np(
+                let res = if len.to_target_usize(this)? >= TASK_COMM_LEN as u64 {
+                    match this.pthread_getname_np(
                         this.read_scalar(thread)?,
                         this.read_scalar(name)?,
                         len,
                         /* truncate*/ false,
                     )? {
-                    Scalar::from_u32(0)
+                        ThreadNameResult::Ok => Scalar::from_u32(0),
+                        ThreadNameResult::NameTooLong => unreachable!(),
+                        // Act like we faild to open `/proc/self/task/$tid/comm`.
+                        ThreadNameResult::ThreadNotFound => this.eval_libc("ENOENT"),
+                    }
                 } else {
                     this.eval_libc("ERANGE")
                 };
                 this.write_scalar(res, dest)?;
             }
             "gettid" => {
-                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.linux_gettid()?;
                 this.write_scalar(result, dest)?;
             }
 
             // Dynamically invoked syscalls
             "syscall" => {
-                // We do not use `check_shim` here because `syscall` is variadic. The argument
-                // count is checked bellow.
-                this.check_abi_and_shim_symbol_clash(abi, Abi::C { unwind: false }, link_name)?;
-                // The syscall variadic function is legal to call with more arguments than needed,
-                // extra arguments are simply ignored. The important check is that when we use an
-                // argument, we have to also check all arguments *before* it to ensure that they
-                // have the right type.
-
-                let sys_getrandom = this.eval_libc("SYS_getrandom").to_target_usize(this)?;
-                let sys_futex = this.eval_libc("SYS_futex").to_target_usize(this)?;
-                let sys_eventfd2 = this.eval_libc("SYS_eventfd2").to_target_usize(this)?;
-
-                let [op] = check_min_arg_count("syscall", args)?;
-                match this.read_target_usize(op)? {
-                    // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
-                    // is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
-                    num if num == sys_getrandom => {
-                        // Used by getrandom 0.1
-                        // The first argument is the syscall id, so skip over it.
-                        let [_, ptr, len, flags] =
-                            check_min_arg_count("syscall(SYS_getrandom, ...)", args)?;
-
-                        let ptr = this.read_pointer(ptr)?;
-                        let len = this.read_target_usize(len)?;
-                        // The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
-                        // neither of which have any effect on our current PRNG.
-                        // See <https://github.com/rust-lang/rust/pull/79196> for a discussion of argument sizes.
-                        let _flags = this.read_scalar(flags)?.to_i32()?;
-
-                        this.gen_random(ptr, len)?;
-                        this.write_scalar(Scalar::from_target_usize(len, this), dest)?;
-                    }
-                    // `futex` is used by some synchronization primitives.
-                    num if num == sys_futex => {
-                        futex(this, args, dest)?;
-                    }
-                    num if num == sys_eventfd2 => {
-                        let [_, initval, flags] =
-                            check_min_arg_count("syscall(SYS_evetfd2, ...)", args)?;
-
-                        let result = this.eventfd(initval, flags)?;
-                        this.write_int(result.to_i32()?, dest)?;
-                    }
-                    num => {
-                        throw_unsup_format!("syscall: unsupported syscall number {num}");
-                    }
-                }
+                syscall(this, link_name, abi, args, dest)?;
             }
 
             // Miscellaneous
             "mmap64" => {
                 let [addr, length, prot, flags, fd, offset] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let offset = this.read_scalar(offset)?.to_i64()?;
                 let ptr = this.mmap(addr, length, prot, flags, fd, offset.into())?;
                 this.write_scalar(ptr, dest)?;
             }
             "mremap" => {
                 let [old_address, old_size, new_size, flags] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let ptr = this.mremap(old_address, old_size, new_size, flags)?;
                 this.write_scalar(ptr, dest)?;
             }
             "__errno_location" => {
-                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let errno_place = this.last_error_place()?;
                 this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
             }
             "__libc_current_sigrtmin" => {
-                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 this.write_int(SIGRTMIN, dest)?;
             }
             "__libc_current_sigrtmax" => {
-                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 this.write_int(SIGRTMAX, dest)?;
             }
@@ -200,7 +163,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // These shims are enabled only when the caller is in the standard library.
             "pthread_getattr_np" if this.frame_in_std() => {
                 let [_thread, _attr] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.write_null(dest)?;
             }
 
diff --git a/src/tools/miri/src/shims/unix/linux/mem.rs b/src/tools/miri/src/shims/unix/linux/mem.rs
index d5f9669b360..8e796d5dce5 100644
--- a/src/tools/miri/src/shims/unix/linux/mem.rs
+++ b/src/tools/miri/src/shims/unix/linux/mem.rs
@@ -1,7 +1,7 @@
 //! This follows the pattern in src/shims/unix/mem.rs: We only support uses of mremap that would
 //! correspond to valid uses of realloc.
 
-use rustc_target::abi::Size;
+use rustc_abi::Size;
 
 use crate::*;
 
@@ -22,7 +22,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let flags = this.read_scalar(flags)?.to_i32()?;
 
         // old_address must be a multiple of the page size
-        #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
+        #[expect(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
         if old_address.addr().bytes() % this.machine.page_size != 0 || new_size == 0 {
             this.set_last_error(LibcError("EINVAL"))?;
             return interp_ok(this.eval_libc("MAP_FAILED"));
diff --git a/src/tools/miri/src/shims/unix/linux/mod.rs b/src/tools/miri/src/shims/unix/linux/mod.rs
index 84b604eb9b8..159e5aca031 100644
--- a/src/tools/miri/src/shims/unix/linux/mod.rs
+++ b/src/tools/miri/src/shims/unix/linux/mod.rs
@@ -3,3 +3,4 @@ pub mod eventfd;
 pub mod foreign_items;
 pub mod mem;
 pub mod sync;
+pub mod syscall;
diff --git a/src/tools/miri/src/shims/unix/linux/sync.rs b/src/tools/miri/src/shims/unix/linux/sync.rs
index c258be78f76..51124fb2a00 100644
--- a/src/tools/miri/src/shims/unix/linux/sync.rs
+++ b/src/tools/miri/src/shims/unix/linux/sync.rs
@@ -1,10 +1,15 @@
+use crate::concurrency::sync::FutexRef;
 use crate::helpers::check_min_arg_count;
 use crate::*;
 
+struct LinuxFutex {
+    futex: FutexRef,
+}
+
 /// Implementation of the SYS_futex syscall.
 /// `args` is the arguments *including* the syscall number.
 pub fn futex<'tcx>(
-    this: &mut MiriInterpCx<'tcx>,
+    ecx: &mut MiriInterpCx<'tcx>,
     args: &[OpTy<'tcx>],
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx> {
@@ -21,20 +26,19 @@ pub fn futex<'tcx>(
     // The first three arguments (after the syscall number itself) are the same to all futex operations:
     //     (int *addr, int op, int val).
     // We checked above that these definitely exist.
-    let addr = this.read_pointer(addr)?;
-    let op = this.read_scalar(op)?.to_i32()?;
-    let val = this.read_scalar(val)?.to_i32()?;
+    let addr = ecx.read_pointer(addr)?;
+    let op = ecx.read_scalar(op)?.to_i32()?;
+    let val = ecx.read_scalar(val)?.to_i32()?;
 
     // This is a vararg function so we have to bring our own type for this pointer.
-    let addr = this.ptr_to_mplace(addr, this.machine.layouts.i32);
-    let addr_usize = addr.ptr().addr().bytes();
+    let addr = ecx.ptr_to_mplace(addr, ecx.machine.layouts.i32);
 
-    let futex_private = this.eval_libc_i32("FUTEX_PRIVATE_FLAG");
-    let futex_wait = this.eval_libc_i32("FUTEX_WAIT");
-    let futex_wait_bitset = this.eval_libc_i32("FUTEX_WAIT_BITSET");
-    let futex_wake = this.eval_libc_i32("FUTEX_WAKE");
-    let futex_wake_bitset = this.eval_libc_i32("FUTEX_WAKE_BITSET");
-    let futex_realtime = this.eval_libc_i32("FUTEX_CLOCK_REALTIME");
+    let futex_private = ecx.eval_libc_i32("FUTEX_PRIVATE_FLAG");
+    let futex_wait = ecx.eval_libc_i32("FUTEX_WAIT");
+    let futex_wait_bitset = ecx.eval_libc_i32("FUTEX_WAIT_BITSET");
+    let futex_wake = ecx.eval_libc_i32("FUTEX_WAKE");
+    let futex_wake_bitset = ecx.eval_libc_i32("FUTEX_WAKE_BITSET");
+    let futex_realtime = ecx.eval_libc_i32("FUTEX_CLOCK_REALTIME");
 
     // FUTEX_PRIVATE enables an optimization that stops it from working across processes.
     // Miri doesn't support that anyway, so we ignore that flag.
@@ -53,9 +57,9 @@ pub fn futex<'tcx>(
             let (timeout, bitset) = if wait_bitset {
                 let [_, _, _, _, timeout, uaddr2, bitset] =
                     check_min_arg_count("`syscall(SYS_futex, FUTEX_WAIT_BITSET, ...)`", args)?;
-                let _timeout = this.read_pointer(timeout)?;
-                let _uaddr2 = this.read_pointer(uaddr2)?;
-                (timeout, this.read_scalar(bitset)?.to_u32()?)
+                let _timeout = ecx.read_pointer(timeout)?;
+                let _uaddr2 = ecx.read_pointer(uaddr2)?;
+                (timeout, ecx.read_scalar(bitset)?.to_u32()?)
             } else {
                 let [_, _, _, _, timeout] =
                     check_min_arg_count("`syscall(SYS_futex, FUTEX_WAIT, ...)`", args)?;
@@ -63,22 +67,21 @@ pub fn futex<'tcx>(
             };
 
             if bitset == 0 {
-                this.set_last_error_and_return(LibcError("EINVAL"), dest)?;
-                return interp_ok(());
+                return ecx.set_last_error_and_return(LibcError("EINVAL"), dest);
             }
 
-            let timeout = this.deref_pointer_as(timeout, this.libc_ty_layout("timespec"))?;
-            let timeout = if this.ptr_is_null(timeout.ptr())? {
+            let timeout = ecx.deref_pointer_as(timeout, ecx.libc_ty_layout("timespec"))?;
+            let timeout = if ecx.ptr_is_null(timeout.ptr())? {
                 None
             } else {
-                let duration = match this.read_timespec(&timeout)? {
+                let duration = match ecx.read_timespec(&timeout)? {
                     Some(duration) => duration,
                     None => {
-                        return this.set_last_error_and_return(LibcError("EINVAL"), dest);
+                        return ecx.set_last_error_and_return(LibcError("EINVAL"), dest);
                     }
                 };
                 let timeout_clock = if op & futex_realtime == futex_realtime {
-                    this.check_no_isolation(
+                    ecx.check_no_isolation(
                         "`futex` syscall with `op=FUTEX_WAIT` and non-null timeout with `FUTEX_CLOCK_REALTIME`",
                     )?;
                     TimeoutClock::RealTime
@@ -99,19 +102,18 @@ pub fn futex<'tcx>(
             // effects of this and the other thread are correctly observed,
             // otherwise we will deadlock.
             //
-            // There are two scenarios to consider:
-            // 1. If we (FUTEX_WAIT) execute first, we'll push ourselves into
-            //    the waiters queue and go to sleep. They (addr write & FUTEX_WAKE)
-            //    will see us in the queue and wake us up.
-            // 2. If they (addr write & FUTEX_WAKE) execute first, we must observe
-            //    addr's new value. If we see an outdated value that happens to equal
-            //    the expected val, then we'll put ourselves to sleep with no one to wake us
-            //    up, so we end up with a deadlock. This is prevented by having a SeqCst
-            //    fence inside FUTEX_WAKE syscall, and another SeqCst fence
-            //    below, the atomic read on addr after the SeqCst fence is guaranteed
-            //    not to see any value older than the addr write immediately before
-            //    calling FUTEX_WAKE. We'll see futex_val != val and return without
-            //    sleeping.
+            // There are two scenarios to consider, depending on whether WAIT or WAKE goes first:
+            // 1. If we (FUTEX_WAIT) execute first, we'll push ourselves into the waiters queue and
+            //    go to sleep. They (FUTEX_WAKE) will see us in the queue and wake us up. It doesn't
+            //    matter how the addr write is ordered.
+            // 2. If they (FUTEX_WAKE) execute first, that means the addr write is also before us
+            //    (FUTEX_WAIT). It is crucial that we observe addr's new value. If we see an
+            //    outdated value that happens to equal the expected val, then we'll put ourselves to
+            //    sleep with no one to wake us up, so we end up with a deadlock. This is prevented
+            //    by having a SeqCst fence inside FUTEX_WAKE syscall, and another SeqCst fence here
+            //    in FUTEX_WAIT. The atomic read on addr after the SeqCst fence is guaranteed not to
+            //    see any value older than the addr write immediately before calling FUTEX_WAKE.
+            //    We'll see futex_val != val and return without sleeping.
             //
             //    Note that the fences do not create any happens-before relationship.
             //    The read sees the write immediately before the fence not because
@@ -137,25 +139,36 @@ pub fn futex<'tcx>(
             //
             // Thankfully, preemptions cannot happen inside a Miri shim, so we do not need to
             // do anything special to guarantee fence-load-comparison atomicity.
-            this.atomic_fence(AtomicFenceOrd::SeqCst)?;
+            ecx.atomic_fence(AtomicFenceOrd::SeqCst)?;
             // Read an `i32` through the pointer, regardless of any wrapper types.
             // It's not uncommon for `addr` to be passed as another type than `*mut i32`, such as `*const AtomicI32`.
-            let futex_val = this.read_scalar_atomic(&addr, AtomicReadOrd::Relaxed)?.to_i32()?;
+            // We do an acquire read -- it only seems reasonable that if we observe a value here, we
+            // actually establish an ordering with that value.
+            let futex_val = ecx.read_scalar_atomic(&addr, AtomicReadOrd::Acquire)?.to_i32()?;
             if val == futex_val {
                 // The value still matches, so we block the thread and make it wait for FUTEX_WAKE.
-                this.futex_wait(
-                    addr_usize,
+
+                // This cannot fail since we already did an atomic acquire read on that pointer.
+                // Acquire reads are only allowed on mutable memory.
+                let futex_ref = ecx
+                    .get_sync_or_init(addr.ptr(), |_| LinuxFutex { futex: Default::default() })
+                    .unwrap()
+                    .futex
+                    .clone();
+
+                ecx.futex_wait(
+                    futex_ref,
                     bitset,
                     timeout,
-                    Scalar::from_target_isize(0, this), // retval_succ
-                    Scalar::from_target_isize(-1, this), // retval_timeout
+                    Scalar::from_target_isize(0, ecx), // retval_succ
+                    Scalar::from_target_isize(-1, ecx), // retval_timeout
                     dest.clone(),
-                    this.eval_libc("ETIMEDOUT"), // errno_timeout
+                    LibcError("ETIMEDOUT"), // errno_timeout
                 );
             } else {
                 // The futex value doesn't match the expected value, so we return failure
                 // right away without sleeping: -1 and errno set to EAGAIN.
-                return this.set_last_error_and_return(LibcError("EAGAIN"), dest);
+                return ecx.set_last_error_and_return(LibcError("EAGAIN"), dest);
             }
         }
         // FUTEX_WAKE: (int *addr, int op = FUTEX_WAKE, int val)
@@ -165,32 +178,43 @@ pub fn futex<'tcx>(
         // FUTEX_WAKE_BITSET: (int *addr, int op = FUTEX_WAKE, int val, const timespect *_unused, int *_unused, unsigned int bitset)
         // Same as FUTEX_WAKE, but allows you to specify a bitset to select which threads to wake up.
         op if op == futex_wake || op == futex_wake_bitset => {
+            let Some(futex_ref) =
+                ecx.get_sync_or_init(addr.ptr(), |_| LinuxFutex { futex: Default::default() })
+            else {
+                // No AllocId, or no live allocation at that AllocId.
+                // Return an error code. (That seems nicer than silently doing something non-intuitive.)
+                // This means that if an address gets reused by a new allocation,
+                // we'll use an independent futex queue for this... that seems acceptable.
+                return ecx.set_last_error_and_return(LibcError("EFAULT"), dest);
+            };
+            let futex_ref = futex_ref.futex.clone();
+
             let bitset = if op == futex_wake_bitset {
                 let [_, _, _, _, timeout, uaddr2, bitset] =
                     check_min_arg_count("`syscall(SYS_futex, FUTEX_WAKE_BITSET, ...)`", args)?;
-                let _timeout = this.read_pointer(timeout)?;
-                let _uaddr2 = this.read_pointer(uaddr2)?;
-                this.read_scalar(bitset)?.to_u32()?
+                let _timeout = ecx.read_pointer(timeout)?;
+                let _uaddr2 = ecx.read_pointer(uaddr2)?;
+                ecx.read_scalar(bitset)?.to_u32()?
             } else {
                 u32::MAX
             };
             if bitset == 0 {
-                return this.set_last_error_and_return(LibcError("EINVAL"), dest);
+                return ecx.set_last_error_and_return(LibcError("EINVAL"), dest);
             }
             // Together with the SeqCst fence in futex_wait, this makes sure that futex_wait
             // will see the latest value on addr which could be changed by our caller
             // before doing the syscall.
-            this.atomic_fence(AtomicFenceOrd::SeqCst)?;
+            ecx.atomic_fence(AtomicFenceOrd::SeqCst)?;
             let mut n = 0;
-            #[allow(clippy::arithmetic_side_effects)]
+            #[expect(clippy::arithmetic_side_effects)]
             for _ in 0..val {
-                if this.futex_wake(addr_usize, bitset)? {
+                if ecx.futex_wake(&futex_ref, bitset)? {
                     n += 1;
                 } else {
                     break;
                 }
             }
-            this.write_scalar(Scalar::from_target_isize(n, this), dest)?;
+            ecx.write_scalar(Scalar::from_target_isize(n, ecx), dest)?;
         }
         op => throw_unsup_format!("Miri does not support `futex` syscall with op={}", op),
     }
diff --git a/src/tools/miri/src/shims/unix/linux/syscall.rs b/src/tools/miri/src/shims/unix/linux/syscall.rs
new file mode 100644
index 00000000000..9f6935f096b
--- /dev/null
+++ b/src/tools/miri/src/shims/unix/linux/syscall.rs
@@ -0,0 +1,63 @@
+use rustc_abi::ExternAbi;
+use rustc_span::Symbol;
+
+use self::shims::unix::linux::eventfd::EvalContextExt as _;
+use crate::helpers::check_min_arg_count;
+use crate::shims::unix::linux::sync::futex;
+use crate::*;
+
+pub fn syscall<'tcx>(
+    ecx: &mut MiriInterpCx<'tcx>,
+    link_name: Symbol,
+    abi: ExternAbi,
+    args: &[OpTy<'tcx>],
+    dest: &MPlaceTy<'tcx>,
+) -> InterpResult<'tcx> {
+    // We do not use `check_shim` here because `syscall` is variadic. The argument
+    // count is checked bellow.
+    ecx.check_abi_and_shim_symbol_clash(abi, ExternAbi::C { unwind: false }, link_name)?;
+    // The syscall variadic function is legal to call with more arguments than needed,
+    // extra arguments are simply ignored. The important check is that when we use an
+    // argument, we have to also check all arguments *before* it to ensure that they
+    // have the right type.
+
+    let sys_getrandom = ecx.eval_libc("SYS_getrandom").to_target_usize(ecx)?;
+    let sys_futex = ecx.eval_libc("SYS_futex").to_target_usize(ecx)?;
+    let sys_eventfd2 = ecx.eval_libc("SYS_eventfd2").to_target_usize(ecx)?;
+
+    let [op] = check_min_arg_count("syscall", args)?;
+    match ecx.read_target_usize(op)? {
+        // `libc::syscall(NR_GETRANDOM, buf.as_mut_ptr(), buf.len(), GRND_NONBLOCK)`
+        // is called if a `HashMap` is created the regular way (e.g. HashMap<K, V>).
+        num if num == sys_getrandom => {
+            // Used by getrandom 0.1
+            // The first argument is the syscall id, so skip over it.
+            let [_, ptr, len, flags] = check_min_arg_count("syscall(SYS_getrandom, ...)", args)?;
+
+            let ptr = ecx.read_pointer(ptr)?;
+            let len = ecx.read_target_usize(len)?;
+            // The only supported flags are GRND_RANDOM and GRND_NONBLOCK,
+            // neither of which have any effect on our current PRNG.
+            // See <https://github.com/rust-lang/rust/pull/79196> for a discussion of argument sizes.
+            let _flags = ecx.read_scalar(flags)?.to_i32()?;
+
+            ecx.gen_random(ptr, len)?;
+            ecx.write_scalar(Scalar::from_target_usize(len, ecx), dest)?;
+        }
+        // `futex` is used by some synchronization primitives.
+        num if num == sys_futex => {
+            futex(ecx, args, dest)?;
+        }
+        num if num == sys_eventfd2 => {
+            let [_, initval, flags] = check_min_arg_count("syscall(SYS_evetfd2, ...)", args)?;
+
+            let result = ecx.eventfd(initval, flags)?;
+            ecx.write_int(result.to_i32()?, dest)?;
+        }
+        num => {
+            throw_unsup_format!("syscall: unsupported syscall number {num}");
+        }
+    };
+
+    interp_ok(())
+}
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 cd07bc9e013..003025916cd 100644
--- a/src/tools/miri/src/shims/unix/macos/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/macos/foreign_items.rs
@@ -1,5 +1,5 @@
+use rustc_abi::ExternAbi;
 use rustc_span::Symbol;
-use rustc_target::spec::abi::Abi;
 
 use super::sync::EvalContextExt as _;
 use crate::shims::unix::*;
@@ -14,7 +14,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_foreign_item_inner(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -25,55 +25,58 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         match link_name.as_str() {
             // errno
             "__error" => {
-                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let errno_place = this.last_error_place()?;
                 this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
             }
 
             // File related shims
             "close$NOCANCEL" => {
-                let [result] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [result] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.close(result)?;
                 this.write_scalar(result, dest)?;
             }
             "stat" | "stat64" | "stat$INODE64" => {
                 let [path, buf] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.macos_fbsd_stat(path, buf)?;
                 this.write_scalar(result, dest)?;
             }
             "lstat" | "lstat64" | "lstat$INODE64" => {
                 let [path, buf] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.macos_fbsd_lstat(path, buf)?;
                 this.write_scalar(result, dest)?;
             }
             "fstat" | "fstat64" | "fstat$INODE64" => {
-                let [fd, buf] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [fd, buf] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.macos_fbsd_fstat(fd, buf)?;
                 this.write_scalar(result, dest)?;
             }
             "opendir$INODE64" => {
-                let [name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [name] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.opendir(name)?;
                 this.write_scalar(result, dest)?;
             }
             "readdir_r" | "readdir_r$INODE64" => {
                 let [dirp, entry, result] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.macos_fbsd_readdir_r(dirp, entry, result)?;
                 this.write_scalar(result, dest)?;
             }
             "realpath$DARWIN_EXTSN" => {
                 let [path, resolved_path] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.realpath(path, resolved_path)?;
                 this.write_scalar(result, dest)?;
             }
 
             // Environment related shims
             "_NSGetEnviron" => {
-                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let environ = this.machine.env_vars.unix().environ();
                 this.write_pointer(environ, dest)?;
             }
@@ -81,7 +84,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Random data generation
             "CCRandomGenerateBytes" => {
                 let [bytes, count] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let bytes = this.read_pointer(bytes)?;
                 let count = this.read_target_usize(count)?;
                 let success = this.eval_libc_i32("kCCSuccess");
@@ -91,29 +94,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
             // Time related shims
             "mach_absolute_time" => {
-                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.mach_absolute_time()?;
                 this.write_scalar(result, dest)?;
             }
 
             "mach_timebase_info" => {
-                let [info] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [info] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.mach_timebase_info(info)?;
                 this.write_scalar(result, dest)?;
             }
 
             // Access to command-line arguments
             "_NSGetArgc" => {
-                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.write_pointer(this.machine.argc.expect("machine must be initialized"), dest)?;
             }
             "_NSGetArgv" => {
-                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.write_pointer(this.machine.argv.expect("machine must be initialized"), dest)?;
             }
             "_NSGetExecutablePath" => {
                 let [buf, bufsize] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.check_no_isolation("`_NSGetExecutablePath`")?;
 
                 let buf_ptr = this.read_pointer(buf)?;
@@ -139,7 +143,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Thread-local storage
             "_tlv_atexit" => {
                 let [dtor, data] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let dtor = this.read_pointer(dtor)?;
                 let dtor = this.get_ptr_fn(dtor)?.as_instance()?;
                 let data = this.read_scalar(data)?;
@@ -149,13 +153,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
             // Querying system information
             "pthread_get_stackaddr_np" => {
-                let [thread] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [thread] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.read_target_usize(thread)?;
                 let stack_addr = Scalar::from_uint(this.machine.stack_addr, this.pointer_size());
                 this.write_scalar(stack_addr, dest)?;
             }
             "pthread_get_stacksize_np" => {
-                let [thread] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [thread] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.read_target_usize(thread)?;
                 let stack_size = Scalar::from_uint(this.machine.stack_size, this.pointer_size());
                 this.write_scalar(stack_size, dest)?;
@@ -163,7 +169,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
             // Threading
             "pthread_setname_np" => {
-                let [name] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [name] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 // The real implementation has logic in two places:
                 // * in userland at https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread.c#L1178-L1200,
@@ -174,18 +181,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // are met, then the name is set and 0 is returned. Otherwise, if
                 // the specified name is lomnger than MAXTHREADNAMESIZE, then
                 // ENAMETOOLONG is returned.
-                //
-                // FIXME: the real implementation maybe returns ESRCH if the thread ID is invalid.
                 let thread = this.pthread_self()?;
-                let res = if this.pthread_setname_np(
+                let res = match this.pthread_setname_np(
                     thread,
                     this.read_scalar(name)?,
                     this.eval_libc("MAXTHREADNAMESIZE").to_target_usize(this)?.try_into().unwrap(),
                     /* truncate */ false,
                 )? {
-                    Scalar::from_u32(0)
-                } else {
-                    this.eval_libc("ENAMETOOLONG")
+                    ThreadNameResult::Ok => Scalar::from_u32(0),
+                    ThreadNameResult::NameTooLong => this.eval_libc("ENAMETOOLONG"),
+                    ThreadNameResult::ThreadNotFound => unreachable!(),
                 };
                 // Contrary to the manpage, `pthread_setname_np` on macOS still
                 // returns an integer indicating success.
@@ -193,7 +198,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             "pthread_getname_np" => {
                 let [thread, name, len] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 // The function's behavior isn't portable between platforms.
                 // In case of macOS, a truncated name (due to a too small buffer)
@@ -203,36 +208,43 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // https://github.com/apple-oss-distributions/libpthread/blob/c032e0b076700a0a47db75528a282b8d3a06531a/src/pthread.c#L1160-L1175.
                 // The key part is the strlcpy, which truncates the resulting value,
                 // but always null terminates (except for zero sized buffers).
-                //
-                // FIXME: the real implementation returns ESRCH if the thread ID is invalid.
-                let res = Scalar::from_u32(0);
-                this.pthread_getname_np(
+                let res = match this.pthread_getname_np(
                     this.read_scalar(thread)?,
                     this.read_scalar(name)?,
                     this.read_scalar(len)?,
                     /* truncate */ true,
-                )?;
+                )? {
+                    ThreadNameResult::Ok => Scalar::from_u32(0),
+                    // `NameTooLong` is possible when the buffer is zero sized,
+                    ThreadNameResult::NameTooLong => Scalar::from_u32(0),
+                    ThreadNameResult::ThreadNotFound => this.eval_libc("ESRCH"),
+                };
                 this.write_scalar(res, dest)?;
             }
 
             "os_unfair_lock_lock" => {
-                let [lock_op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [lock_op] =
+                    this.check_shim(abi, ExternAbi::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)?;
+                let [lock_op] =
+                    this.check_shim(abi, ExternAbi::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)?;
+                let [lock_op] =
+                    this.check_shim(abi, ExternAbi::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)?;
+                let [lock_op] =
+                    this.check_shim(abi, ExternAbi::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)?;
+                let [lock_op] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 this.os_unfair_lock_assert_not_owner(lock_op)?;
             }
 
diff --git a/src/tools/miri/src/shims/unix/macos/sync.rs b/src/tools/miri/src/shims/unix/macos/sync.rs
index 1df1202442a..f66a57ae706 100644
--- a/src/tools/miri/src/shims/unix/macos/sync.rs
+++ b/src/tools/miri/src/shims/unix/macos/sync.rs
@@ -10,22 +10,25 @@
 //! and we do not detect copying of the lock, but macOS doesn't guarantee anything
 //! in that case either.
 
-use rustc_target::abi::Size;
+use rustc_abi::Size;
 
 use crate::*;
 
-#[derive(Copy, Clone)]
+#[derive(Clone)]
 enum MacOsUnfairLock {
     Poisoned,
-    Active { id: MutexId },
+    Active { mutex_ref: MutexRef },
 }
 
 impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
 trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
-    fn os_unfair_lock_get_data(
-        &mut self,
+    fn os_unfair_lock_get_data<'a>(
+        &'a mut self,
         lock_ptr: &OpTy<'tcx>,
-    ) -> InterpResult<'tcx, MacOsUnfairLock> {
+    ) -> InterpResult<'tcx, &'a MacOsUnfairLock>
+    where
+        'tcx: 'a,
+    {
         let this = self.eval_context_mut();
         let lock = this.deref_pointer(lock_ptr)?;
         this.lazy_sync_get_data(
@@ -42,8 +45,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 interp_ok(MacOsUnfairLock::Poisoned)
             },
             |ecx| {
-                let id = ecx.machine.sync.mutex_create();
-                interp_ok(MacOsUnfairLock::Active { id })
+                let mutex_ref = ecx.machine.sync.mutex_create();
+                interp_ok(MacOsUnfairLock::Active { mutex_ref })
             },
         )
     }
@@ -54,7 +57,7 @@ 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 MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else {
+        let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
             // Trying to get a poisoned lock. Just block forever...
             this.block_thread(
                 BlockReason::Sleep,
@@ -68,18 +71,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             );
             return interp_ok(());
         };
+        let mutex_ref = mutex_ref.clone();
 
-        if this.mutex_is_locked(id) {
-            if this.mutex_get_owner(id) == this.active_thread() {
+        if this.mutex_is_locked(&mutex_ref) {
+            if this.mutex_get_owner(&mutex_ref) == 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);
+            this.mutex_enqueue_and_block(&mutex_ref, None);
         } else {
-            this.mutex_lock(id);
+            this.mutex_lock(&mutex_ref);
         }
 
         interp_ok(())
@@ -92,18 +96,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
-        let MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else {
+        let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
             // Trying to get a poisoned lock. That never works.
             this.write_scalar(Scalar::from_bool(false), dest)?;
             return interp_ok(());
         };
+        let mutex_ref = mutex_ref.clone();
 
-        if this.mutex_is_locked(id) {
+        if this.mutex_is_locked(&mutex_ref) {
             // 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.mutex_lock(&mutex_ref);
             this.write_scalar(Scalar::from_bool(true), dest)?;
         }
 
@@ -113,15 +118,16 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn os_unfair_lock_unlock(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
-        let MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else {
+        let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
             // The lock is poisoned, who knows who owns it... we'll pretend: someone else.
             throw_machine_stop!(TerminationInfo::Abort(
                 "attempted to unlock an os_unfair_lock not owned by the current thread".to_owned()
             ));
         };
+        let mutex_ref = mutex_ref.clone();
 
         // Now, unlock.
-        if this.mutex_unlock(id)?.is_none() {
+        if this.mutex_unlock(&mutex_ref)?.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()
@@ -130,7 +136,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         // If the lock is not locked by anyone now, it went quer.
         // Reset to zero so that it can be moved and initialized again for the next phase.
-        if !this.mutex_is_locked(id) {
+        if !this.mutex_is_locked(&mutex_ref) {
             let lock_place = this.deref_pointer_as(lock_op, this.machine.layouts.u32)?;
             this.write_scalar_atomic(Scalar::from_u32(0), &lock_place, AtomicWriteOrd::Relaxed)?;
         }
@@ -141,13 +147,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn os_unfair_lock_assert_owner(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
-        let MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else {
+        let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
             // The lock is poisoned, who knows who owns it... we'll pretend: someone else.
             throw_machine_stop!(TerminationInfo::Abort(
                 "called os_unfair_lock_assert_owner on an os_unfair_lock not owned by the current thread".to_owned()
             ));
         };
-        if !this.mutex_is_locked(id) || this.mutex_get_owner(id) != this.active_thread() {
+        let mutex_ref = mutex_ref.clone();
+
+        if !this.mutex_is_locked(&mutex_ref)
+            || this.mutex_get_owner(&mutex_ref) != 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()
             ));
@@ -161,11 +171,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn os_unfair_lock_assert_not_owner(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
-        let MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else {
+        let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
             // The lock is poisoned, who knows who owns it... we'll pretend: someone else.
             return interp_ok(());
         };
-        if this.mutex_is_locked(id) && this.mutex_get_owner(id) == this.active_thread() {
+        let mutex_ref = mutex_ref.clone();
+
+        if this.mutex_is_locked(&mutex_ref)
+            && this.mutex_get_owner(&mutex_ref) == 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()
             ));
@@ -173,7 +187,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         // If the lock is not locked by anyone now, it went quer.
         // Reset to zero so that it can be moved and initialized again for the next phase.
-        if !this.mutex_is_locked(id) {
+        if !this.mutex_is_locked(&mutex_ref) {
             let lock_place = this.deref_pointer_as(lock_op, this.machine.layouts.u32)?;
             this.write_scalar_atomic(Scalar::from_u32(0), &lock_place, AtomicWriteOrd::Relaxed)?;
         }
diff --git a/src/tools/miri/src/shims/unix/mem.rs b/src/tools/miri/src/shims/unix/mem.rs
index 9371edfc83d..5531b944e17 100644
--- a/src/tools/miri/src/shims/unix/mem.rs
+++ b/src/tools/miri/src/shims/unix/mem.rs
@@ -14,7 +14,7 @@
 //! munmap shim which would partially unmap a region of address space previously mapped by mmap will
 //! report UB.
 
-use rustc_target::abi::Size;
+use rustc_abi::Size;
 
 use crate::*;
 
@@ -132,7 +132,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         // addr must be a multiple of the page size, but apart from that munmap is just implemented
         // as a dealloc.
-        #[allow(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
+        #[expect(clippy::arithmetic_side_effects)] // PAGE_SIZE is nonzero
         if addr.addr().bytes() % this.machine.page_size != 0 {
             return this.set_last_error_and_return_i32(LibcError("EINVAL"));
         }
diff --git a/src/tools/miri/src/shims/unix/mod.rs b/src/tools/miri/src/shims/unix/mod.rs
index 9bc310e8d0a..c8c25c636ee 100644
--- a/src/tools/miri/src/shims/unix/mod.rs
+++ b/src/tools/miri/src/shims/unix/mod.rs
@@ -21,7 +21,7 @@ pub use self::fs::{DirTable, EvalContextExt as _};
 pub use self::linux::epoll::EpollInterestTable;
 pub use self::mem::EvalContextExt as _;
 pub use self::sync::EvalContextExt as _;
-pub use self::thread::EvalContextExt as _;
+pub use self::thread::{EvalContextExt as _, ThreadNameResult};
 pub use self::unnamed_socket::EvalContextExt as _;
 
 // Make up some constants.
diff --git a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
index c9c1b01b8b1..526b64cff69 100644
--- a/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
+++ b/src/tools/miri/src/shims/unix/solarish/foreign_items.rs
@@ -1,5 +1,5 @@
+use rustc_abi::ExternAbi;
 use rustc_span::Symbol;
-use rustc_target::spec::abi::Abi;
 
 use crate::shims::unix::*;
 use crate::*;
@@ -13,7 +13,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_foreign_item_inner(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -22,42 +22,50 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Threading
             "pthread_setname_np" => {
                 let [thread, name] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 // THREAD_NAME_MAX allows a thread name of 31+1 length
                 // https://github.com/illumos/illumos-gate/blob/7671517e13b8123748eda4ef1ee165c6d9dba7fe/usr/src/uts/common/sys/thread.h#L613
                 let max_len = 32;
-                let res = this.pthread_setname_np(
+                // See https://illumos.org/man/3C/pthread_setname_np for the error codes.
+                let res = match this.pthread_setname_np(
                     this.read_scalar(thread)?,
                     this.read_scalar(name)?,
                     max_len,
                     /* truncate */ false,
-                )?;
-                let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
+                )? {
+                    ThreadNameResult::Ok => Scalar::from_u32(0),
+                    ThreadNameResult::NameTooLong => this.eval_libc("ERANGE"),
+                    ThreadNameResult::ThreadNotFound => this.eval_libc("ESRCH"),
+                };
                 this.write_scalar(res, dest)?;
             }
             "pthread_getname_np" => {
                 let [thread, name, len] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
-                // https://github.com/illumos/illumos-gate/blob/c56822be04b6c157c8b6f2281e47214c3b86f657/usr/src/lib/libc/port/threads/thr.c#L2449-L2480
-                let res = this.pthread_getname_np(
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
+                // See https://illumos.org/man/3C/pthread_getname_np for the error codes.
+                let res = match this.pthread_getname_np(
                     this.read_scalar(thread)?,
                     this.read_scalar(name)?,
                     this.read_scalar(len)?,
                     /* truncate */ false,
-                )?;
-                let res = if res { Scalar::from_u32(0) } else { this.eval_libc("ERANGE") };
+                )? {
+                    ThreadNameResult::Ok => Scalar::from_u32(0),
+                    ThreadNameResult::NameTooLong => this.eval_libc("ERANGE"),
+                    ThreadNameResult::ThreadNotFound => this.eval_libc("ESRCH"),
+                };
                 this.write_scalar(res, dest)?;
             }
 
             // Miscellaneous
             "___errno" => {
-                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let errno_place = this.last_error_place()?;
                 this.write_scalar(errno_place.to_ref(this).to_scalar(), dest)?;
             }
 
             "stack_getbounds" => {
-                let [stack] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [stack] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let stack = this.deref_pointer_as(stack, this.libc_ty_layout("stack_t"))?;
 
                 this.write_int_fields_named(
@@ -76,7 +84,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
             "pset_info" => {
                 let [pset, tpe, cpus, list] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 // We do not need to handle the current process cpu mask, available_parallelism
                 // implementation pass null anyway. We only care for the number of
                 // cpus.
diff --git a/src/tools/miri/src/shims/unix/sync.rs b/src/tools/miri/src/shims/unix/sync.rs
index a4beaa47baa..416cf020dcc 100644
--- a/src/tools/miri/src/shims/unix/sync.rs
+++ b/src/tools/miri/src/shims/unix/sync.rs
@@ -1,6 +1,6 @@
 use std::sync::atomic::{AtomicBool, Ordering};
 
-use rustc_target::abi::Size;
+use rustc_abi::Size;
 
 use crate::concurrency::sync::LAZY_INIT_COOKIE;
 use crate::*;
@@ -116,9 +116,9 @@ enum MutexKind {
     ErrorCheck,
 }
 
-#[derive(Debug, Clone, Copy)]
+#[derive(Debug, Clone)]
 struct PthreadMutex {
-    id: MutexId,
+    mutex_ref: MutexRef,
     kind: MutexKind,
 }
 
@@ -175,19 +175,20 @@ fn mutex_create<'tcx>(
 ) -> InterpResult<'tcx, PthreadMutex> {
     let mutex = ecx.deref_pointer(mutex_ptr)?;
     let id = ecx.machine.sync.mutex_create();
-    let data = PthreadMutex { id, kind };
-    ecx.lazy_sync_init(&mutex, mutex_init_offset(ecx)?, data)?;
+    let data = PthreadMutex { mutex_ref: id, kind };
+    ecx.lazy_sync_init(&mutex, mutex_init_offset(ecx)?, data.clone())?;
     interp_ok(data)
 }
 
-/// Returns the `MutexId` of the mutex stored at `mutex_op`.
-///
-/// `mutex_get_id` will also check if the mutex has been moved since its first use and
-/// return an error if it has.
+/// Returns the mutex data stored at the address that `mutex_ptr` points to.
+/// Will raise an error if the mutex has been moved since its first use.
 fn mutex_get_data<'tcx, 'a>(
     ecx: &'a mut MiriInterpCx<'tcx>,
     mutex_ptr: &OpTy<'tcx>,
-) -> InterpResult<'tcx, PthreadMutex> {
+) -> InterpResult<'tcx, &'a PthreadMutex>
+where
+    'tcx: 'a,
+{
     let mutex = ecx.deref_pointer(mutex_ptr)?;
     ecx.lazy_sync_get_data(
         &mutex,
@@ -196,7 +197,7 @@ fn mutex_get_data<'tcx, 'a>(
         |ecx| {
             let kind = mutex_kind_from_static_initializer(ecx, &mutex)?;
             let id = ecx.machine.sync.mutex_create();
-            interp_ok(PthreadMutex { id, kind })
+            interp_ok(PthreadMutex { mutex_ref: id, kind })
         },
     )
 }
@@ -261,10 +262,13 @@ fn rwlock_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size
     interp_ok(offset)
 }
 
-fn rwlock_get_data<'tcx>(
-    ecx: &mut MiriInterpCx<'tcx>,
+fn rwlock_get_data<'tcx, 'a>(
+    ecx: &'a mut MiriInterpCx<'tcx>,
     rwlock_ptr: &OpTy<'tcx>,
-) -> InterpResult<'tcx, PthreadRwLock> {
+) -> InterpResult<'tcx, &'a PthreadRwLock>
+where
+    'tcx: 'a,
+{
     let rwlock = ecx.deref_pointer(rwlock_ptr)?;
     ecx.lazy_sync_get_data(
         &rwlock,
@@ -391,10 +395,13 @@ fn cond_create<'tcx>(
     interp_ok(data)
 }
 
-fn cond_get_data<'tcx>(
-    ecx: &mut MiriInterpCx<'tcx>,
+fn cond_get_data<'tcx, 'a>(
+    ecx: &'a mut MiriInterpCx<'tcx>,
     cond_ptr: &OpTy<'tcx>,
-) -> InterpResult<'tcx, PthreadCondvar> {
+) -> InterpResult<'tcx, &'a PthreadCondvar>
+where
+    'tcx: 'a,
+{
     let cond = ecx.deref_pointer(cond_ptr)?;
     ecx.lazy_sync_get_data(
         &cond,
@@ -500,12 +507,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
-        let mutex = mutex_get_data(this, mutex_op)?;
+        let mutex = mutex_get_data(this, mutex_op)?.clone();
 
-        let ret = if this.mutex_is_locked(mutex.id) {
-            let owner_thread = this.mutex_get_owner(mutex.id);
+        let ret = if this.mutex_is_locked(&mutex.mutex_ref) {
+            let owner_thread = this.mutex_get_owner(&mutex.mutex_ref);
             if owner_thread != this.active_thread() {
-                this.mutex_enqueue_and_block(mutex.id, Some((Scalar::from_i32(0), dest.clone())));
+                this.mutex_enqueue_and_block(
+                    &mutex.mutex_ref,
+                    Some((Scalar::from_i32(0), dest.clone())),
+                );
                 return interp_ok(());
             } else {
                 // Trying to acquire the same mutex again.
@@ -517,14 +527,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     MutexKind::Normal => throw_machine_stop!(TerminationInfo::Deadlock),
                     MutexKind::ErrorCheck => this.eval_libc_i32("EDEADLK"),
                     MutexKind::Recursive => {
-                        this.mutex_lock(mutex.id);
+                        this.mutex_lock(&mutex.mutex_ref);
                         0
                     }
                 }
             }
         } else {
             // The mutex is unlocked. Let's lock it.
-            this.mutex_lock(mutex.id);
+            this.mutex_lock(&mutex.mutex_ref);
             0
         };
         this.write_scalar(Scalar::from_i32(ret), dest)?;
@@ -534,10 +544,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
-        let mutex = mutex_get_data(this, mutex_op)?;
+        let mutex = mutex_get_data(this, mutex_op)?.clone();
 
-        interp_ok(Scalar::from_i32(if this.mutex_is_locked(mutex.id) {
-            let owner_thread = this.mutex_get_owner(mutex.id);
+        interp_ok(Scalar::from_i32(if this.mutex_is_locked(&mutex.mutex_ref) {
+            let owner_thread = this.mutex_get_owner(&mutex.mutex_ref);
             if owner_thread != this.active_thread() {
                 this.eval_libc_i32("EBUSY")
             } else {
@@ -545,14 +555,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     MutexKind::Default | MutexKind::Normal | MutexKind::ErrorCheck =>
                         this.eval_libc_i32("EBUSY"),
                     MutexKind::Recursive => {
-                        this.mutex_lock(mutex.id);
+                        this.mutex_lock(&mutex.mutex_ref);
                         0
                     }
                 }
             }
         } else {
             // The mutex is unlocked. Let's lock it.
-            this.mutex_lock(mutex.id);
+            this.mutex_lock(&mutex.mutex_ref);
             0
         }))
     }
@@ -560,9 +570,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
-        let mutex = mutex_get_data(this, mutex_op)?;
+        let mutex = mutex_get_data(this, mutex_op)?.clone();
 
-        if let Some(_old_locked_count) = this.mutex_unlock(mutex.id)? {
+        if let Some(_old_locked_count) = this.mutex_unlock(&mutex.mutex_ref)? {
             // The mutex was locked by the current thread.
             interp_ok(Scalar::from_i32(0))
         } else {
@@ -588,10 +598,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let this = self.eval_context_mut();
 
         // Reading the field also has the side-effect that we detect double-`destroy`
-        // since we make the field unint below.
-        let mutex = mutex_get_data(this, mutex_op)?;
+        // since we make the field uninit below.
+        let mutex = mutex_get_data(this, mutex_op)?.clone();
 
-        if this.mutex_is_locked(mutex.id) {
+        if this.mutex_is_locked(&mutex.mutex_ref) {
             throw_ub_format!("destroyed a locked mutex");
         }
 
@@ -685,7 +695,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         let id = rwlock_get_data(this, rwlock_op)?.id;
 
-        #[allow(clippy::if_same_then_else)]
         if this.rwlock_reader_unlock(id)? || this.rwlock_writer_unlock(id)? {
             interp_ok(())
         } else {
@@ -697,7 +706,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let this = self.eval_context_mut();
 
         // Reading the field also has the side-effect that we detect double-`destroy`
-        // since we make the field unint below.
+        // since we make the field uninit below.
         let id = rwlock_get_data(this, rwlock_op)?.id;
 
         if this.rwlock_is_locked(id) {
@@ -822,12 +831,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
-        let data = cond_get_data(this, cond_op)?;
-        let mutex_id = mutex_get_data(this, mutex_op)?.id;
+        let data = *cond_get_data(this, cond_op)?;
+        let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref.clone();
 
         this.condvar_wait(
             data.id,
-            mutex_id,
+            mutex_ref,
             None, // no timeout
             Scalar::from_i32(0),
             Scalar::from_i32(0), // retval_timeout -- unused
@@ -846,8 +855,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     ) -> InterpResult<'tcx> {
         let this = self.eval_context_mut();
 
-        let data = cond_get_data(this, cond_op)?;
-        let mutex_id = mutex_get_data(this, mutex_op)?.id;
+        let data = *cond_get_data(this, cond_op)?;
+        let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref.clone();
 
         // Extract the timeout.
         let duration = match this
@@ -870,7 +879,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         this.condvar_wait(
             data.id,
-            mutex_id,
+            mutex_ref,
             Some((timeout_clock, TimeoutAnchor::Absolute, duration)),
             Scalar::from_i32(0),
             this.eval_libc("ETIMEDOUT"), // retval_timeout
@@ -884,7 +893,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let this = self.eval_context_mut();
 
         // Reading the field also has the side-effect that we detect double-`destroy`
-        // since we make the field unint below.
+        // since we make the field uninit below.
         let id = cond_get_data(this, cond_op)?.id;
         if this.condvar_is_awaited(id) {
             throw_ub_format!("destroying an awaited conditional variable");
diff --git a/src/tools/miri/src/shims/unix/thread.rs b/src/tools/miri/src/shims/unix/thread.rs
index 51256d800a4..3d990a1a042 100644
--- a/src/tools/miri/src/shims/unix/thread.rs
+++ b/src/tools/miri/src/shims/unix/thread.rs
@@ -1,7 +1,14 @@
-use rustc_target::spec::abi::Abi;
+use rustc_abi::ExternAbi;
 
 use crate::*;
 
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub enum ThreadNameResult {
+    Ok,
+    NameTooLong,
+    ThreadNotFound,
+}
+
 impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
 pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn pthread_create(
@@ -22,7 +29,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         this.start_regular_thread(
             Some(thread_info_place),
             start_routine,
-            Abi::C { unwind: false },
+            ExternAbi::C { unwind: false },
             func_arg,
             this.machine.layouts.mut_raw_ptr,
         )?;
@@ -30,7 +37,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         interp_ok(())
     }
 
-    fn pthread_join(&mut self, thread: &OpTy<'tcx>, retval: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
+    fn pthread_join(
+        &mut self,
+        thread: &OpTy<'tcx>,
+        retval: &OpTy<'tcx>,
+    ) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
         if !this.ptr_is_null(this.read_pointer(retval)?)? {
@@ -38,22 +49,26 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             throw_unsup_format!("Miri supports pthread_join only with retval==NULL");
         }
 
-        let thread_id = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?;
-        this.join_thread_exclusive(thread_id.try_into().expect("thread ID should fit in u32"))?;
+        let thread = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?;
+        let Ok(thread) = this.thread_id_try_from(thread) else {
+            return interp_ok(this.eval_libc("ESRCH"));
+        };
 
-        interp_ok(())
+        this.join_thread_exclusive(thread)?;
+
+        interp_ok(Scalar::from_u32(0))
     }
 
-    fn pthread_detach(&mut self, thread: &OpTy<'tcx>) -> InterpResult<'tcx, ()> {
+    fn pthread_detach(&mut self, thread: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
-        let thread_id = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?;
-        this.detach_thread(
-            thread_id.try_into().expect("thread ID should fit in u32"),
-            /*allow_terminated_joined*/ false,
-        )?;
+        let thread = this.read_scalar(thread)?.to_int(this.libc_ty_layout("pthread_t").size)?;
+        let Ok(thread) = this.thread_id_try_from(thread) else {
+            return interp_ok(this.eval_libc("ESRCH"));
+        };
+        this.detach_thread(thread, /*allow_terminated_joined*/ false)?;
 
-        interp_ok(())
+        interp_ok(Scalar::from_u32(0))
     }
 
     fn pthread_self(&mut self) -> InterpResult<'tcx, Scalar> {
@@ -65,18 +80,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
     /// Set the name of the specified thread. If the name including the null terminator
     /// is longer or equals to `name_max_len`, then if `truncate` is set the truncated name
-    /// is used as the thread name, otherwise `false` is returned.
+    /// is used as the thread name, otherwise [`ThreadNameResult::NameTooLong`] is returned.
+    /// If the specified thread wasn't found, [`ThreadNameResult::ThreadNotFound`] is returned.
     fn pthread_setname_np(
         &mut self,
         thread: Scalar,
         name: Scalar,
         name_max_len: usize,
         truncate: bool,
-    ) -> InterpResult<'tcx, bool> {
+    ) -> InterpResult<'tcx, ThreadNameResult> {
         let this = self.eval_context_mut();
 
         let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?;
-        let thread = ThreadId::try_from(thread).unwrap();
+        let Ok(thread) = this.thread_id_try_from(thread) else {
+            return interp_ok(ThreadNameResult::ThreadNotFound);
+        };
         let name = name.to_pointer(this)?;
         let mut name = this.read_c_str(name)?.to_owned();
 
@@ -85,29 +103,32 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             if truncate {
                 name.truncate(name_max_len.saturating_sub(1));
             } else {
-                return interp_ok(false);
+                return interp_ok(ThreadNameResult::NameTooLong);
             }
         }
 
         this.set_thread_name(thread, name);
 
-        interp_ok(true)
+        interp_ok(ThreadNameResult::Ok)
     }
 
     /// Get the name of the specified thread. If the thread name doesn't fit
     /// the buffer, then if `truncate` is set the truncated name is written out,
-    /// otherwise `false` is returned.
+    /// otherwise [`ThreadNameResult::NameTooLong`] is returned. If the specified
+    /// thread wasn't found, [`ThreadNameResult::ThreadNotFound`] is returned.
     fn pthread_getname_np(
         &mut self,
         thread: Scalar,
         name_out: Scalar,
         len: Scalar,
         truncate: bool,
-    ) -> InterpResult<'tcx, bool> {
+    ) -> InterpResult<'tcx, ThreadNameResult> {
         let this = self.eval_context_mut();
 
         let thread = thread.to_int(this.libc_ty_layout("pthread_t").size)?;
-        let thread = ThreadId::try_from(thread).unwrap();
+        let Ok(thread) = this.thread_id_try_from(thread) else {
+            return interp_ok(ThreadNameResult::ThreadNotFound);
+        };
         let name_out = name_out.to_pointer(this)?;
         let len = len.to_target_usize(this)?;
 
@@ -119,8 +140,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         };
 
         let (success, _written) = this.write_c_str(name, name_out, len)?;
+        let res = if success { ThreadNameResult::Ok } else { ThreadNameResult::NameTooLong };
 
-        interp_ok(success)
+        interp_ok(res)
     }
 
     fn sched_yield(&mut self) -> InterpResult<'tcx, ()> {
diff --git a/src/tools/miri/src/shims/unix/unnamed_socket.rs b/src/tools/miri/src/shims/unix/unnamed_socket.rs
index d0eba1eacd1..8ccce7c1986 100644
--- a/src/tools/miri/src/shims/unix/unnamed_socket.rs
+++ b/src/tools/miri/src/shims/unix/unnamed_socket.rs
@@ -7,7 +7,7 @@ use std::collections::VecDeque;
 use std::io;
 use std::io::{ErrorKind, Read};
 
-use rustc_target::abi::Size;
+use rustc_abi::Size;
 
 use crate::concurrency::VClock;
 use crate::shims::unix::fd::{FileDescriptionRef, WeakFileDescriptionRef};
diff --git a/src/tools/miri/src/shims/wasi/foreign_items.rs b/src/tools/miri/src/shims/wasi/foreign_items.rs
index b5de950de9a..2c349203d46 100644
--- a/src/tools/miri/src/shims/wasi/foreign_items.rs
+++ b/src/tools/miri/src/shims/wasi/foreign_items.rs
@@ -1,5 +1,5 @@
+use rustc_abi::ExternAbi;
 use rustc_span::Symbol;
-use rustc_target::spec::abi::Abi;
 
 use crate::shims::alloc::EvalContextExt as _;
 use crate::*;
@@ -13,7 +13,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_foreign_item_inner(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -22,13 +22,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Allocation
             "posix_memalign" => {
                 let [memptr, align, size] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let result = this.posix_memalign(memptr, align, size)?;
                 this.write_scalar(result, dest)?;
             }
             "aligned_alloc" => {
                 let [align, size] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let res = this.aligned_alloc(align, size)?;
                 this.write_pointer(res, dest)?;
             }
diff --git a/src/tools/miri/src/shims/windows/foreign_items.rs b/src/tools/miri/src/shims/windows/foreign_items.rs
index dee778876f6..a1fad6f9af4 100644
--- a/src/tools/miri/src/shims/windows/foreign_items.rs
+++ b/src/tools/miri/src/shims/windows/foreign_items.rs
@@ -2,15 +2,19 @@ use std::ffi::OsStr;
 use std::path::{self, Path, PathBuf};
 use std::{io, iter, str};
 
+use rustc_abi::{Align, ExternAbi, Size};
 use rustc_span::Symbol;
-use rustc_target::abi::{Align, Size};
-use rustc_target::spec::abi::Abi;
 
 use self::shims::windows::handle::{Handle, PseudoHandle};
 use crate::shims::os_str::bytes_to_os_str;
+use crate::shims::windows::handle::HandleError;
 use crate::shims::windows::*;
 use crate::*;
 
+// The NTSTATUS STATUS_INVALID_HANDLE (0xC0000008) encoded as a HRESULT by setting the N bit.
+// (https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/0642cb2f-2075-4469-918c-4441e69c548a)
+const STATUS_INVALID_HANDLE: u32 = 0xD0000008;
+
 pub fn is_dyn_sym(name: &str) -> bool {
     // std does dynamic detection for these symbols
     matches!(
@@ -26,7 +30,7 @@ fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result<PathBuf>> {
 }
 
 #[cfg(unix)]
-#[allow(clippy::get_first, clippy::arithmetic_side_effects)]
+#[expect(clippy::get_first, clippy::arithmetic_side_effects)]
 fn win_absolute<'tcx>(path: &Path) -> InterpResult<'tcx, io::Result<PathBuf>> {
     // We are on Unix, so we need to implement parts of the logic ourselves.
     let bytes = path.as_os_str().as_encoded_bytes();
@@ -79,7 +83,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_foreign_item_inner(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -97,47 +101,49 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Environment related shims
             "GetEnvironmentVariableW" => {
                 let [name, buf, size] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let result = this.GetEnvironmentVariableW(name, buf, size)?;
                 this.write_scalar(result, dest)?;
             }
             "SetEnvironmentVariableW" => {
                 let [name, value] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let result = this.SetEnvironmentVariableW(name, value)?;
                 this.write_scalar(result, dest)?;
             }
             "GetEnvironmentStringsW" => {
-                let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                let [] =
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let result = this.GetEnvironmentStringsW()?;
                 this.write_pointer(result, dest)?;
             }
             "FreeEnvironmentStringsW" => {
                 let [env_block] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let result = this.FreeEnvironmentStringsW(env_block)?;
                 this.write_scalar(result, dest)?;
             }
             "GetCurrentDirectoryW" => {
                 let [size, buf] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let result = this.GetCurrentDirectoryW(size, buf)?;
                 this.write_scalar(result, dest)?;
             }
             "SetCurrentDirectoryW" => {
                 let [path] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let result = this.SetCurrentDirectoryW(path)?;
                 this.write_scalar(result, dest)?;
             }
             "GetUserProfileDirectoryW" => {
                 let [token, buf, size] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let result = this.GetUserProfileDirectoryW(token, buf, size)?;
                 this.write_scalar(result, dest)?;
             }
             "GetCurrentProcessId" => {
-                let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                let [] =
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let result = this.GetCurrentProcessId()?;
                 this.write_scalar(result, dest)?;
             }
@@ -160,7 +166,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     n,
                     byte_offset,
                     _key,
-                ] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                ] = this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let handle = this.read_target_isize(handle)?;
                 let buf = this.read_pointer(buf)?;
                 let n = this.read_scalar(n)?.to_u32()?;
@@ -212,7 +218,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             "GetFullPathNameW" => {
                 let [filename, size, buffer, filepart] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 this.check_no_isolation("`GetFullPathNameW`")?;
 
                 let filename = this.read_pointer(filename)?;
@@ -244,7 +250,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Allocation
             "HeapAlloc" => {
                 let [handle, flags, size] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 this.read_target_isize(handle)?;
                 let flags = this.read_scalar(flags)?.to_u32()?;
                 let size = this.read_target_usize(size)?;
@@ -268,7 +274,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             "HeapFree" => {
                 let [handle, flags, ptr] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 this.read_target_isize(handle)?;
                 this.read_scalar(flags)?.to_u32()?;
                 let ptr = this.read_pointer(ptr)?;
@@ -281,7 +287,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             "HeapReAlloc" => {
                 let [handle, flags, old_ptr, size] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 this.read_target_isize(handle)?;
                 this.read_scalar(flags)?.to_u32()?;
                 let old_ptr = this.read_pointer(old_ptr)?;
@@ -300,7 +306,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 this.write_pointer(new_ptr, dest)?;
             }
             "LocalFree" => {
-                let [ptr] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                let [ptr] =
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
                 // "If the hMem parameter is NULL, LocalFree ignores the parameter and returns NULL."
                 // (https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-localfree)
@@ -313,12 +320,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // errno
             "SetLastError" => {
                 let [error] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let error = this.read_scalar(error)?;
                 this.set_last_error(error)?;
             }
             "GetLastError" => {
-                let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                let [] =
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let last_error = this.get_last_error()?;
                 this.write_scalar(last_error, dest)?;
             }
@@ -327,7 +335,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             "GetSystemInfo" => {
                 // Also called from `page_size` crate.
                 let [system_info] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let system_info =
                     this.deref_pointer_as(system_info, this.windows_ty_layout("SYSTEM_INFO"))?;
                 // Initialize with `0`.
@@ -350,12 +358,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // This just creates a key; Windows does not natively support TLS destructors.
 
                 // Create key and return it.
-                let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                let [] =
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let key = this.machine.tls.create_tls_key(None, dest.layout.size)?;
                 this.write_scalar(Scalar::from_uint(key, dest.layout.size), dest)?;
             }
             "TlsGetValue" => {
-                let [key] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                let [key] =
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let key = u128::from(this.read_scalar(key)?.to_u32()?);
                 let active_thread = this.active_thread();
                 let ptr = this.machine.tls.load_tls(key, active_thread, this)?;
@@ -363,7 +373,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             "TlsSetValue" => {
                 let [key, new_ptr] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let key = u128::from(this.read_scalar(key)?.to_u32()?);
                 let active_thread = this.active_thread();
                 let new_data = this.read_scalar(new_ptr)?;
@@ -372,10 +382,19 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // Return success (`1`).
                 this.write_int(1, dest)?;
             }
+            "TlsFree" => {
+                let [key] = this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
+                let key = u128::from(this.read_scalar(key)?.to_u32()?);
+                this.machine.tls.delete_tls_key(key)?;
+
+                // Return success (`1`).
+                this.write_int(1, dest)?;
+            }
 
             // Access to command-line arguments
             "GetCommandLineW" => {
-                let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                let [] =
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 this.write_pointer(
                     this.machine.cmd_line.expect("machine must be initialized"),
                     dest,
@@ -386,32 +405,32 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             "GetSystemTimeAsFileTime" | "GetSystemTimePreciseAsFileTime" => {
                 #[allow(non_snake_case)]
                 let [LPFILETIME] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 this.GetSystemTimeAsFileTime(link_name.as_str(), LPFILETIME)?;
             }
             "QueryPerformanceCounter" => {
                 #[allow(non_snake_case)]
                 let [lpPerformanceCount] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let result = this.QueryPerformanceCounter(lpPerformanceCount)?;
                 this.write_scalar(result, dest)?;
             }
             "QueryPerformanceFrequency" => {
                 #[allow(non_snake_case)]
                 let [lpFrequency] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let result = this.QueryPerformanceFrequency(lpFrequency)?;
                 this.write_scalar(result, dest)?;
             }
             "Sleep" => {
                 let [timeout] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
 
                 this.Sleep(timeout)?;
             }
             "CreateWaitableTimerExW" => {
                 let [attributes, name, flags, access] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 this.read_pointer(attributes)?;
                 this.read_pointer(name)?;
                 this.read_scalar(flags)?.to_u32()?;
@@ -425,30 +444,30 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Synchronization primitives
             "InitOnceBeginInitialize" => {
                 let [ptr, flags, pending, context] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 this.InitOnceBeginInitialize(ptr, flags, pending, context, dest)?;
             }
             "InitOnceComplete" => {
                 let [ptr, flags, context] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let result = this.InitOnceComplete(ptr, flags, context)?;
                 this.write_scalar(result, dest)?;
             }
             "WaitOnAddress" => {
                 let [ptr_op, compare_op, size_op, timeout_op] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
 
                 this.WaitOnAddress(ptr_op, compare_op, size_op, timeout_op, dest)?;
             }
             "WakeByAddressSingle" => {
                 let [ptr_op] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
 
                 this.WakeByAddressSingle(ptr_op)?;
             }
             "WakeByAddressAll" => {
                 let [ptr_op] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
 
                 this.WakeByAddressAll(ptr_op)?;
             }
@@ -457,7 +476,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             "GetProcAddress" => {
                 #[allow(non_snake_case)]
                 let [hModule, lpProcName] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 this.read_target_isize(hModule)?;
                 let name = this.read_c_str(this.read_pointer(lpProcName)?)?;
                 if let Ok(name) = str::from_utf8(name)
@@ -473,7 +492,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Threading
             "CreateThread" => {
                 let [security, stacksize, start, arg, flags, thread] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
 
                 let thread_id =
                     this.CreateThread(security, stacksize, start, arg, flags, thread)?;
@@ -482,13 +501,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             "WaitForSingleObject" => {
                 let [handle, timeout] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
 
                 let ret = this.WaitForSingleObject(handle, timeout)?;
-                this.write_scalar(Scalar::from_u32(ret), dest)?;
+                this.write_scalar(ret, dest)?;
             }
             "GetCurrentThread" => {
-                let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                let [] =
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
 
                 this.write_scalar(
                     Handle::Pseudo(PseudoHandle::CurrentThread).to_scalar(this),
@@ -497,50 +517,64 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             "SetThreadDescription" => {
                 let [handle, name] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
 
                 let handle = this.read_scalar(handle)?;
                 let name = this.read_wide_str(this.read_pointer(name)?)?;
 
-                let thread = match Handle::from_scalar(handle, this)? {
-                    Some(Handle::Thread(thread)) => thread,
-                    Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.active_thread(),
-                    _ => this.invalid_handle("SetThreadDescription")?,
+                let thread = match Handle::try_from_scalar(handle, this)? {
+                    Ok(Handle::Thread(thread)) => Ok(thread),
+                    Ok(Handle::Pseudo(PseudoHandle::CurrentThread)) => Ok(this.active_thread()),
+                    Ok(_) | Err(HandleError::InvalidHandle) =>
+                        this.invalid_handle("SetThreadDescription")?,
+                    Err(HandleError::ThreadNotFound(e)) => Err(e),
+                };
+                let res = match thread {
+                    Ok(thread) => {
+                        // FIXME: use non-lossy conversion
+                        this.set_thread_name(thread, String::from_utf16_lossy(&name).into_bytes());
+                        Scalar::from_u32(0)
+                    }
+                    Err(_) => Scalar::from_u32(STATUS_INVALID_HANDLE),
                 };
 
-                // FIXME: use non-lossy conversion
-                this.set_thread_name(thread, String::from_utf16_lossy(&name).into_bytes());
-
-                this.write_null(dest)?;
+                this.write_scalar(res, dest)?;
             }
             "GetThreadDescription" => {
                 let [handle, name_ptr] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
 
                 let handle = this.read_scalar(handle)?;
                 let name_ptr = this.deref_pointer(name_ptr)?; // the pointer where we should store the ptr to the name
 
-                let thread = match Handle::from_scalar(handle, this)? {
-                    Some(Handle::Thread(thread)) => thread,
-                    Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.active_thread(),
-                    _ => this.invalid_handle("SetThreadDescription")?,
+                let thread = match Handle::try_from_scalar(handle, this)? {
+                    Ok(Handle::Thread(thread)) => Ok(thread),
+                    Ok(Handle::Pseudo(PseudoHandle::CurrentThread)) => Ok(this.active_thread()),
+                    Ok(_) | Err(HandleError::InvalidHandle) =>
+                        this.invalid_handle("GetThreadDescription")?,
+                    Err(HandleError::ThreadNotFound(e)) => Err(e),
+                };
+                let (name, res) = match thread {
+                    Ok(thread) => {
+                        // Looks like the default thread name is empty.
+                        let name = this.get_thread_name(thread).unwrap_or(b"").to_owned();
+                        let name = this.alloc_os_str_as_wide_str(
+                            bytes_to_os_str(&name)?,
+                            MiriMemoryKind::WinLocal.into(),
+                        )?;
+                        (Scalar::from_maybe_pointer(name, this), Scalar::from_u32(0))
+                    }
+                    Err(_) => (Scalar::null_ptr(this), Scalar::from_u32(STATUS_INVALID_HANDLE)),
                 };
-                // Looks like the default thread name is empty.
-                let name = this.get_thread_name(thread).unwrap_or(b"").to_owned();
-                let name = this.alloc_os_str_as_wide_str(
-                    bytes_to_os_str(&name)?,
-                    MiriMemoryKind::WinLocal.into(),
-                )?;
-
-                this.write_scalar(Scalar::from_maybe_pointer(name, this), &name_ptr)?;
 
-                this.write_null(dest)?;
+                this.write_scalar(name, &name_ptr)?;
+                this.write_scalar(res, dest)?;
             }
 
             // Miscellaneous
             "ExitProcess" => {
                 let [code] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let code = this.read_scalar(code)?.to_u32()?;
                 throw_machine_stop!(TerminationInfo::Exit { code: code.into(), leak_check: false });
             }
@@ -548,7 +582,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // used by getrandom 0.1
                 // This is really 'RtlGenRandom'.
                 let [ptr, len] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
                 let len = this.read_scalar(len)?.to_u32()?;
                 this.gen_random(ptr, len.into())?;
@@ -557,7 +591,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             "ProcessPrng" => {
                 // used by `std`
                 let [ptr, len] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let ptr = this.read_pointer(ptr)?;
                 let len = this.read_target_usize(len)?;
                 this.gen_random(ptr, len)?;
@@ -566,7 +600,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             "BCryptGenRandom" => {
                 // used by getrandom 0.2
                 let [algorithm, ptr, len, flags] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let algorithm = this.read_scalar(algorithm)?;
                 let algorithm = algorithm.to_target_usize(this)?;
                 let ptr = this.read_pointer(ptr)?;
@@ -601,7 +635,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             "GetConsoleScreenBufferInfo" => {
                 // `term` needs this, so we fake it.
                 let [console, buffer_info] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 this.read_target_isize(console)?;
                 // FIXME: this should use deref_pointer_as, but CONSOLE_SCREEN_BUFFER_INFO is not in std
                 this.deref_pointer(buffer_info)?;
@@ -611,7 +645,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             "GetStdHandle" => {
                 let [which] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 let which = this.read_scalar(which)?.to_i32()?;
                 // We just make this the identity function, so we know later in `NtWriteFile` which
                 // one it is. This is very fake, but libtest needs it so we cannot make it a
@@ -621,15 +655,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             "CloseHandle" => {
                 let [handle] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
 
-                this.CloseHandle(handle)?;
+                let ret = this.CloseHandle(handle)?;
 
-                this.write_int(1, dest)?;
+                this.write_scalar(ret, dest)?;
             }
             "GetModuleFileNameW" => {
                 let [handle, filename, size] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 this.check_no_isolation("`GetModuleFileNameW`")?;
 
                 let handle = this.read_target_usize(handle)?;
@@ -663,7 +697,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             "FormatMessageW" => {
                 let [flags, module, message_id, language_id, buffer, size, arguments] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
 
                 let flags = this.read_scalar(flags)?.to_u32()?;
                 let _module = this.read_pointer(module)?; // seems to contain a module name
@@ -698,7 +732,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // 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.
             "GetProcessHeap" if this.frame_in_std() => {
-                let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                let [] =
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 // Just fake a HANDLE
                 // It's fine to not use the Handle type here because its a stub
                 this.write_int(1, dest)?;
@@ -706,20 +741,20 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             "GetModuleHandleA" if this.frame_in_std() => {
                 #[allow(non_snake_case)]
                 let [_lpModuleName] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 // We need to return something non-null here to make `compat_fn!` work.
                 this.write_int(1, dest)?;
             }
             "SetConsoleTextAttribute" if this.frame_in_std() => {
                 #[allow(non_snake_case)]
                 let [_hConsoleOutput, _wAttribute] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 // Pretend these does not exist / nothing happened, by returning zero.
                 this.write_null(dest)?;
             }
             "GetConsoleMode" if this.frame_in_std() => {
                 let [console, mode] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 this.read_target_isize(console)?;
                 this.deref_pointer(mode)?;
                 // Indicate an error.
@@ -728,27 +763,28 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             "GetFileType" if this.frame_in_std() => {
                 #[allow(non_snake_case)]
                 let [_hFile] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 // Return unknown file type.
                 this.write_null(dest)?;
             }
             "AddVectoredExceptionHandler" if this.frame_in_std() => {
                 #[allow(non_snake_case)]
                 let [_First, _Handler] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
                 this.write_int(1, dest)?;
             }
             "SetThreadStackGuarantee" if this.frame_in_std() => {
                 #[allow(non_snake_case)]
                 let [_StackSizeInBytes] =
-                    this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
                 // Any non zero value works for the stdlib. This is just used for stack overflows anyway.
                 this.write_int(1, dest)?;
             }
             // this is only callable from std because we know that std ignores the return value
             "SwitchToThread" if this.frame_in_std() => {
-                let [] = this.check_shim(abi, Abi::System { unwind: false }, link_name, args)?;
+                let [] =
+                    this.check_shim(abi, ExternAbi::System { unwind: false }, link_name, args)?;
 
                 this.yield_active_thread();
 
@@ -767,7 +803,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     );
                 }
                 // This function looks and behaves excatly like miri_start_unwind.
-                let [payload] = this.check_shim(abi, Abi::C { unwind: true }, link_name, args)?;
+                let [payload] =
+                    this.check_shim(abi, ExternAbi::C { unwind: true }, link_name, args)?;
                 this.handle_miri_start_unwind(payload)?;
                 return interp_ok(EmulateItemResult::NeedsUnwind);
             }
diff --git a/src/tools/miri/src/shims/windows/handle.rs b/src/tools/miri/src/shims/windows/handle.rs
index 69d78f58bed..3d872b65a63 100644
--- a/src/tools/miri/src/shims/windows/handle.rs
+++ b/src/tools/miri/src/shims/windows/handle.rs
@@ -1,7 +1,8 @@
 use std::mem::variant_count;
 
-use rustc_target::abi::HasDataLayout;
+use rustc_abi::HasDataLayout;
 
+use crate::concurrency::thread::ThreadNotFound;
 use crate::*;
 
 #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
@@ -34,6 +35,14 @@ impl PseudoHandle {
     }
 }
 
+/// Errors that can occur when constructing a [`Handle`] from a Scalar.
+pub enum HandleError {
+    /// There is no thread with the given ID.
+    ThreadNotFound(ThreadNotFound),
+    /// Can't convert scalar to handle because it is structurally invalid.
+    InvalidHandle,
+}
+
 impl Handle {
     const NULL_DISCRIMINANT: u32 = 0;
     const PSEUDO_DISCRIMINANT: u32 = 1;
@@ -63,7 +72,7 @@ impl Handle {
         let floor_log2 = variant_count.ilog2();
 
         // we need to add one for non powers of two to compensate for the difference
-        #[allow(clippy::arithmetic_side_effects)] // cannot overflow
+        #[expect(clippy::arithmetic_side_effects)] // cannot overflow
         if variant_count.is_power_of_two() { floor_log2 } else { floor_log2 + 1 }
     }
 
@@ -88,15 +97,14 @@ impl Handle {
 
         // packs the data into the lower `data_size` bits
         // and packs the discriminant right above the data
-        #[allow(clippy::arithmetic_side_effects)] // cannot overflow
-        return discriminant << data_size | data;
+        discriminant << data_size | data
     }
 
     fn new(discriminant: u32, data: u32) -> Option<Self> {
         match discriminant {
             Self::NULL_DISCRIMINANT if data == 0 => Some(Self::Null),
             Self::PSEUDO_DISCRIMINANT => Some(Self::Pseudo(PseudoHandle::from_value(data)?)),
-            Self::THREAD_DISCRIMINANT => Some(Self::Thread(data.into())),
+            Self::THREAD_DISCRIMINANT => Some(Self::Thread(ThreadId::new_unchecked(data))),
             _ => None,
         }
     }
@@ -107,11 +115,10 @@ impl Handle {
         let data_size = u32::BITS.strict_sub(disc_size);
 
         // the lower `data_size` bits of this mask are 1
-        #[allow(clippy::arithmetic_side_effects)] // cannot overflow
+        #[expect(clippy::arithmetic_side_effects)] // cannot overflow
         let data_mask = 2u32.pow(data_size) - 1;
 
         // the discriminant is stored right above the lower `data_size` bits
-        #[allow(clippy::arithmetic_side_effects)] // cannot overflow
         let discriminant = handle >> data_size;
 
         // the data is stored in the lower `data_size` bits
@@ -123,26 +130,40 @@ impl Handle {
     pub fn to_scalar(self, cx: &impl HasDataLayout) -> Scalar {
         // 64-bit handles are sign extended 32-bit handles
         // see https://docs.microsoft.com/en-us/windows/win32/winprog64/interprocess-communication
-        #[allow(clippy::cast_possible_wrap)] // we want it to wrap
+        #[expect(clippy::cast_possible_wrap)] // we want it to wrap
         let signed_handle = self.to_packed() as i32;
         Scalar::from_target_isize(signed_handle.into(), cx)
     }
 
-    pub fn from_scalar<'tcx>(
+    /// Convert a scalar into a structured `Handle`.
+    /// Structurally invalid handles return [`HandleError::InvalidHandle`].
+    /// If the handle is structurally valid but semantically invalid, e.g. a for non-existent thread
+    /// ID, returns [`HandleError::ThreadNotFound`].
+    pub fn try_from_scalar<'tcx>(
         handle: Scalar,
-        cx: &impl HasDataLayout,
-    ) -> InterpResult<'tcx, Option<Self>> {
+        cx: &MiriInterpCx<'tcx>,
+    ) -> InterpResult<'tcx, Result<Self, HandleError>> {
         let sign_extended_handle = handle.to_target_isize(cx)?;
 
-        #[allow(clippy::cast_sign_loss)] // we want to lose the sign
+        #[expect(clippy::cast_sign_loss)] // we want to lose the sign
         let handle = if let Ok(signed_handle) = i32::try_from(sign_extended_handle) {
             signed_handle as u32
         } else {
             // if a handle doesn't fit in an i32, it isn't valid.
-            return interp_ok(None);
+            return interp_ok(Err(HandleError::InvalidHandle));
         };
 
-        interp_ok(Self::from_packed(handle))
+        match Self::from_packed(handle) {
+            Some(Self::Thread(thread)) => {
+                // validate the thread id
+                match cx.machine.threads.thread_id_try_from(thread.to_u32()) {
+                    Ok(id) => interp_ok(Ok(Self::Thread(id))),
+                    Err(e) => interp_ok(Err(HandleError::ThreadNotFound(e))),
+                }
+            }
+            Some(handle) => interp_ok(Ok(handle)),
+            None => interp_ok(Err(HandleError::InvalidHandle)),
+        }
     }
 }
 
@@ -156,17 +177,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         )))
     }
 
-    fn CloseHandle(&mut self, handle_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
+    fn CloseHandle(&mut self, handle_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
         let handle = this.read_scalar(handle_op)?;
-
-        match Handle::from_scalar(handle, this)? {
-            Some(Handle::Thread(thread)) =>
-                this.detach_thread(thread, /*allow_terminated_joined*/ true)?,
+        let ret = match Handle::try_from_scalar(handle, this)? {
+            Ok(Handle::Thread(thread)) => {
+                this.detach_thread(thread, /*allow_terminated_joined*/ true)?;
+                this.eval_windows("c", "TRUE")
+            }
             _ => this.invalid_handle("CloseHandle")?,
-        }
+        };
 
-        interp_ok(())
+        interp_ok(ret)
     }
 }
diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs
index f7566a8112d..a394e0430bc 100644
--- a/src/tools/miri/src/shims/windows/sync.rs
+++ b/src/tools/miri/src/shims/windows/sync.rs
@@ -1,8 +1,9 @@
 use std::time::Duration;
 
-use rustc_target::abi::Size;
+use rustc_abi::Size;
 
 use crate::concurrency::init_once::InitOnceStatus;
+use crate::concurrency::sync::FutexRef;
 use crate::*;
 
 #[derive(Copy, Clone)]
@@ -10,15 +11,22 @@ struct WindowsInitOnce {
     id: InitOnceId,
 }
 
+struct WindowsFutex {
+    futex: FutexRef,
+}
+
 impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
 trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
     // Windows sync primitives are pointer sized.
     // We only use the first 4 bytes for the id.
 
-    fn init_once_get_data(
-        &mut self,
+    fn init_once_get_data<'a>(
+        &'a mut self,
         init_once_ptr: &OpTy<'tcx>,
-    ) -> InterpResult<'tcx, WindowsInitOnce> {
+    ) -> InterpResult<'tcx, &'a WindowsInitOnce>
+    where
+        'tcx: 'a,
+    {
         let this = self.eval_context_mut();
 
         let init_once = this.deref_pointer(init_once_ptr)?;
@@ -168,8 +176,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         let size = this.read_target_usize(size_op)?;
         let timeout_ms = this.read_scalar(timeout_op)?.to_u32()?;
 
-        let addr = ptr.addr().bytes();
-
         if size > 8 || !size.is_power_of_two() {
             let invalid_param = this.eval_windows("c", "ERROR_INVALID_PARAMETER");
             this.set_last_error(invalid_param)?;
@@ -190,19 +196,27 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         let layout = this.machine.layouts.uint(size).unwrap();
         let futex_val =
-            this.read_scalar_atomic(&this.ptr_to_mplace(ptr, layout), AtomicReadOrd::Relaxed)?;
+            this.read_scalar_atomic(&this.ptr_to_mplace(ptr, layout), AtomicReadOrd::Acquire)?;
         let compare_val = this.read_scalar(&this.ptr_to_mplace(compare, layout))?;
 
         if futex_val == compare_val {
             // If the values are the same, we have to block.
+
+            // This cannot fail since we already did an atomic acquire read on that pointer.
+            let futex_ref = this
+                .get_sync_or_init(ptr, |_| WindowsFutex { futex: Default::default() })
+                .unwrap()
+                .futex
+                .clone();
+
             this.futex_wait(
-                addr,
+                futex_ref,
                 u32::MAX, // bitset
                 timeout,
                 Scalar::from_i32(1), // retval_succ
                 Scalar::from_i32(0), // retval_timeout
                 dest.clone(),
-                this.eval_windows("c", "ERROR_TIMEOUT"), // errno_timeout
+                IoError::WindowsError("ERROR_TIMEOUT"), // errno_timeout
             );
         }
 
@@ -219,8 +233,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // See the Linux futex implementation for why this fence exists.
         this.atomic_fence(AtomicFenceOrd::SeqCst)?;
 
-        let addr = ptr.addr().bytes();
-        this.futex_wake(addr, u32::MAX)?;
+        let Some(futex_ref) =
+            this.get_sync_or_init(ptr, |_| WindowsFutex { futex: Default::default() })
+        else {
+            // Seems like this cannot return an error, so we just wake nobody.
+            return interp_ok(());
+        };
+        let futex_ref = futex_ref.futex.clone();
+
+        this.futex_wake(&futex_ref, u32::MAX)?;
 
         interp_ok(())
     }
@@ -232,8 +253,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // See the Linux futex implementation for why this fence exists.
         this.atomic_fence(AtomicFenceOrd::SeqCst)?;
 
-        let addr = ptr.addr().bytes();
-        while this.futex_wake(addr, u32::MAX)? {}
+        let Some(futex_ref) =
+            this.get_sync_or_init(ptr, |_| WindowsFutex { futex: Default::default() })
+        else {
+            // Seems like this cannot return an error, so we just wake nobody.
+            return interp_ok(());
+        };
+        let futex_ref = futex_ref.futex.clone();
+
+        while this.futex_wake(&futex_ref, u32::MAX)? {}
 
         interp_ok(())
     }
diff --git a/src/tools/miri/src/shims/windows/thread.rs b/src/tools/miri/src/shims/windows/thread.rs
index a920abea3a8..efc1c2286bc 100644
--- a/src/tools/miri/src/shims/windows/thread.rs
+++ b/src/tools/miri/src/shims/windows/thread.rs
@@ -1,5 +1,5 @@
+use rustc_abi::ExternAbi;
 use rustc_middle::ty::layout::LayoutOf;
-use rustc_target::spec::abi::Abi;
 
 use self::shims::windows::handle::{EvalContextExt as _, Handle, PseudoHandle};
 use crate::*;
@@ -49,7 +49,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         this.start_regular_thread(
             thread,
             start_routine,
-            Abi::System { unwind: false },
+            ExternAbi::System { unwind: false },
             func_arg,
             this.layout_of(this.tcx.types.u32)?,
         )
@@ -59,17 +59,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         &mut self,
         handle_op: &OpTy<'tcx>,
         timeout_op: &OpTy<'tcx>,
-    ) -> InterpResult<'tcx, u32> {
+    ) -> InterpResult<'tcx, Scalar> {
         let this = self.eval_context_mut();
 
         let handle = this.read_scalar(handle_op)?;
         let timeout = this.read_scalar(timeout_op)?.to_u32()?;
 
-        let thread = match Handle::from_scalar(handle, this)? {
-            Some(Handle::Thread(thread)) => thread,
+        let thread = match Handle::try_from_scalar(handle, this)? {
+            Ok(Handle::Thread(thread)) => thread,
             // Unlike on posix, the outcome of joining the current thread is not documented.
             // On current Windows, it just deadlocks.
-            Some(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.active_thread(),
+            Ok(Handle::Pseudo(PseudoHandle::CurrentThread)) => this.active_thread(),
             _ => this.invalid_handle("WaitForSingleObject")?,
         };
 
@@ -79,6 +79,6 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 
         this.join_thread(thread)?;
 
-        interp_ok(0)
+        interp_ok(this.eval_windows("c", "WAIT_OBJECT_0"))
     }
 }
diff --git a/src/tools/miri/src/shims/x86/aesni.rs b/src/tools/miri/src/shims/x86/aesni.rs
index 3a8138654fd..4c6c1cefeb1 100644
--- a/src/tools/miri/src/shims/x86/aesni.rs
+++ b/src/tools/miri/src/shims/x86/aesni.rs
@@ -1,7 +1,7 @@
+use rustc_abi::ExternAbi;
 use rustc_middle::ty::Ty;
 use rustc_middle::ty::layout::LayoutOf as _;
 use rustc_span::Symbol;
-use rustc_target::spec::abi::Abi;
 
 use crate::*;
 
@@ -10,7 +10,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_x86_aesni_intrinsic(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -27,7 +27,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdec_si128
             "aesdec" | "aesdec.256" | "aesdec.512" => {
                 let [state, key] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 aes_round(this, state, key, dest, |state, key| {
                     let key = aes::Block::from(key.to_le_bytes());
@@ -45,7 +45,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesdeclast_si128
             "aesdeclast" | "aesdeclast.256" | "aesdeclast.512" => {
                 let [state, key] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 aes_round(this, state, key, dest, |state, key| {
                     let mut state = aes::Block::from(state.to_le_bytes());
@@ -70,7 +70,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenc_si128
             "aesenc" | "aesenc.256" | "aesenc.512" => {
                 let [state, key] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 aes_round(this, state, key, dest, |state, key| {
                     let key = aes::Block::from(key.to_le_bytes());
@@ -88,7 +88,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_aesenclast_si128
             "aesenclast" | "aesenclast.256" | "aesenclast.512" => {
                 let [state, key] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 aes_round(this, state, key, dest, |state, key| {
                     let mut state = aes::Block::from(state.to_le_bytes());
@@ -109,7 +109,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Used to implement the _mm_aesimc_si128 function.
             // Performs the AES InvMixColumns operation on `op`
             "aesimc" => {
-                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [op] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 // Transmute to `u128`
                 let op = op.transmute(this.machine.layouts.u128, this)?;
@@ -132,7 +132,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 // Performs an AES round (given by `f`) on each 128-bit word of
 // `state` with the corresponding 128-bit key of `key`.
 fn aes_round<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     state: &OpTy<'tcx>,
     key: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
@@ -145,21 +145,20 @@ fn aes_round<'tcx>(
     assert_eq!(dest.layout.size.bytes() % 16, 0);
     let len = dest.layout.size.bytes() / 16;
 
-    let u128_array_layout =
-        this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u128, len))?;
+    let u128_array_layout = ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u128, len))?;
 
-    let state = state.transmute(u128_array_layout, this)?;
-    let key = key.transmute(u128_array_layout, this)?;
-    let dest = dest.transmute(u128_array_layout, this)?;
+    let state = state.transmute(u128_array_layout, ecx)?;
+    let key = key.transmute(u128_array_layout, ecx)?;
+    let dest = dest.transmute(u128_array_layout, ecx)?;
 
     for i in 0..len {
-        let state = this.read_scalar(&this.project_index(&state, i)?)?.to_u128()?;
-        let key = this.read_scalar(&this.project_index(&key, i)?)?.to_u128()?;
-        let dest = this.project_index(&dest, i)?;
+        let state = ecx.read_scalar(&ecx.project_index(&state, i)?)?.to_u128()?;
+        let key = ecx.read_scalar(&ecx.project_index(&key, i)?)?.to_u128()?;
+        let dest = ecx.project_index(&dest, i)?;
 
         let res = f(state, key);
 
-        this.write_scalar(Scalar::from_u128(res), &dest)?;
+        ecx.write_scalar(Scalar::from_u128(res), &dest)?;
     }
 
     interp_ok(())
diff --git a/src/tools/miri/src/shims/x86/avx.rs b/src/tools/miri/src/shims/x86/avx.rs
index 080e7c8ce0b..3971fa3b913 100644
--- a/src/tools/miri/src/shims/x86/avx.rs
+++ b/src/tools/miri/src/shims/x86/avx.rs
@@ -1,9 +1,9 @@
+use rustc_abi::ExternAbi;
 use rustc_apfloat::ieee::{Double, Single};
 use rustc_middle::mir;
 use rustc_middle::ty::Ty;
 use rustc_middle::ty::layout::LayoutOf as _;
 use rustc_span::Symbol;
-use rustc_target::spec::abi::Abi;
 
 use super::{
     FloatBinOp, FloatUnaryOp, bin_op_simd_float_all, conditional_dot_product, convert_float_to_int,
@@ -17,7 +17,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_x86_avx_intrinsic(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -34,7 +34,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // semantics.
             "min.ps.256" | "max.ps.256" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which = match unprefixed_name {
                     "min.ps.256" => FloatBinOp::Min,
@@ -47,7 +47,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Used to implement _mm256_min_pd and _mm256_max_pd functions.
             "min.pd.256" | "max.pd.256" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which = match unprefixed_name {
                     "min.pd.256" => FloatBinOp::Min,
@@ -61,7 +61,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Rounds the elements of `op` according to `rounding`.
             "round.ps.256" => {
                 let [op, rounding] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 round_all::<rustc_apfloat::ieee::Single>(this, op, rounding, dest)?;
             }
@@ -69,14 +69,14 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Rounds the elements of `op` according to `rounding`.
             "round.pd.256" => {
                 let [op, rounding] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 round_all::<rustc_apfloat::ieee::Double>(this, op, rounding, dest)?;
             }
             // Used to implement _mm256_{rcp,rsqrt}_ps functions.
             // Performs the operations on all components of `op`.
             "rcp.ps.256" | "rsqrt.ps.256" => {
-                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [op] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which = match unprefixed_name {
                     "rcp.ps.256" => FloatUnaryOp::Rcp,
@@ -89,7 +89,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Used to implement the _mm256_dp_ps function.
             "dp.ps.256" => {
                 let [left, right, imm] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 conditional_dot_product(this, left, right, imm, dest)?;
             }
@@ -98,7 +98,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // in `left` and `right`.
             "hadd.ps.256" | "hadd.pd.256" | "hsub.ps.256" | "hsub.pd.256" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which = match unprefixed_name {
                     "hadd.ps.256" | "hadd.pd.256" => mir::BinOp::Add,
@@ -114,7 +114,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // if true.
             "cmp.ps.256" => {
                 let [left, right, imm] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which =
                     FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
@@ -127,7 +127,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // if true.
             "cmp.pd.256" => {
                 let [left, right, imm] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which =
                     FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
@@ -138,7 +138,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // and _mm256_cvttpd_epi32 functions.
             // Converts packed f32/f64 to packed i32.
             "cvt.ps2dq.256" | "cvtt.ps2dq.256" | "cvt.pd2dq.256" | "cvtt.pd2dq.256" => {
-                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [op] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let rnd = match unprefixed_name {
                     // "current SSE rounding mode", assume nearest
@@ -157,7 +157,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // `control` determines which element of the current `data` array is written.
             "vpermilvar.ps" | "vpermilvar.ps.256" => {
                 let [data, control] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (data, data_len) = this.project_to_simd(data)?;
                 let (control, control_len) = this.project_to_simd(control)?;
@@ -191,7 +191,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // written.
             "vpermilvar.pd" | "vpermilvar.pd.256" => {
                 let [data, control] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (data, data_len) = this.project_to_simd(data)?;
                 let (control, control_len) = this.project_to_simd(control)?;
@@ -224,7 +224,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // zero, according to `imm`.
             "vperm2f128.ps.256" | "vperm2f128.pd.256" | "vperm2f128.si.256" => {
                 let [left, right, imm] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 assert_eq!(dest.layout, left.layout);
                 assert_eq!(dest.layout, right.layout);
@@ -268,7 +268,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // loaded.
             "maskload.ps" | "maskload.pd" | "maskload.ps.256" | "maskload.pd.256" => {
                 let [ptr, mask] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 mask_load(this, ptr, mask, dest)?;
             }
@@ -279,7 +279,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Unlike SSE2's _mm_maskmoveu_si128, these are not non-temporal stores.
             "maskstore.ps" | "maskstore.pd" | "maskstore.ps.256" | "maskstore.pd.256" => {
                 let [ptr, mask, value] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 mask_store(this, ptr, mask, value)?;
             }
@@ -289,7 +289,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // the data crosses a cache line, but for Miri this is just a regular
             // unaligned read.
             "ldu.dq.256" => {
-                let [src_ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [src_ptr] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let src_ptr = this.read_pointer(src_ptr)?;
                 let dest = dest.force_mplace(this)?;
 
@@ -301,7 +302,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Tests `op & mask == 0`, `op & mask == mask` or
             // `op & mask != 0 && op & mask != mask`
             "ptestz.256" | "ptestc.256" | "ptestnzc.256" => {
-                let [op, mask] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [op, mask] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (all_zero, masked_set) = test_bits_masked(this, op, mask)?;
                 let res = match unprefixed_name {
@@ -324,7 +326,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             "vtestz.pd.256" | "vtestc.pd.256" | "vtestnzc.pd.256" | "vtestz.pd" | "vtestc.pd"
             | "vtestnzc.pd" | "vtestz.ps.256" | "vtestc.ps.256" | "vtestnzc.ps.256"
             | "vtestz.ps" | "vtestc.ps" | "vtestnzc.ps" => {
-                let [op, mask] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [op, mask] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (direct, negated) = test_high_bits_masked(this, op, mask)?;
                 let res = match unprefixed_name {
@@ -346,7 +349,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 // compiler, making these functions no-ops.
 
                 // The only thing that needs to be ensured is the correct calling convention.
-                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
             }
             _ => return interp_ok(EmulateItemResult::NotSupported),
         }
diff --git a/src/tools/miri/src/shims/x86/avx2.rs b/src/tools/miri/src/shims/x86/avx2.rs
index 331b693a3e4..6aefb87d4d0 100644
--- a/src/tools/miri/src/shims/x86/avx2.rs
+++ b/src/tools/miri/src/shims/x86/avx2.rs
@@ -1,8 +1,8 @@
+use rustc_abi::ExternAbi;
 use rustc_middle::mir;
 use rustc_middle::ty::Ty;
 use rustc_middle::ty::layout::LayoutOf as _;
 use rustc_span::Symbol;
-use rustc_target::spec::abi::Abi;
 
 use super::{
     ShiftOp, horizontal_bin_op, int_abs, mask_load, mask_store, mpsadbw, packssdw, packsswb,
@@ -15,7 +15,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_x86_avx2_intrinsic(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -28,7 +28,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Used to implement the _mm256_abs_epi{8,16,32} functions.
             // Calculates the absolute value of packed 8/16/32-bit integers.
             "pabs.b" | "pabs.w" | "pabs.d" => {
-                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [op] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 int_abs(this, op, dest)?;
             }
@@ -37,7 +37,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // integer values in `left` and `right`.
             "phadd.w" | "phadd.sw" | "phadd.d" | "phsub.w" | "phsub.sw" | "phsub.d" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (which, saturating) = match unprefixed_name {
                     "phadd.w" | "phadd.d" => (mir::BinOp::Add, false),
@@ -58,7 +58,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             | "gather.d.pd.256" | "gather.q.pd" | "gather.q.pd.256" | "gather.d.ps"
             | "gather.d.ps.256" | "gather.q.ps" | "gather.q.ps.256" => {
                 let [src, slice, offsets, mask, scale] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 assert_eq!(dest.layout, src.layout);
 
@@ -116,7 +116,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // intermediate 32-bit integers, and pack the results in `dest`.
             "pmadd.wd" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (left, left_len) = this.project_to_simd(left)?;
                 let (right, right_len) = this.project_to_simd(right)?;
@@ -153,7 +153,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // produces the output at index `i`.
             "pmadd.ub.sw" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (left, left_len) = this.project_to_simd(left)?;
                 let (right, right_len) = this.project_to_simd(right)?;
@@ -188,7 +188,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // loaded.
             "maskload.d" | "maskload.q" | "maskload.d.256" | "maskload.q.256" => {
                 let [ptr, mask] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 mask_load(this, ptr, mask, dest)?;
             }
@@ -199,7 +199,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Unlike SSE2's _mm_maskmoveu_si128, these are not non-temporal stores.
             "maskstore.d" | "maskstore.q" | "maskstore.d.256" | "maskstore.q.256" => {
                 let [ptr, mask, value] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 mask_store(this, ptr, mask, value)?;
             }
@@ -211,7 +211,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mpsadbw_epu8
             "mpsadbw" => {
                 let [left, right, imm] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 mpsadbw(this, left, right, imm, dest)?;
             }
@@ -223,7 +223,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mulhrs_epi16
             "pmul.hr.sw" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 pmulhrsw(this, left, right, dest)?;
             }
@@ -232,7 +232,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // vector with signed saturation.
             "packsswb" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 packsswb(this, left, right, dest)?;
             }
@@ -241,7 +241,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // vector with signed saturation.
             "packssdw" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 packssdw(this, left, right, dest)?;
             }
@@ -250,7 +250,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // unsigned integer vector with saturation.
             "packuswb" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 packuswb(this, left, right, dest)?;
             }
@@ -259,7 +259,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // the result to a 16-bit unsigned integer vector with saturation.
             "packusdw" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 packusdw(this, left, right, dest)?;
             }
@@ -269,7 +269,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // as indices.
             "permd" | "permps" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (left, left_len) = this.project_to_simd(left)?;
                 let (right, right_len) = this.project_to_simd(right)?;
@@ -290,7 +290,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Shuffles 128-bit blocks of `a` and `b` using `imm` as pattern.
             "vperm2i128" => {
                 let [left, right, imm] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 assert_eq!(left.layout.size.bits(), 256);
                 assert_eq!(right.layout.size.bits(), 256);
@@ -328,7 +328,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_sad_epu8
             "psad.bw" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (left, left_len) = this.project_to_simd(left)?;
                 let (right, right_len) = this.project_to_simd(right)?;
@@ -361,7 +361,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Each 128-bit block is shuffled independently.
             "pshuf.b" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (left, left_len) = this.project_to_simd(left)?;
                 let (right, right_len) = this.project_to_simd(right)?;
@@ -393,7 +393,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Basically, we multiply `left` with `right.signum()`.
             "psign.b" | "psign.w" | "psign.d" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 psign(this, left, right, dest)?;
             }
@@ -408,7 +408,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             "psll.w" | "psrl.w" | "psra.w" | "psll.d" | "psrl.d" | "psra.d" | "psll.q"
             | "psrl.q" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which = match unprefixed_name {
                     "psll.w" | "psll.d" | "psll.q" => ShiftOp::Left,
@@ -424,7 +424,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             "psllv.d" | "psllv.d.256" | "psllv.q" | "psllv.q.256" | "psrlv.d" | "psrlv.d.256"
             | "psrlv.q" | "psrlv.q.256" | "psrav.d" | "psrav.d.256" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which = match unprefixed_name {
                     "psllv.d" | "psllv.d.256" | "psllv.q" | "psllv.q.256" => ShiftOp::Left,
diff --git a/src/tools/miri/src/shims/x86/bmi.rs b/src/tools/miri/src/shims/x86/bmi.rs
index 59e76abf179..0f9e8d6f025 100644
--- a/src/tools/miri/src/shims/x86/bmi.rs
+++ b/src/tools/miri/src/shims/x86/bmi.rs
@@ -1,5 +1,5 @@
+use rustc_abi::ExternAbi;
 use rustc_span::Symbol;
-use rustc_target::spec::abi::Abi;
 
 use crate::*;
 
@@ -8,7 +8,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_x86_bmi_intrinsic(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -33,7 +33,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             return interp_ok(EmulateItemResult::NotSupported);
         }
 
-        let [left, right] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+        let [left, right] =
+            this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
         let left = this.read_scalar(left)?;
         let right = this.read_scalar(right)?;
 
diff --git a/src/tools/miri/src/shims/x86/gfni.rs b/src/tools/miri/src/shims/x86/gfni.rs
index c91b8c835f2..92010345f55 100644
--- a/src/tools/miri/src/shims/x86/gfni.rs
+++ b/src/tools/miri/src/shims/x86/gfni.rs
@@ -1,5 +1,5 @@
+use rustc_abi::ExternAbi;
 use rustc_span::Symbol;
-use rustc_target::spec::abi::Abi;
 
 use crate::*;
 
@@ -8,7 +8,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_x86_gfni_intrinsic(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -30,7 +30,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8affine_
             "vgf2p8affineqb.128" | "vgf2p8affineqb.256" | "vgf2p8affineqb.512" => {
                 let [left, right, imm8] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 affine_transform(this, left, right, imm8, dest, /* inverse */ false)?;
             }
             // Used to implement the `_mm{, 256, 512}_gf2p8affineinv_epi64_epi8` functions.
@@ -38,7 +38,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8affineinv
             "vgf2p8affineinvqb.128" | "vgf2p8affineinvqb.256" | "vgf2p8affineinvqb.512" => {
                 let [left, right, imm8] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 affine_transform(this, left, right, imm8, dest, /* inverse */ true)?;
             }
             // Used to implement the `_mm{, 256, 512}_gf2p8mul_epi8` functions.
@@ -48,7 +48,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8mul
             "vgf2p8mulb.128" | "vgf2p8mulb.256" | "vgf2p8mulb.512" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (left, left_len) = this.project_to_simd(left)?;
                 let (right, right_len) = this.project_to_simd(right)?;
@@ -75,21 +75,21 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
 /// If `inverse` is set, then the inverse transformation with respect to the reduction polynomial
 /// x^8 + x^4 + x^3 + x + 1 is performed instead.
 fn affine_transform<'tcx>(
-    this: &mut MiriInterpCx<'tcx>,
+    ecx: &mut MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     imm8: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
     inverse: bool,
 ) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.project_to_simd(left)?;
-    let (right, right_len) = this.project_to_simd(right)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (left, left_len) = ecx.project_to_simd(left)?;
+    let (right, right_len) = ecx.project_to_simd(right)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, right_len);
     assert_eq!(dest_len, left_len);
 
-    let imm8 = this.read_scalar(imm8)?.to_u8()?;
+    let imm8 = ecx.read_scalar(imm8)?.to_u8()?;
 
     // Each 8x8 bit matrix gets multiplied with eight bit vectors.
     // Therefore, the iteration is done in chunks of eight.
@@ -98,13 +98,13 @@ fn affine_transform<'tcx>(
         let mut matrix = [0u8; 8];
         for j in 0..8 {
             matrix[usize::try_from(j).unwrap()] =
-                this.read_scalar(&this.project_index(&right, i.wrapping_add(j))?)?.to_u8()?;
+                ecx.read_scalar(&ecx.project_index(&right, i.wrapping_add(j))?)?.to_u8()?;
         }
 
         // Multiply the matrix with the vector and perform the addition.
         for j in 0..8 {
             let index = i.wrapping_add(j);
-            let left = this.read_scalar(&this.project_index(&left, index)?)?.to_u8()?;
+            let left = ecx.read_scalar(&ecx.project_index(&left, index)?)?.to_u8()?;
             let left = if inverse { TABLE[usize::from(left)] } else { left };
 
             let mut res = 0;
@@ -124,8 +124,8 @@ fn affine_transform<'tcx>(
             // Perform the addition.
             res ^= imm8;
 
-            let dest = this.project_index(&dest, index)?;
-            this.write_scalar(Scalar::from_u8(res), &dest)?;
+            let dest = ecx.project_index(&dest, index)?;
+            ecx.write_scalar(Scalar::from_u8(res), &dest)?;
         }
     }
 
@@ -136,7 +136,7 @@ fn affine_transform<'tcx>(
 // This is a evaluated at compile time. Trait based conversion is not available.
 /// See <https://www.corsix.org/content/galois-field-instructions-2021-cpus> for the
 /// definition of `gf_inv` which was used for the creation of this table.
-#[allow(clippy::cast_possible_truncation)]
+#[expect(clippy::cast_possible_truncation)]
 static TABLE: [u8; 256] = {
     let mut array = [0; 256];
 
@@ -163,7 +163,7 @@ static TABLE: [u8; 256] = {
 /// polynomial representation with the reduction polynomial x^8 + x^4 + x^3 + x + 1.
 /// See <https://www.corsix.org/content/galois-field-instructions-2021-cpus> for details.
 // This is a const function. Trait based conversion is not available.
-#[allow(clippy::cast_possible_truncation)]
+#[expect(clippy::cast_possible_truncation)]
 const fn gf2p8_mul(left: u8, right: u8) -> u8 {
     // This implementation is based on the `gf2p8mul_byte` definition found inside the Intel intrinsics guide.
     // See https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=gf2p8mul
diff --git a/src/tools/miri/src/shims/x86/mod.rs b/src/tools/miri/src/shims/x86/mod.rs
index 9339d301aee..3e02a4b3637 100644
--- a/src/tools/miri/src/shims/x86/mod.rs
+++ b/src/tools/miri/src/shims/x86/mod.rs
@@ -1,12 +1,10 @@
-use rand::Rng as _;
+use rustc_abi::{ExternAbi, Size};
 use rustc_apfloat::Float;
 use rustc_apfloat::ieee::Single;
 use rustc_middle::ty::Ty;
 use rustc_middle::ty::layout::LayoutOf as _;
 use rustc_middle::{mir, ty};
 use rustc_span::Symbol;
-use rustc_target::abi::Size;
-use rustc_target::spec::abi::Abi;
 
 use self::helpers::bool_to_simd_element;
 use crate::*;
@@ -29,7 +27,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_x86_intrinsic(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -47,7 +45,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     return interp_ok(EmulateItemResult::NotSupported);
                 }
 
-                let [cb_in, a, b] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?;
+                let [cb_in, a, b] = this.check_shim(abi, ExternAbi::Unadjusted, link_name, args)?;
 
                 let op = if unprefixed_name.starts_with("add") {
                     mir::BinOp::AddWithOverflow
@@ -71,7 +69,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                     return interp_ok(EmulateItemResult::NotSupported);
                 }
 
-                let [c_in, a, b, out] = this.check_shim(abi, Abi::Unadjusted, link_name, args)?;
+                let [c_in, a, b, out] =
+                    this.check_shim(abi, ExternAbi::Unadjusted, link_name, args)?;
                 let out = this.deref_pointer_as(
                     out,
                     if is_u64 { this.machine.layouts.u64 } else { this.machine.layouts.u32 },
@@ -88,18 +87,29 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // the instruction behaves like a no-op, so it is always safe to call the
             // intrinsic.
             "sse2.pause" => {
-                let [] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 // Only exhibit the spin-loop hint behavior when SSE2 is enabled.
                 if this.tcx.sess.unstable_target_features.contains(&Symbol::intern("sse2")) {
                     this.yield_active_thread();
                 }
             }
 
-            "pclmulqdq" => {
+            "pclmulqdq" | "pclmulqdq.256" | "pclmulqdq.512" => {
+                let mut len = 2; // in units of 64bits
+                this.expect_target_feature_for_intrinsic(link_name, "pclmulqdq")?;
+                if unprefixed_name.ends_with(".256") {
+                    this.expect_target_feature_for_intrinsic(link_name, "vpclmulqdq")?;
+                    len = 4;
+                } else if unprefixed_name.ends_with(".512") {
+                    this.expect_target_feature_for_intrinsic(link_name, "vpclmulqdq")?;
+                    this.expect_target_feature_for_intrinsic(link_name, "avx512f")?;
+                    len = 8;
+                }
+
                 let [left, right, imm] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
-                pclmulqdq(this, left, right, imm, dest)?;
+                pclmulqdq(this, left, right, imm, dest, len)?;
             }
 
             name if name.starts_with("bmi.") => {
@@ -215,7 +225,7 @@ impl FloatBinOp {
     /// Convert from the `imm` argument used to specify the comparison
     /// operation in intrinsics such as `llvm.x86.sse.cmp.ss`.
     fn cmp_from_imm<'tcx>(
-        this: &crate::MiriInterpCx<'tcx>,
+        ecx: &crate::MiriInterpCx<'tcx>,
         imm: i8,
         intrinsic: Symbol,
     ) -> InterpResult<'tcx, Self> {
@@ -249,7 +259,7 @@ impl FloatBinOp {
         };
         // When bit 3 is 1 (only possible in AVX), unord is toggled.
         if imm & 0b1000 != 0 {
-            this.expect_target_feature_for_intrinsic(intrinsic, "avx")?;
+            ecx.expect_target_feature_for_intrinsic(intrinsic, "avx")?;
             unord = !unord;
         }
         interp_ok(Self::Cmp { gt, lt, eq, unord })
@@ -316,28 +326,28 @@ fn bin_op_float<'tcx, F: rustc_apfloat::Float>(
 /// Performs `which` operation on the first component of `left` and `right`
 /// and copies the other components from `left`. The result is stored in `dest`.
 fn bin_op_simd_float_first<'tcx, F: rustc_apfloat::Float>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     which: FloatBinOp,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.project_to_simd(left)?;
-    let (right, right_len) = this.project_to_simd(right)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (left, left_len) = ecx.project_to_simd(left)?;
+    let (right, right_len) = ecx.project_to_simd(right)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, left_len);
     assert_eq!(dest_len, right_len);
 
     let res0 = bin_op_float::<F>(
         which,
-        &this.read_immediate(&this.project_index(&left, 0)?)?,
-        &this.read_immediate(&this.project_index(&right, 0)?)?,
+        &ecx.read_immediate(&ecx.project_index(&left, 0)?)?,
+        &ecx.read_immediate(&ecx.project_index(&right, 0)?)?,
     )?;
-    this.write_scalar(res0, &this.project_index(&dest, 0)?)?;
+    ecx.write_scalar(res0, &ecx.project_index(&dest, 0)?)?;
 
     for i in 1..dest_len {
-        this.copy_op(&this.project_index(&left, i)?, &this.project_index(&dest, i)?)?;
+        ecx.copy_op(&ecx.project_index(&left, i)?, &ecx.project_index(&dest, i)?)?;
     }
 
     interp_ok(())
@@ -346,26 +356,26 @@ fn bin_op_simd_float_first<'tcx, F: rustc_apfloat::Float>(
 /// Performs `which` operation on each component of `left` and
 /// `right`, storing the result is stored in `dest`.
 fn bin_op_simd_float_all<'tcx, F: rustc_apfloat::Float>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     which: FloatBinOp,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.project_to_simd(left)?;
-    let (right, right_len) = this.project_to_simd(right)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (left, left_len) = ecx.project_to_simd(left)?;
+    let (right, right_len) = ecx.project_to_simd(right)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, left_len);
     assert_eq!(dest_len, right_len);
 
     for i in 0..dest_len {
-        let left = this.read_immediate(&this.project_index(&left, i)?)?;
-        let right = this.read_immediate(&this.project_index(&right, i)?)?;
-        let dest = this.project_index(&dest, i)?;
+        let left = ecx.read_immediate(&ecx.project_index(&left, i)?)?;
+        let right = ecx.read_immediate(&ecx.project_index(&right, i)?)?;
+        let dest = ecx.project_index(&dest, i)?;
 
         let res = bin_op_float::<F>(which, &left, &right)?;
-        this.write_scalar(res, &dest)?;
+        ecx.write_scalar(res, &dest)?;
     }
 
     interp_ok(())
@@ -386,9 +396,8 @@ enum FloatUnaryOp {
 }
 
 /// Performs `which` scalar operation on `op` and returns the result.
-#[allow(clippy::arithmetic_side_effects)] // floating point operations without side effects
 fn unary_op_f32<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     which: FloatUnaryOp,
     op: &ImmTy<'tcx>,
 ) -> InterpResult<'tcx, Scalar> {
@@ -398,56 +407,38 @@ fn unary_op_f32<'tcx>(
             let div = (Single::from_u128(1).value / op).value;
             // Apply a relative error with a magnitude on the order of 2^-12 to simulate the
             // inaccuracy of RCP.
-            let res = apply_random_float_error(this, div, -12);
+            let res = math::apply_random_float_error(ecx, div, -12);
             interp_ok(Scalar::from_f32(res))
         }
         FloatUnaryOp::Rsqrt => {
-            let op = op.to_scalar().to_u32()?;
-            // FIXME using host floats
-            let sqrt = Single::from_bits(f32::from_bits(op).sqrt().to_bits().into());
-            let rsqrt = (Single::from_u128(1).value / sqrt).value;
+            let op = op.to_scalar().to_f32()?;
+            let rsqrt = (Single::from_u128(1).value / math::sqrt(op)).value;
             // Apply a relative error with a magnitude on the order of 2^-12 to simulate the
             // inaccuracy of RSQRT.
-            let res = apply_random_float_error(this, rsqrt, -12);
+            let res = math::apply_random_float_error(ecx, rsqrt, -12);
             interp_ok(Scalar::from_f32(res))
         }
     }
 }
 
-/// Disturbes a floating-point result by a relative error on the order of (-2^scale, 2^scale).
-#[allow(clippy::arithmetic_side_effects)] // floating point arithmetic cannot panic
-fn apply_random_float_error<F: rustc_apfloat::Float>(
-    this: &mut crate::MiriInterpCx<'_>,
-    val: F,
-    err_scale: i32,
-) -> F {
-    let rng = this.machine.rng.get_mut();
-    // generates rand(0, 2^64) * 2^(scale - 64) = rand(0, 1) * 2^scale
-    let err = F::from_u128(rng.gen::<u64>().into()).value.scalbn(err_scale.strict_sub(64));
-    // give it a random sign
-    let err = if rng.gen::<bool>() { -err } else { err };
-    // multiple the value with (1+err)
-    (val * (F::from_u128(1).value + err).value).value
-}
-
 /// Performs `which` operation on the first component of `op` and copies
 /// the other components. The result is stored in `dest`.
 fn unary_op_ss<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     which: FloatUnaryOp,
     op: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (op, op_len) = this.project_to_simd(op)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (op, op_len) = ecx.project_to_simd(op)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, op_len);
 
-    let res0 = unary_op_f32(this, which, &this.read_immediate(&this.project_index(&op, 0)?)?)?;
-    this.write_scalar(res0, &this.project_index(&dest, 0)?)?;
+    let res0 = unary_op_f32(ecx, which, &ecx.read_immediate(&ecx.project_index(&op, 0)?)?)?;
+    ecx.write_scalar(res0, &ecx.project_index(&dest, 0)?)?;
 
     for i in 1..dest_len {
-        this.copy_op(&this.project_index(&op, i)?, &this.project_index(&dest, i)?)?;
+        ecx.copy_op(&ecx.project_index(&op, i)?, &ecx.project_index(&dest, i)?)?;
     }
 
     interp_ok(())
@@ -456,22 +447,22 @@ fn unary_op_ss<'tcx>(
 /// Performs `which` operation on each component of `op`, storing the
 /// result is stored in `dest`.
 fn unary_op_ps<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     which: FloatUnaryOp,
     op: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (op, op_len) = this.project_to_simd(op)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (op, op_len) = ecx.project_to_simd(op)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, op_len);
 
     for i in 0..dest_len {
-        let op = this.read_immediate(&this.project_index(&op, i)?)?;
-        let dest = this.project_index(&dest, i)?;
+        let op = ecx.read_immediate(&ecx.project_index(&op, i)?)?;
+        let dest = ecx.project_index(&dest, i)?;
 
-        let res = unary_op_f32(this, which, &op)?;
-        this.write_scalar(res, &dest)?;
+        let res = unary_op_f32(ecx, which, &op)?;
+        ecx.write_scalar(res, &dest)?;
     }
 
     interp_ok(())
@@ -493,14 +484,14 @@ enum ShiftOp {
 /// For arithmetic right-shifts, when right is larger than BITS - 1, the sign
 /// bit is copied to all bits.
 fn shift_simd_by_scalar<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     which: ShiftOp,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.project_to_simd(left)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (left, left_len) = ecx.project_to_simd(left)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, left_len);
     // `right` may have a different length, and we only care about its
@@ -510,11 +501,11 @@ fn shift_simd_by_scalar<'tcx>(
     // by checked_{shl,shr} (u32).
     // It is ok to saturate the value to u32::MAX because any value
     // above BITS - 1 will produce the same result.
-    let shift = u32::try_from(extract_first_u64(this, right)?).unwrap_or(u32::MAX);
+    let shift = u32::try_from(extract_first_u64(ecx, right)?).unwrap_or(u32::MAX);
 
     for i in 0..dest_len {
-        let left = this.read_scalar(&this.project_index(&left, i)?)?;
-        let dest = this.project_index(&dest, i)?;
+        let left = ecx.read_scalar(&ecx.project_index(&left, i)?)?;
+        let dest = ecx.project_index(&dest, i)?;
 
         let res = match which {
             ShiftOp::Left => {
@@ -537,7 +528,7 @@ fn shift_simd_by_scalar<'tcx>(
                 Scalar::from_int(res, dest.layout.size)
             }
         };
-        this.write_scalar(res, &dest)?;
+        ecx.write_scalar(res, &dest)?;
     }
 
     interp_ok(())
@@ -549,23 +540,23 @@ fn shift_simd_by_scalar<'tcx>(
 /// For arithmetic right-shifts, when right is larger than BITS - 1, the sign
 /// bit is copied to all bits.
 fn shift_simd_by_simd<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     which: ShiftOp,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.project_to_simd(left)?;
-    let (right, right_len) = this.project_to_simd(right)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (left, left_len) = ecx.project_to_simd(left)?;
+    let (right, right_len) = ecx.project_to_simd(right)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, left_len);
     assert_eq!(dest_len, right_len);
 
     for i in 0..dest_len {
-        let left = this.read_scalar(&this.project_index(&left, i)?)?;
-        let right = this.read_scalar(&this.project_index(&right, i)?)?;
-        let dest = this.project_index(&dest, i)?;
+        let left = ecx.read_scalar(&ecx.project_index(&left, i)?)?;
+        let right = ecx.read_scalar(&ecx.project_index(&right, i)?)?;
+        let dest = ecx.project_index(&dest, i)?;
 
         // It is ok to saturate the value to u32::MAX because any value
         // above BITS - 1 will produce the same result.
@@ -592,7 +583,7 @@ fn shift_simd_by_simd<'tcx>(
                 Scalar::from_int(res, dest.layout.size)
             }
         };
-        this.write_scalar(res, &dest)?;
+        ecx.write_scalar(res, &dest)?;
     }
 
     interp_ok(())
@@ -601,44 +592,44 @@ fn shift_simd_by_simd<'tcx>(
 /// Takes a 128-bit vector, transmutes it to `[u64; 2]` and extracts
 /// the first value.
 fn extract_first_u64<'tcx>(
-    this: &crate::MiriInterpCx<'tcx>,
+    ecx: &crate::MiriInterpCx<'tcx>,
     op: &OpTy<'tcx>,
 ) -> InterpResult<'tcx, u64> {
     // Transmute vector to `[u64; 2]`
-    let array_layout = this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u64, 2))?;
-    let op = op.transmute(array_layout, this)?;
+    let array_layout = ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u64, 2))?;
+    let op = op.transmute(array_layout, ecx)?;
 
     // Get the first u64 from the array
-    this.read_scalar(&this.project_index(&op, 0)?)?.to_u64()
+    ecx.read_scalar(&ecx.project_index(&op, 0)?)?.to_u64()
 }
 
 // Rounds the first element of `right` according to `rounding`
 // and copies the remaining elements from `left`.
 fn round_first<'tcx, F: rustc_apfloat::Float>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     rounding: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.project_to_simd(left)?;
-    let (right, right_len) = this.project_to_simd(right)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (left, left_len) = ecx.project_to_simd(left)?;
+    let (right, right_len) = ecx.project_to_simd(right)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, left_len);
     assert_eq!(dest_len, right_len);
 
-    let rounding = rounding_from_imm(this.read_scalar(rounding)?.to_i32()?)?;
+    let rounding = rounding_from_imm(ecx.read_scalar(rounding)?.to_i32()?)?;
 
-    let op0: F = this.read_scalar(&this.project_index(&right, 0)?)?.to_float()?;
+    let op0: F = ecx.read_scalar(&ecx.project_index(&right, 0)?)?.to_float()?;
     let res = op0.round_to_integral(rounding).value;
-    this.write_scalar(
+    ecx.write_scalar(
         Scalar::from_uint(res.to_bits(), Size::from_bits(F::BITS)),
-        &this.project_index(&dest, 0)?,
+        &ecx.project_index(&dest, 0)?,
     )?;
 
     for i in 1..dest_len {
-        this.copy_op(&this.project_index(&left, i)?, &this.project_index(&dest, i)?)?;
+        ecx.copy_op(&ecx.project_index(&left, i)?, &ecx.project_index(&dest, i)?)?;
     }
 
     interp_ok(())
@@ -646,24 +637,24 @@ fn round_first<'tcx, F: rustc_apfloat::Float>(
 
 // Rounds all elements of `op` according to `rounding`.
 fn round_all<'tcx, F: rustc_apfloat::Float>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     op: &OpTy<'tcx>,
     rounding: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (op, op_len) = this.project_to_simd(op)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (op, op_len) = ecx.project_to_simd(op)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, op_len);
 
-    let rounding = rounding_from_imm(this.read_scalar(rounding)?.to_i32()?)?;
+    let rounding = rounding_from_imm(ecx.read_scalar(rounding)?.to_i32()?)?;
 
     for i in 0..dest_len {
-        let op: F = this.read_scalar(&this.project_index(&op, i)?)?.to_float()?;
+        let op: F = ecx.read_scalar(&ecx.project_index(&op, i)?)?.to_float()?;
         let res = op.round_to_integral(rounding).value;
-        this.write_scalar(
+        ecx.write_scalar(
             Scalar::from_uint(res.to_bits(), Size::from_bits(F::BITS)),
-            &this.project_index(&dest, i)?,
+            &ecx.project_index(&dest, i)?,
         )?;
     }
 
@@ -698,31 +689,31 @@ fn rounding_from_imm<'tcx>(rounding: i32) -> InterpResult<'tcx, rustc_apfloat::R
 /// If `op` has more elements than `dest`, extra elements are ignored. If `op`
 /// has less elements than `dest`, the rest is filled with zeros.
 fn convert_float_to_int<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     op: &OpTy<'tcx>,
     rnd: rustc_apfloat::Round,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (op, op_len) = this.project_to_simd(op)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (op, op_len) = ecx.project_to_simd(op)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     // Output must be *signed* integers.
-    assert!(matches!(dest.layout.field(this, 0).ty.kind(), ty::Int(_)));
+    assert!(matches!(dest.layout.field(ecx, 0).ty.kind(), ty::Int(_)));
 
     for i in 0..op_len.min(dest_len) {
-        let op = this.read_immediate(&this.project_index(&op, i)?)?;
-        let dest = this.project_index(&dest, i)?;
+        let op = ecx.read_immediate(&ecx.project_index(&op, i)?)?;
+        let dest = ecx.project_index(&dest, i)?;
 
-        let res = this.float_to_int_checked(&op, dest.layout, rnd)?.unwrap_or_else(|| {
+        let res = ecx.float_to_int_checked(&op, dest.layout, rnd)?.unwrap_or_else(|| {
             // Fallback to minimum according to SSE/AVX semantics.
             ImmTy::from_int(dest.layout.size.signed_int_min(), dest.layout)
         });
-        this.write_immediate(*res, &dest)?;
+        ecx.write_immediate(*res, &dest)?;
     }
     // Fill remainder with zeros
     for i in op_len..dest_len {
-        let dest = this.project_index(&dest, i)?;
-        this.write_scalar(Scalar::from_int(0, dest.layout.size), &dest)?;
+        let dest = ecx.project_index(&dest, i)?;
+        ecx.write_scalar(Scalar::from_int(0, dest.layout.size), &dest)?;
     }
 
     interp_ok(())
@@ -733,26 +724,26 @@ fn convert_float_to_int<'tcx>(
 /// In case of overflow (when the operand is the minimum value), the operation
 /// will wrap around.
 fn int_abs<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     op: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (op, op_len) = this.project_to_simd(op)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (op, op_len) = ecx.project_to_simd(op)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(op_len, dest_len);
 
-    let zero = ImmTy::from_int(0, op.layout.field(this, 0));
+    let zero = ImmTy::from_int(0, op.layout.field(ecx, 0));
 
     for i in 0..dest_len {
-        let op = this.read_immediate(&this.project_index(&op, i)?)?;
-        let dest = this.project_index(&dest, i)?;
+        let op = ecx.read_immediate(&ecx.project_index(&op, i)?)?;
+        let dest = ecx.project_index(&dest, i)?;
 
-        let lt_zero = this.binary_op(mir::BinOp::Lt, &op, &zero)?;
+        let lt_zero = ecx.binary_op(mir::BinOp::Lt, &op, &zero)?;
         let res =
-            if lt_zero.to_scalar().to_bool()? { this.unary_op(mir::UnOp::Neg, &op)? } else { op };
+            if lt_zero.to_scalar().to_bool()? { ecx.unary_op(mir::UnOp::Neg, &op)? } else { op };
 
-        this.write_immediate(*res, &dest)?;
+        ecx.write_immediate(*res, &dest)?;
     }
 
     interp_ok(())
@@ -766,25 +757,25 @@ fn int_abs<'tcx>(
 /// * The third element is the `op` vector split into chunks, i.e, it's
 ///   type is `[[T; M]; N]` where `T` is the element type of `op`.
 fn split_simd_to_128bit_chunks<'tcx, P: Projectable<'tcx, Provenance>>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     op: &P,
 ) -> InterpResult<'tcx, (u64, u64, P)> {
     let simd_layout = op.layout();
-    let (simd_len, element_ty) = simd_layout.ty.simd_size_and_type(this.tcx.tcx);
+    let (simd_len, element_ty) = simd_layout.ty.simd_size_and_type(ecx.tcx.tcx);
 
     assert_eq!(simd_layout.size.bits() % 128, 0);
     let num_chunks = simd_layout.size.bits() / 128;
     let items_per_chunk = simd_len.strict_div(num_chunks);
 
     // Transmute to `[[T; items_per_chunk]; num_chunks]`
-    let chunked_layout = this
+    let chunked_layout = ecx
         .layout_of(Ty::new_array(
-            this.tcx.tcx,
-            Ty::new_array(this.tcx.tcx, element_ty, items_per_chunk),
+            ecx.tcx.tcx,
+            Ty::new_array(ecx.tcx.tcx, element_ty, items_per_chunk),
             num_chunks,
         ))
         .unwrap();
-    let chunked_op = op.transmute(chunked_layout, this)?;
+    let chunked_op = op.transmute(chunked_layout, ecx)?;
 
     interp_ok((num_chunks, items_per_chunk, chunked_op))
 }
@@ -799,7 +790,7 @@ fn split_simd_to_128bit_chunks<'tcx, P: Projectable<'tcx, Provenance>>(
 /// the is i-th 128-bit chunk of `dest` is calculated with the i-th
 /// 128-bit chunks of `left` and `right`).
 fn horizontal_bin_op<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     which: mir::BinOp,
     saturating: bool,
     left: &OpTy<'tcx>,
@@ -809,15 +800,15 @@ fn horizontal_bin_op<'tcx>(
     assert_eq!(left.layout, dest.layout);
     assert_eq!(right.layout, dest.layout);
 
-    let (num_chunks, items_per_chunk, left) = split_simd_to_128bit_chunks(this, left)?;
-    let (_, _, right) = split_simd_to_128bit_chunks(this, right)?;
-    let (_, _, dest) = split_simd_to_128bit_chunks(this, dest)?;
+    let (num_chunks, items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?;
+    let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?;
+    let (_, _, dest) = split_simd_to_128bit_chunks(ecx, dest)?;
 
     let middle = items_per_chunk / 2;
     for i in 0..num_chunks {
-        let left = this.project_index(&left, i)?;
-        let right = this.project_index(&right, i)?;
-        let dest = this.project_index(&dest, i)?;
+        let left = ecx.project_index(&left, i)?;
+        let right = ecx.project_index(&right, i)?;
+        let dest = ecx.project_index(&dest, i)?;
 
         for j in 0..items_per_chunk {
             // `j` is the index in `dest`
@@ -825,16 +816,16 @@ fn horizontal_bin_op<'tcx>(
             let (k, src) = if j < middle { (j, &left) } else { (j.strict_sub(middle), &right) };
             // `base_i` is the index of the first item of the 2-item chunk in `src`
             let base_i = k.strict_mul(2);
-            let lhs = this.read_immediate(&this.project_index(src, base_i)?)?;
-            let rhs = this.read_immediate(&this.project_index(src, base_i.strict_add(1))?)?;
+            let lhs = ecx.read_immediate(&ecx.project_index(src, base_i)?)?;
+            let rhs = ecx.read_immediate(&ecx.project_index(src, base_i.strict_add(1))?)?;
 
             let res = if saturating {
-                Immediate::from(this.saturating_arith(which, &lhs, &rhs)?)
+                Immediate::from(ecx.saturating_arith(which, &lhs, &rhs)?)
             } else {
-                *this.binary_op(which, &lhs, &rhs)?
+                *ecx.binary_op(which, &lhs, &rhs)?
             };
 
-            this.write_immediate(res, &this.project_index(&dest, j)?)?;
+            ecx.write_immediate(res, &ecx.project_index(&dest, j)?)?;
         }
     }
 
@@ -850,7 +841,7 @@ fn horizontal_bin_op<'tcx>(
 /// the is i-th 128-bit chunk of `dest` is calculated with the i-th
 /// 128-bit blocks of `left` and `right`).
 fn conditional_dot_product<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     imm: &OpTy<'tcx>,
@@ -859,20 +850,20 @@ fn conditional_dot_product<'tcx>(
     assert_eq!(left.layout, dest.layout);
     assert_eq!(right.layout, dest.layout);
 
-    let (num_chunks, items_per_chunk, left) = split_simd_to_128bit_chunks(this, left)?;
-    let (_, _, right) = split_simd_to_128bit_chunks(this, right)?;
-    let (_, _, dest) = split_simd_to_128bit_chunks(this, dest)?;
+    let (num_chunks, items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?;
+    let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?;
+    let (_, _, dest) = split_simd_to_128bit_chunks(ecx, dest)?;
 
-    let element_layout = left.layout.field(this, 0).field(this, 0);
+    let element_layout = left.layout.field(ecx, 0).field(ecx, 0);
     assert!(items_per_chunk <= 4);
 
     // `imm` is a `u8` for SSE4.1 or an `i32` for AVX :/
-    let imm = this.read_scalar(imm)?.to_uint(imm.layout.size)?;
+    let imm = ecx.read_scalar(imm)?.to_uint(imm.layout.size)?;
 
     for i in 0..num_chunks {
-        let left = this.project_index(&left, i)?;
-        let right = this.project_index(&right, i)?;
-        let dest = this.project_index(&dest, i)?;
+        let left = ecx.project_index(&left, i)?;
+        let right = ecx.project_index(&right, i)?;
+        let dest = ecx.project_index(&dest, i)?;
 
         // Calculate dot product
         // Elements are floating point numbers, but we can use `from_int`
@@ -880,22 +871,22 @@ fn conditional_dot_product<'tcx>(
         let mut sum = ImmTy::from_int(0u8, element_layout);
         for j in 0..items_per_chunk {
             if imm & (1 << j.strict_add(4)) != 0 {
-                let left = this.read_immediate(&this.project_index(&left, j)?)?;
-                let right = this.read_immediate(&this.project_index(&right, j)?)?;
+                let left = ecx.read_immediate(&ecx.project_index(&left, j)?)?;
+                let right = ecx.read_immediate(&ecx.project_index(&right, j)?)?;
 
-                let mul = this.binary_op(mir::BinOp::Mul, &left, &right)?;
-                sum = this.binary_op(mir::BinOp::Add, &sum, &mul)?;
+                let mul = ecx.binary_op(mir::BinOp::Mul, &left, &right)?;
+                sum = ecx.binary_op(mir::BinOp::Add, &sum, &mul)?;
             }
         }
 
         // Write to destination (conditioned to imm)
         for j in 0..items_per_chunk {
-            let dest = this.project_index(&dest, j)?;
+            let dest = ecx.project_index(&dest, j)?;
 
             if imm & (1 << j) != 0 {
-                this.write_immediate(*sum, &dest)?;
+                ecx.write_immediate(*sum, &dest)?;
             } else {
-                this.write_scalar(Scalar::from_int(0u8, element_layout.size), &dest)?;
+                ecx.write_scalar(Scalar::from_int(0u8, element_layout.size), &dest)?;
             }
         }
     }
@@ -908,25 +899,25 @@ fn conditional_dot_product<'tcx>(
 /// The first is true when all the bits of `op & mask` are zero.
 /// The second is true when `(op & mask) == mask`
 fn test_bits_masked<'tcx>(
-    this: &crate::MiriInterpCx<'tcx>,
+    ecx: &crate::MiriInterpCx<'tcx>,
     op: &OpTy<'tcx>,
     mask: &OpTy<'tcx>,
 ) -> InterpResult<'tcx, (bool, bool)> {
     assert_eq!(op.layout, mask.layout);
 
-    let (op, op_len) = this.project_to_simd(op)?;
-    let (mask, mask_len) = this.project_to_simd(mask)?;
+    let (op, op_len) = ecx.project_to_simd(op)?;
+    let (mask, mask_len) = ecx.project_to_simd(mask)?;
 
     assert_eq!(op_len, mask_len);
 
     let mut all_zero = true;
     let mut masked_set = true;
     for i in 0..op_len {
-        let op = this.project_index(&op, i)?;
-        let mask = this.project_index(&mask, i)?;
+        let op = ecx.project_index(&op, i)?;
+        let mask = ecx.project_index(&mask, i)?;
 
-        let op = this.read_scalar(&op)?.to_uint(op.layout.size)?;
-        let mask = this.read_scalar(&mask)?.to_uint(mask.layout.size)?;
+        let op = ecx.read_scalar(&op)?.to_uint(op.layout.size)?;
+        let mask = ecx.read_scalar(&mask)?.to_uint(mask.layout.size)?;
         all_zero &= (op & mask) == 0;
         masked_set &= (op & mask) == mask;
     }
@@ -939,27 +930,27 @@ fn test_bits_masked<'tcx>(
 /// The first is true when the highest bit of each element of `op & mask` is zero.
 /// The second is true when the highest bit of each element of `!op & mask` is zero.
 fn test_high_bits_masked<'tcx>(
-    this: &crate::MiriInterpCx<'tcx>,
+    ecx: &crate::MiriInterpCx<'tcx>,
     op: &OpTy<'tcx>,
     mask: &OpTy<'tcx>,
 ) -> InterpResult<'tcx, (bool, bool)> {
     assert_eq!(op.layout, mask.layout);
 
-    let (op, op_len) = this.project_to_simd(op)?;
-    let (mask, mask_len) = this.project_to_simd(mask)?;
+    let (op, op_len) = ecx.project_to_simd(op)?;
+    let (mask, mask_len) = ecx.project_to_simd(mask)?;
 
     assert_eq!(op_len, mask_len);
 
-    let high_bit_offset = op.layout.field(this, 0).size.bits().strict_sub(1);
+    let high_bit_offset = op.layout.field(ecx, 0).size.bits().strict_sub(1);
 
     let mut direct = true;
     let mut negated = true;
     for i in 0..op_len {
-        let op = this.project_index(&op, i)?;
-        let mask = this.project_index(&mask, i)?;
+        let op = ecx.project_index(&op, i)?;
+        let mask = ecx.project_index(&mask, i)?;
 
-        let op = this.read_scalar(&op)?.to_uint(op.layout.size)?;
-        let mask = this.read_scalar(&mask)?.to_uint(mask.layout.size)?;
+        let op = ecx.read_scalar(&op)?.to_uint(op.layout.size)?;
+        let mask = ecx.read_scalar(&mask)?.to_uint(mask.layout.size)?;
         direct &= (op & mask) >> high_bit_offset == 0;
         negated &= (!op & mask) >> high_bit_offset == 0;
     }
@@ -970,30 +961,30 @@ fn test_high_bits_masked<'tcx>(
 /// Conditionally loads from `ptr` according the high bit of each
 /// element of `mask`. `ptr` does not need to be aligned.
 fn mask_load<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     ptr: &OpTy<'tcx>,
     mask: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (mask, mask_len) = this.project_to_simd(mask)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (mask, mask_len) = ecx.project_to_simd(mask)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, mask_len);
 
-    let mask_item_size = mask.layout.field(this, 0).size;
+    let mask_item_size = mask.layout.field(ecx, 0).size;
     let high_bit_offset = mask_item_size.bits().strict_sub(1);
 
-    let ptr = this.read_pointer(ptr)?;
+    let ptr = ecx.read_pointer(ptr)?;
     for i in 0..dest_len {
-        let mask = this.project_index(&mask, i)?;
-        let dest = this.project_index(&dest, i)?;
+        let mask = ecx.project_index(&mask, i)?;
+        let dest = ecx.project_index(&dest, i)?;
 
-        if this.read_scalar(&mask)?.to_uint(mask_item_size)? >> high_bit_offset != 0 {
-            let ptr = ptr.wrapping_offset(dest.layout.size * i, &this.tcx);
+        if ecx.read_scalar(&mask)?.to_uint(mask_item_size)? >> high_bit_offset != 0 {
+            let ptr = ptr.wrapping_offset(dest.layout.size * i, &ecx.tcx);
             // Unaligned copy, which is what we want.
-            this.mem_copy(ptr, dest.ptr(), dest.layout.size, /*nonoverlapping*/ true)?;
+            ecx.mem_copy(ptr, dest.ptr(), dest.layout.size, /*nonoverlapping*/ true)?;
         } else {
-            this.write_scalar(Scalar::from_int(0, dest.layout.size), &dest)?;
+            ecx.write_scalar(Scalar::from_int(0, dest.layout.size), &dest)?;
         }
     }
 
@@ -1003,31 +994,31 @@ fn mask_load<'tcx>(
 /// Conditionally stores into `ptr` according the high bit of each
 /// element of `mask`. `ptr` does not need to be aligned.
 fn mask_store<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     ptr: &OpTy<'tcx>,
     mask: &OpTy<'tcx>,
     value: &OpTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (mask, mask_len) = this.project_to_simd(mask)?;
-    let (value, value_len) = this.project_to_simd(value)?;
+    let (mask, mask_len) = ecx.project_to_simd(mask)?;
+    let (value, value_len) = ecx.project_to_simd(value)?;
 
     assert_eq!(value_len, mask_len);
 
-    let mask_item_size = mask.layout.field(this, 0).size;
+    let mask_item_size = mask.layout.field(ecx, 0).size;
     let high_bit_offset = mask_item_size.bits().strict_sub(1);
 
-    let ptr = this.read_pointer(ptr)?;
+    let ptr = ecx.read_pointer(ptr)?;
     for i in 0..value_len {
-        let mask = this.project_index(&mask, i)?;
-        let value = this.project_index(&value, i)?;
+        let mask = ecx.project_index(&mask, i)?;
+        let value = ecx.project_index(&value, i)?;
 
-        if this.read_scalar(&mask)?.to_uint(mask_item_size)? >> high_bit_offset != 0 {
+        if ecx.read_scalar(&mask)?.to_uint(mask_item_size)? >> high_bit_offset != 0 {
             // *Non-inbounds* pointer arithmetic to compute the destination.
             // (That's why we can't use a place projection.)
-            let ptr = ptr.wrapping_offset(value.layout.size * i, &this.tcx);
+            let ptr = ptr.wrapping_offset(value.layout.size * i, &ecx.tcx);
             // Deref the pointer *unaligned*, and do the copy.
-            let dest = this.ptr_to_mplace_unaligned(ptr, value.layout);
-            this.copy_op(&value, &dest)?;
+            let dest = ecx.ptr_to_mplace_unaligned(ptr, value.layout);
+            ecx.copy_op(&value, &dest)?;
         }
     }
 
@@ -1046,7 +1037,7 @@ fn mask_store<'tcx>(
 /// the is i-th 128-bit chunk of `dest` is calculated with the i-th
 /// 128-bit chunks of `left` and `right`).
 fn mpsadbw<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     imm: &OpTy<'tcx>,
@@ -1055,13 +1046,13 @@ fn mpsadbw<'tcx>(
     assert_eq!(left.layout, right.layout);
     assert_eq!(left.layout.size, dest.layout.size);
 
-    let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(this, left)?;
-    let (_, _, right) = split_simd_to_128bit_chunks(this, right)?;
-    let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(this, dest)?;
+    let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?;
+    let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?;
+    let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(ecx, dest)?;
 
     assert_eq!(op_items_per_chunk, dest_items_per_chunk.strict_mul(2));
 
-    let imm = this.read_scalar(imm)?.to_uint(imm.layout.size)?;
+    let imm = ecx.read_scalar(imm)?.to_uint(imm.layout.size)?;
     // Bit 2 of `imm` specifies the offset for indices of `left`.
     // The offset is 0 when the bit is 0 or 4 when the bit is 1.
     let left_offset = u64::try_from((imm >> 2) & 1).unwrap().strict_mul(4);
@@ -1070,23 +1061,23 @@ fn mpsadbw<'tcx>(
     let right_offset = u64::try_from(imm & 0b11).unwrap().strict_mul(4);
 
     for i in 0..num_chunks {
-        let left = this.project_index(&left, i)?;
-        let right = this.project_index(&right, i)?;
-        let dest = this.project_index(&dest, i)?;
+        let left = ecx.project_index(&left, i)?;
+        let right = ecx.project_index(&right, i)?;
+        let dest = ecx.project_index(&dest, i)?;
 
         for j in 0..dest_items_per_chunk {
             let left_offset = left_offset.strict_add(j);
             let mut res: u16 = 0;
             for k in 0..4 {
-                let left = this
-                    .read_scalar(&this.project_index(&left, left_offset.strict_add(k))?)?
+                let left = ecx
+                    .read_scalar(&ecx.project_index(&left, left_offset.strict_add(k))?)?
                     .to_u8()?;
-                let right = this
-                    .read_scalar(&this.project_index(&right, right_offset.strict_add(k))?)?
+                let right = ecx
+                    .read_scalar(&ecx.project_index(&right, right_offset.strict_add(k))?)?
                     .to_u8()?;
                 res = res.strict_add(left.abs_diff(right).into());
             }
-            this.write_scalar(Scalar::from_u16(res), &this.project_index(&dest, j)?)?;
+            ecx.write_scalar(Scalar::from_u16(res), &ecx.project_index(&dest, j)?)?;
         }
     }
 
@@ -1101,31 +1092,31 @@ fn mpsadbw<'tcx>(
 /// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhrs_epi16>
 /// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm256_mulhrs_epi16>
 fn pmulhrsw<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.project_to_simd(left)?;
-    let (right, right_len) = this.project_to_simd(right)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (left, left_len) = ecx.project_to_simd(left)?;
+    let (right, right_len) = ecx.project_to_simd(right)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, left_len);
     assert_eq!(dest_len, right_len);
 
     for i in 0..dest_len {
-        let left = this.read_scalar(&this.project_index(&left, i)?)?.to_i16()?;
-        let right = this.read_scalar(&this.project_index(&right, i)?)?.to_i16()?;
-        let dest = this.project_index(&dest, i)?;
+        let left = ecx.read_scalar(&ecx.project_index(&left, i)?)?.to_i16()?;
+        let right = ecx.read_scalar(&ecx.project_index(&right, i)?)?.to_i16()?;
+        let dest = ecx.project_index(&dest, i)?;
 
         let res = (i32::from(left).strict_mul(right.into()) >> 14).strict_add(1) >> 1;
 
         // The result of this operation can overflow a signed 16-bit integer.
         // When `left` and `right` are -0x8000, the result is 0x8000.
-        #[allow(clippy::cast_possible_truncation)]
+        #[expect(clippy::cast_possible_truncation)]
         let res = res as i16;
 
-        this.write_scalar(Scalar::from_i16(res), &dest)?;
+        ecx.write_scalar(Scalar::from_i16(res), &dest)?;
     }
 
     interp_ok(())
@@ -1134,61 +1125,68 @@ fn pmulhrsw<'tcx>(
 /// Perform a carry-less multiplication of two 64-bit integers, selected from `left` and `right` according to `imm8`,
 /// and store the results in `dst`.
 ///
-/// `left` and `right` are both vectors of type 2 x i64. Only bits 0 and 4 of `imm8` matter;
+/// `left` and `right` are both vectors of type `len` x i64. Only bits 0 and 4 of `imm8` matter;
 /// they select the element of `left` and `right`, respectively.
 ///
+/// `len` is the SIMD vector length (in counts of `i64` values). It is expected to be one of
+/// `2`, `4`, or `8`.
+///
 /// <https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_clmulepi64_si128>
 fn pclmulqdq<'tcx>(
-    this: &mut MiriInterpCx<'tcx>,
+    ecx: &mut MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     imm8: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
+    len: u64,
 ) -> InterpResult<'tcx, ()> {
     assert_eq!(left.layout, right.layout);
     assert_eq!(left.layout.size, dest.layout.size);
+    assert!([2u64, 4, 8].contains(&len));
 
-    // Transmute to `[u64; 2]`
+    // Transmute the input into arrays of `[u64; len]`.
+    // Transmute the output into an array of `[u128, len / 2]`.
 
-    let array_layout = this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u64, 2))?;
-    let left = left.transmute(array_layout, this)?;
-    let right = right.transmute(array_layout, this)?;
-    let dest = dest.transmute(array_layout, this)?;
+    let src_layout = ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u64, len))?;
+    let dest_layout = ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u128, len / 2))?;
 
-    let imm8 = this.read_scalar(imm8)?.to_u8()?;
+    let left = left.transmute(src_layout, ecx)?;
+    let right = right.transmute(src_layout, ecx)?;
+    let dest = dest.transmute(dest_layout, ecx)?;
 
-    // select the 64-bit integer from left that the user specified (low or high)
-    let index = if (imm8 & 0x01) == 0 { 0 } else { 1 };
-    let left = this.read_scalar(&this.project_index(&left, index)?)?.to_u64()?;
+    let imm8 = ecx.read_scalar(imm8)?.to_u8()?;
 
-    // select the 64-bit integer from right that the user specified (low or high)
-    let index = if (imm8 & 0x10) == 0 { 0 } else { 1 };
-    let right = this.read_scalar(&this.project_index(&right, index)?)?.to_u64()?;
+    for i in 0..(len / 2) {
+        let lo = i.strict_mul(2);
+        let hi = i.strict_mul(2).strict_add(1);
 
-    // Perform carry-less multiplication
-    //
-    // This operation is like long multiplication, but ignores all carries.
-    // That idea corresponds to the xor operator, which is used in the implementation.
-    //
-    // Wikipedia has an example https://en.wikipedia.org/wiki/Carry-less_product#Example
-    let mut result: u128 = 0;
+        // select the 64-bit integer from left that the user specified (low or high)
+        let index = if (imm8 & 0x01) == 0 { lo } else { hi };
+        let left = ecx.read_scalar(&ecx.project_index(&left, index)?)?.to_u64()?;
 
-    for i in 0..64 {
-        // if the i-th bit in right is set
-        if (right & (1 << i)) != 0 {
-            // xor result with `left` shifted to the left by i positions
-            result ^= u128::from(left) << i;
-        }
-    }
+        // select the 64-bit integer from right that the user specified (low or high)
+        let index = if (imm8 & 0x10) == 0 { lo } else { hi };
+        let right = ecx.read_scalar(&ecx.project_index(&right, index)?)?.to_u64()?;
 
-    let result_low = (result & 0xFFFF_FFFF_FFFF_FFFF) as u64;
-    let result_high = (result >> 64) as u64;
+        // Perform carry-less multiplication.
+        //
+        // This operation is like long multiplication, but ignores all carries.
+        // That idea corresponds to the xor operator, which is used in the implementation.
+        //
+        // Wikipedia has an example https://en.wikipedia.org/wiki/Carry-less_product#Example
+        let mut result: u128 = 0;
 
-    let dest_low = this.project_index(&dest, 0)?;
-    this.write_scalar(Scalar::from_u64(result_low), &dest_low)?;
+        for i in 0..64 {
+            // if the i-th bit in right is set
+            if (right & (1 << i)) != 0 {
+                // xor result with `left` shifted to the left by i positions
+                result ^= u128::from(left) << i;
+            }
+        }
 
-    let dest_high = this.project_index(&dest, 1)?;
-    this.write_scalar(Scalar::from_u64(result_high), &dest_high)?;
+        let dest = ecx.project_index(&dest, i)?;
+        ecx.write_scalar(Scalar::from_u128(result), &dest)?;
+    }
 
     interp_ok(())
 }
@@ -1201,7 +1199,7 @@ fn pclmulqdq<'tcx>(
 /// the is i-th 128-bit chunk of `dest` is calculated with the i-th
 /// 128-bit chunks of `left` and `right`).
 fn pack_generic<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
@@ -1210,28 +1208,28 @@ fn pack_generic<'tcx>(
     assert_eq!(left.layout, right.layout);
     assert_eq!(left.layout.size, dest.layout.size);
 
-    let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(this, left)?;
-    let (_, _, right) = split_simd_to_128bit_chunks(this, right)?;
-    let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(this, dest)?;
+    let (num_chunks, op_items_per_chunk, left) = split_simd_to_128bit_chunks(ecx, left)?;
+    let (_, _, right) = split_simd_to_128bit_chunks(ecx, right)?;
+    let (_, dest_items_per_chunk, dest) = split_simd_to_128bit_chunks(ecx, dest)?;
 
     assert_eq!(dest_items_per_chunk, op_items_per_chunk.strict_mul(2));
 
     for i in 0..num_chunks {
-        let left = this.project_index(&left, i)?;
-        let right = this.project_index(&right, i)?;
-        let dest = this.project_index(&dest, i)?;
+        let left = ecx.project_index(&left, i)?;
+        let right = ecx.project_index(&right, i)?;
+        let dest = ecx.project_index(&dest, i)?;
 
         for j in 0..op_items_per_chunk {
-            let left = this.read_scalar(&this.project_index(&left, j)?)?;
-            let right = this.read_scalar(&this.project_index(&right, j)?)?;
-            let left_dest = this.project_index(&dest, j)?;
-            let right_dest = this.project_index(&dest, j.strict_add(op_items_per_chunk))?;
+            let left = ecx.read_scalar(&ecx.project_index(&left, j)?)?;
+            let right = ecx.read_scalar(&ecx.project_index(&right, j)?)?;
+            let left_dest = ecx.project_index(&dest, j)?;
+            let right_dest = ecx.project_index(&dest, j.strict_add(op_items_per_chunk))?;
 
             let left_res = f(left)?;
             let right_res = f(right)?;
 
-            this.write_scalar(left_res, &left_dest)?;
-            this.write_scalar(right_res, &right_dest)?;
+            ecx.write_scalar(left_res, &left_dest)?;
+            ecx.write_scalar(right_res, &right_dest)?;
         }
     }
 
@@ -1245,12 +1243,12 @@ fn pack_generic<'tcx>(
 /// the is i-th 128-bit chunk of `dest` is calculated with the i-th
 /// 128-bit chunks of `left` and `right`).
 fn packsswb<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    pack_generic(this, left, right, dest, |op| {
+    pack_generic(ecx, left, right, dest, |op| {
         let op = op.to_i16()?;
         let res = i8::try_from(op).unwrap_or(if op < 0 { i8::MIN } else { i8::MAX });
         interp_ok(Scalar::from_i8(res))
@@ -1264,12 +1262,12 @@ fn packsswb<'tcx>(
 /// the is i-th 128-bit chunk of `dest` is calculated with the i-th
 /// 128-bit chunks of `left` and `right`).
 fn packuswb<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    pack_generic(this, left, right, dest, |op| {
+    pack_generic(ecx, left, right, dest, |op| {
         let op = op.to_i16()?;
         let res = u8::try_from(op).unwrap_or(if op < 0 { 0 } else { u8::MAX });
         interp_ok(Scalar::from_u8(res))
@@ -1283,12 +1281,12 @@ fn packuswb<'tcx>(
 /// the is i-th 128-bit chunk of `dest` is calculated with the i-th
 /// 128-bit chunks of `left` and `right`).
 fn packssdw<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    pack_generic(this, left, right, dest, |op| {
+    pack_generic(ecx, left, right, dest, |op| {
         let op = op.to_i32()?;
         let res = i16::try_from(op).unwrap_or(if op < 0 { i16::MIN } else { i16::MAX });
         interp_ok(Scalar::from_i16(res))
@@ -1302,12 +1300,12 @@ fn packssdw<'tcx>(
 /// the is i-th 128-bit chunk of `dest` is calculated with the i-th
 /// 128-bit chunks of `left` and `right`).
 fn packusdw<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    pack_generic(this, left, right, dest, |op| {
+    pack_generic(ecx, left, right, dest, |op| {
         let op = op.to_i32()?;
         let res = u16::try_from(op).unwrap_or(if op < 0 { 0 } else { u16::MAX });
         interp_ok(Scalar::from_u16(res))
@@ -1319,27 +1317,27 @@ fn packusdw<'tcx>(
 /// is written to the corresponding output element.
 /// In other words, multiplies `left` with `right.signum()`.
 fn psign<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     left: &OpTy<'tcx>,
     right: &OpTy<'tcx>,
     dest: &MPlaceTy<'tcx>,
 ) -> InterpResult<'tcx, ()> {
-    let (left, left_len) = this.project_to_simd(left)?;
-    let (right, right_len) = this.project_to_simd(right)?;
-    let (dest, dest_len) = this.project_to_simd(dest)?;
+    let (left, left_len) = ecx.project_to_simd(left)?;
+    let (right, right_len) = ecx.project_to_simd(right)?;
+    let (dest, dest_len) = ecx.project_to_simd(dest)?;
 
     assert_eq!(dest_len, left_len);
     assert_eq!(dest_len, right_len);
 
     for i in 0..dest_len {
-        let dest = this.project_index(&dest, i)?;
-        let left = this.read_immediate(&this.project_index(&left, i)?)?;
-        let right = this.read_scalar(&this.project_index(&right, i)?)?.to_int(dest.layout.size)?;
+        let dest = ecx.project_index(&dest, i)?;
+        let left = ecx.read_immediate(&ecx.project_index(&left, i)?)?;
+        let right = ecx.read_scalar(&ecx.project_index(&right, i)?)?.to_int(dest.layout.size)?;
 
         let res =
-            this.binary_op(mir::BinOp::Mul, &left, &ImmTy::from_int(right.signum(), dest.layout))?;
+            ecx.binary_op(mir::BinOp::Mul, &left, &ImmTy::from_int(right.signum(), dest.layout))?;
 
-        this.write_immediate(*res, &dest)?;
+        ecx.write_immediate(*res, &dest)?;
     }
 
     interp_ok(())
@@ -1349,7 +1347,7 @@ fn psign<'tcx>(
 /// of `op` and returns both the sum and the overflow bit. `op` is expected to be
 /// either one of `mir::BinOp::AddWithOverflow` and `mir::BinOp::SubWithOverflow`.
 fn carrying_add<'tcx>(
-    this: &mut crate::MiriInterpCx<'tcx>,
+    ecx: &mut crate::MiriInterpCx<'tcx>,
     cb_in: &OpTy<'tcx>,
     a: &OpTy<'tcx>,
     b: &OpTy<'tcx>,
@@ -1357,13 +1355,13 @@ fn carrying_add<'tcx>(
 ) -> InterpResult<'tcx, (ImmTy<'tcx>, Scalar)> {
     assert!(op == mir::BinOp::AddWithOverflow || op == mir::BinOp::SubWithOverflow);
 
-    let cb_in = this.read_scalar(cb_in)?.to_u8()? != 0;
-    let a = this.read_immediate(a)?;
-    let b = this.read_immediate(b)?;
+    let cb_in = ecx.read_scalar(cb_in)?.to_u8()? != 0;
+    let a = ecx.read_immediate(a)?;
+    let b = ecx.read_immediate(b)?;
 
-    let (sum, overflow1) = this.binary_op(op, &a, &b)?.to_pair(this);
+    let (sum, overflow1) = ecx.binary_op(op, &a, &b)?.to_pair(ecx);
     let (sum, overflow2) =
-        this.binary_op(op, &sum, &ImmTy::from_uint(cb_in, a.layout))?.to_pair(this);
+        ecx.binary_op(op, &sum, &ImmTy::from_uint(cb_in, a.layout))?.to_pair(ecx);
     let cb_out = overflow1.to_scalar().to_bool()? | overflow2.to_scalar().to_bool()?;
 
     interp_ok((sum, Scalar::from_u8(cb_out.into())))
diff --git a/src/tools/miri/src/shims/x86/sha.rs b/src/tools/miri/src/shims/x86/sha.rs
index 8553771b694..f18ff1ec253 100644
--- a/src/tools/miri/src/shims/x86/sha.rs
+++ b/src/tools/miri/src/shims/x86/sha.rs
@@ -4,8 +4,8 @@
 //!
 //! [RustCrypto's sha256 module]: https://github.com/RustCrypto/hashes/blob/6be8466247e936c415d8aafb848697f39894a386/sha2/src/sha256/soft.rs
 
+use rustc_abi::ExternAbi;
 use rustc_span::Symbol;
-use rustc_target::spec::abi::Abi;
 
 use crate::*;
 
@@ -14,7 +14,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_x86_sha_intrinsic(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -23,27 +23,27 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         // Prefix should have already been checked.
         let unprefixed_name = link_name.as_str().strip_prefix("llvm.x86.sha").unwrap();
 
-        fn read<'c>(this: &mut MiriInterpCx<'c>, reg: &OpTy<'c>) -> InterpResult<'c, [u32; 4]> {
+        fn read<'c>(ecx: &mut MiriInterpCx<'c>, reg: &OpTy<'c>) -> InterpResult<'c, [u32; 4]> {
             let mut res = [0; 4];
             // We reverse the order because x86 is little endian but the copied implementation uses
             // big endian.
             for (i, dst) in res.iter_mut().rev().enumerate() {
-                let projected = &this.project_index(reg, i.try_into().unwrap())?;
-                *dst = this.read_scalar(projected)?.to_u32()?
+                let projected = &ecx.project_index(reg, i.try_into().unwrap())?;
+                *dst = ecx.read_scalar(projected)?.to_u32()?
             }
             interp_ok(res)
         }
 
         fn write<'c>(
-            this: &mut MiriInterpCx<'c>,
+            ecx: &mut MiriInterpCx<'c>,
             dest: &MPlaceTy<'c>,
             val: [u32; 4],
         ) -> InterpResult<'c, ()> {
             // We reverse the order because x86 is little endian but the copied implementation uses
             // big endian.
             for (i, part) in val.into_iter().rev().enumerate() {
-                let projected = &this.project_index(dest, i.try_into().unwrap())?;
-                this.write_scalar(Scalar::from_u32(part), projected)?;
+                let projected = &ecx.project_index(dest, i.try_into().unwrap())?;
+                ecx.write_scalar(Scalar::from_u32(part), projected)?;
             }
             interp_ok(())
         }
@@ -51,7 +51,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
         match unprefixed_name {
             // Used to implement the _mm_sha256rnds2_epu32 function.
             "256rnds2" => {
-                let [a, b, k] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [a, b, k] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (a_reg, a_len) = this.project_to_simd(a)?;
                 let (b_reg, b_len) = this.project_to_simd(b)?;
@@ -72,7 +73,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             // Used to implement the _mm_sha256msg1_epu32 function.
             "256msg1" => {
-                let [a, b] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [a, b] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (a_reg, a_len) = this.project_to_simd(a)?;
                 let (b_reg, b_len) = this.project_to_simd(b)?;
@@ -90,7 +92,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             }
             // Used to implement the _mm_sha256msg2_epu32 function.
             "256msg2" => {
-                let [a, b] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [a, b] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (a_reg, a_len) = this.project_to_simd(a)?;
                 let (b_reg, b_len) = this.project_to_simd(b)?;
diff --git a/src/tools/miri/src/shims/x86/sse.rs b/src/tools/miri/src/shims/x86/sse.rs
index 013900e95d3..9432b40a805 100644
--- a/src/tools/miri/src/shims/x86/sse.rs
+++ b/src/tools/miri/src/shims/x86/sse.rs
@@ -1,6 +1,6 @@
+use rustc_abi::ExternAbi;
 use rustc_apfloat::ieee::Single;
 use rustc_span::Symbol;
-use rustc_target::spec::abi::Abi;
 
 use super::{
     FloatBinOp, FloatUnaryOp, bin_op_simd_float_all, bin_op_simd_float_first, unary_op_ps,
@@ -13,7 +13,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_x86_sse_intrinsic(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -33,7 +33,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // `right` and copies the remaining components from `left`.
             "min.ss" | "max.ss" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which = match unprefixed_name {
                     "min.ss" => FloatBinOp::Min,
@@ -50,7 +50,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // semantics.
             "min.ps" | "max.ps" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which = match unprefixed_name {
                     "min.ps" => FloatBinOp::Min,
@@ -64,7 +64,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Performs the operations on the first component of `op` and
             // copies the remaining components from `op`.
             "rcp.ss" | "rsqrt.ss" => {
-                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [op] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which = match unprefixed_name {
                     "rcp.ss" => FloatUnaryOp::Rcp,
@@ -77,7 +77,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Used to implement _mm_{sqrt,rcp,rsqrt}_ps functions.
             // Performs the operations on all components of `op`.
             "rcp.ps" | "rsqrt.ps" => {
-                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [op] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which = match unprefixed_name {
                     "rcp.ps" => FloatUnaryOp::Rcp,
@@ -97,7 +97,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // with hard-coded operations.
             "cmp.ss" => {
                 let [left, right, imm] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which =
                     FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
@@ -114,7 +114,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // with hard-coded operations.
             "cmp.ps" => {
                 let [left, right, imm] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which =
                     FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
@@ -128,7 +128,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             | "ucomieq.ss" | "ucomilt.ss" | "ucomile.ss" | "ucomigt.ss" | "ucomige.ss"
             | "ucomineq.ss" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (left, left_len) = this.project_to_simd(left)?;
                 let (right, right_len) = this.project_to_simd(right)?;
@@ -156,7 +156,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // _mm_cvtss_si64 and _mm_cvttss_si64 functions.
             // Converts the first component of `op` from f32 to i32/i64.
             "cvtss2si" | "cvttss2si" | "cvtss2si64" | "cvttss2si64" => {
-                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [op] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let (op, _) = this.project_to_simd(op)?;
 
                 let op = this.read_immediate(&this.project_index(&op, 0)?)?;
@@ -185,7 +185,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // https://www.felixcloutier.com/x86/cvtsi2ss
             "cvtsi2ss" | "cvtsi642ss" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (left, left_len) = this.project_to_simd(left)?;
                 let (dest, dest_len) = this.project_to_simd(dest)?;
diff --git a/src/tools/miri/src/shims/x86/sse2.rs b/src/tools/miri/src/shims/x86/sse2.rs
index de72e63f8af..4ff999be7cc 100644
--- a/src/tools/miri/src/shims/x86/sse2.rs
+++ b/src/tools/miri/src/shims/x86/sse2.rs
@@ -1,6 +1,6 @@
+use rustc_abi::ExternAbi;
 use rustc_apfloat::ieee::Double;
 use rustc_span::Symbol;
-use rustc_target::spec::abi::Abi;
 
 use super::{
     FloatBinOp, ShiftOp, bin_op_simd_float_all, bin_op_simd_float_first, convert_float_to_int,
@@ -13,7 +13,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_x86_sse2_intrinsic(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -40,7 +40,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // intermediate 32-bit integers, and pack the results in `dest`.
             "pmadd.wd" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (left, left_len) = this.project_to_simd(left)?;
                 let (right, right_len) = this.project_to_simd(right)?;
@@ -79,7 +79,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_sad_epu8
             "psad.bw" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (left, left_len) = this.project_to_simd(left)?;
                 let (right, right_len) = this.project_to_simd(right)?;
@@ -118,7 +118,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             "psll.w" | "psrl.w" | "psra.w" | "psll.d" | "psrl.d" | "psra.d" | "psll.q"
             | "psrl.q" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which = match unprefixed_name {
                     "psll.w" | "psll.d" | "psll.q" => ShiftOp::Left,
@@ -133,7 +133,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // and _mm_cvttpd_epi32 functions.
             // Converts packed f32/f64 to packed i32.
             "cvtps2dq" | "cvttps2dq" | "cvtpd2dq" | "cvttpd2dq" => {
-                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [op] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (op_len, _) = op.layout.ty.simd_size_and_type(*this.tcx);
                 let (dest_len, _) = dest.layout.ty.simd_size_and_type(*this.tcx);
@@ -171,7 +171,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // vector with signed saturation.
             "packsswb.128" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 packsswb(this, left, right, dest)?;
             }
@@ -180,7 +180,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // unsigned integer vector with saturation.
             "packuswb.128" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 packuswb(this, left, right, dest)?;
             }
@@ -189,7 +189,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // vector with signed saturation.
             "packssdw.128" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 packssdw(this, left, right, dest)?;
             }
@@ -200,7 +200,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // semantics.
             "min.sd" | "max.sd" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which = match unprefixed_name {
                     "min.sd" => FloatBinOp::Min,
@@ -217,7 +217,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // semantics.
             "min.pd" | "max.pd" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which = match unprefixed_name {
                     "min.pd" => FloatBinOp::Min,
@@ -237,7 +237,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // with hard-coded operations.
             "cmp.sd" => {
                 let [left, right, imm] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which =
                     FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
@@ -254,7 +254,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // with hard-coded operations.
             "cmp.pd" => {
                 let [left, right, imm] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which =
                     FloatBinOp::cmp_from_imm(this, this.read_scalar(imm)?.to_i8()?, link_name)?;
@@ -268,7 +268,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             | "ucomieq.sd" | "ucomilt.sd" | "ucomile.sd" | "ucomigt.sd" | "ucomige.sd"
             | "ucomineq.sd" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (left, left_len) = this.project_to_simd(left)?;
                 let (right, right_len) = this.project_to_simd(right)?;
@@ -296,7 +296,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // _mm_cvtsd_si64 and _mm_cvttsd_si64 functions.
             // Converts the first component of `op` from f64 to i32/i64.
             "cvtsd2si" | "cvttsd2si" | "cvtsd2si64" | "cvttsd2si64" => {
-                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [op] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let (op, _) = this.project_to_simd(op)?;
 
                 let op = this.read_immediate(&this.project_index(&op, 0)?)?;
@@ -323,7 +323,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // the remaining elements from `left`
             "cvtsd2ss" | "cvtss2sd" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (left, left_len) = this.project_to_simd(left)?;
                 let (right, _) = this.project_to_simd(right)?;
diff --git a/src/tools/miri/src/shims/x86/sse3.rs b/src/tools/miri/src/shims/x86/sse3.rs
index 9aa4dd59950..86153d71b28 100644
--- a/src/tools/miri/src/shims/x86/sse3.rs
+++ b/src/tools/miri/src/shims/x86/sse3.rs
@@ -1,6 +1,6 @@
+use rustc_abi::ExternAbi;
 use rustc_middle::mir;
 use rustc_span::Symbol;
-use rustc_target::spec::abi::Abi;
 
 use super::horizontal_bin_op;
 use crate::*;
@@ -10,7 +10,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_x86_sse3_intrinsic(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -25,7 +25,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // in `left` and `right`.
             "hadd.ps" | "hadd.pd" | "hsub.ps" | "hsub.pd" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let which = match unprefixed_name {
                     "hadd.ps" | "hadd.pd" => mir::BinOp::Add,
@@ -41,7 +41,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // the data crosses a cache line, but for Miri this is just a regular
             // unaligned read.
             "ldu.dq" => {
-                let [src_ptr] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [src_ptr] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let src_ptr = this.read_pointer(src_ptr)?;
                 let dest = dest.force_mplace(this)?;
 
diff --git a/src/tools/miri/src/shims/x86/sse41.rs b/src/tools/miri/src/shims/x86/sse41.rs
index 7bf8ddea2fe..b81d6f24141 100644
--- a/src/tools/miri/src/shims/x86/sse41.rs
+++ b/src/tools/miri/src/shims/x86/sse41.rs
@@ -1,5 +1,5 @@
+use rustc_abi::ExternAbi;
 use rustc_span::Symbol;
-use rustc_target::spec::abi::Abi;
 
 use super::{conditional_dot_product, mpsadbw, packusdw, round_all, round_first, test_bits_masked};
 use crate::*;
@@ -9,7 +9,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_x86_sse41_intrinsic(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -27,7 +27,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // `i` is zeroed.
             "insertps" => {
                 let [left, right, imm] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (left, left_len) = this.project_to_simd(left)?;
                 let (right, right_len) = this.project_to_simd(right)?;
@@ -63,7 +63,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // the result to a 16-bit unsigned integer vector with saturation.
             "packusdw" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 packusdw(this, left, right, dest)?;
             }
@@ -74,7 +74,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // 4 bits of `imm`.
             "dpps" | "dppd" => {
                 let [left, right, imm] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 conditional_dot_product(this, left, right, imm, dest)?;
             }
@@ -83,7 +83,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // and copies the remaining elements from `left`.
             "round.ss" => {
                 let [left, right, rounding] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 round_first::<rustc_apfloat::ieee::Single>(this, left, right, rounding, dest)?;
             }
@@ -91,7 +91,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // functions. Rounds the elements of `op` according to `rounding`.
             "round.ps" => {
                 let [op, rounding] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 round_all::<rustc_apfloat::ieee::Single>(this, op, rounding, dest)?;
             }
@@ -100,7 +100,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // and copies the remaining elements from `left`.
             "round.sd" => {
                 let [left, right, rounding] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 round_first::<rustc_apfloat::ieee::Double>(this, left, right, rounding, dest)?;
             }
@@ -108,7 +108,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // functions. Rounds the elements of `op` according to `rounding`.
             "round.pd" => {
                 let [op, rounding] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 round_all::<rustc_apfloat::ieee::Double>(this, op, rounding, dest)?;
             }
@@ -116,7 +116,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Find the minimum unsinged 16-bit integer in `op` and
             // returns its value and position.
             "phminposuw" => {
-                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [op] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (op, op_len) = this.project_to_simd(op)?;
                 let (dest, dest_len) = this.project_to_simd(dest)?;
@@ -151,7 +151,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mpsadbw_epu8
             "mpsadbw" => {
                 let [left, right, imm] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 mpsadbw(this, left, right, imm, dest)?;
             }
@@ -160,7 +160,8 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Tests `(op & mask) == 0`, `(op & mask) == mask` or
             // `(op & mask) != 0 && (op & mask) != mask`
             "ptestz" | "ptestc" | "ptestnzc" => {
-                let [op, mask] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [op, mask] =
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (all_zero, masked_set) = test_bits_masked(this, op, mask)?;
                 let res = match unprefixed_name {
diff --git a/src/tools/miri/src/shims/x86/sse42.rs b/src/tools/miri/src/shims/x86/sse42.rs
index f31cd406349..0b058a9911e 100644
--- a/src/tools/miri/src/shims/x86/sse42.rs
+++ b/src/tools/miri/src/shims/x86/sse42.rs
@@ -1,9 +1,8 @@
+use rustc_abi::{ExternAbi, Size};
 use rustc_middle::mir;
 use rustc_middle::ty::Ty;
 use rustc_middle::ty::layout::LayoutOf as _;
 use rustc_span::Symbol;
-use rustc_target::abi::Size;
-use rustc_target::spec::abi::Abi;
 
 use crate::*;
 
@@ -69,9 +68,9 @@ const USE_SIGNED: u8 = 2;
 /// The mask may be negated if negation flags inside the immediate byte are set.
 ///
 /// For more information, see the Intel Software Developer's Manual, Vol. 2b, Chapter 4.1.
-#[allow(clippy::arithmetic_side_effects)]
+#[expect(clippy::arithmetic_side_effects)]
 fn compare_strings<'tcx>(
-    this: &mut MiriInterpCx<'tcx>,
+    ecx: &mut MiriInterpCx<'tcx>,
     str1: &OpTy<'tcx>,
     str2: &OpTy<'tcx>,
     len: Option<(u64, u64)>,
@@ -81,8 +80,8 @@ fn compare_strings<'tcx>(
     let (len1, len2) = if let Some(t) = len {
         t
     } else {
-        let len1 = implicit_len(this, str1, imm)?.unwrap_or(default_len);
-        let len2 = implicit_len(this, str2, imm)?.unwrap_or(default_len);
+        let len1 = implicit_len(ecx, str1, imm)?.unwrap_or(default_len);
+        let len2 = implicit_len(ecx, str2, imm)?.unwrap_or(default_len);
         (len1, len2)
     };
 
@@ -91,12 +90,12 @@ fn compare_strings<'tcx>(
         0 => {
             // Equal any: Checks which characters of `str2` are inside `str1`.
             for i in 0..len2 {
-                let ch2 = this.read_immediate(&this.project_index(str2, i)?)?;
+                let ch2 = ecx.read_immediate(&ecx.project_index(str2, i)?)?;
 
                 for j in 0..len1 {
-                    let ch1 = this.read_immediate(&this.project_index(str1, j)?)?;
+                    let ch1 = ecx.read_immediate(&ecx.project_index(str1, j)?)?;
 
-                    let eq = this.binary_op(mir::BinOp::Eq, &ch1, &ch2)?;
+                    let eq = ecx.binary_op(mir::BinOp::Eq, &ch1, &ch2)?;
                     if eq.to_scalar().to_bool()? {
                         result |= 1 << i;
                         break;
@@ -120,9 +119,9 @@ fn compare_strings<'tcx>(
 
             for i in 0..len2 {
                 for j in (0..len1).step_by(2) {
-                    let ch2 = get_ch(this.read_scalar(&this.project_index(str2, i)?)?)?;
-                    let ch1_1 = get_ch(this.read_scalar(&this.project_index(str1, j)?)?)?;
-                    let ch1_2 = get_ch(this.read_scalar(&this.project_index(str1, j + 1)?)?)?;
+                    let ch2 = get_ch(ecx.read_scalar(&ecx.project_index(str2, i)?)?)?;
+                    let ch1_1 = get_ch(ecx.read_scalar(&ecx.project_index(str1, j)?)?)?;
+                    let ch1_2 = get_ch(ecx.read_scalar(&ecx.project_index(str1, j + 1)?)?)?;
 
                     if ch1_1 <= ch2 && ch2 <= ch1_2 {
                         result |= 1 << i;
@@ -136,9 +135,9 @@ fn compare_strings<'tcx>(
             result ^= (1 << len1.max(len2)) - 1;
 
             for i in 0..len1.min(len2) {
-                let ch1 = this.read_immediate(&this.project_index(str1, i)?)?;
-                let ch2 = this.read_immediate(&this.project_index(str2, i)?)?;
-                let eq = this.binary_op(mir::BinOp::Eq, &ch1, &ch2)?;
+                let ch1 = ecx.read_immediate(&ecx.project_index(str1, i)?)?;
+                let ch2 = ecx.read_immediate(&ecx.project_index(str2, i)?)?;
+                let eq = ecx.binary_op(mir::BinOp::Eq, &ch1, &ch2)?;
                 result |= i32::from(eq.to_scalar().to_bool()?) << i;
             }
         }
@@ -160,9 +159,9 @@ fn compare_strings<'tcx>(
                         if k >= default_len {
                             break;
                         } else {
-                            let ch1 = this.read_immediate(&this.project_index(str1, j)?)?;
-                            let ch2 = this.read_immediate(&this.project_index(str2, k)?)?;
-                            let ne = this.binary_op(mir::BinOp::Ne, &ch1, &ch2)?;
+                            let ch1 = ecx.read_immediate(&ecx.project_index(str1, j)?)?;
+                            let ch2 = ecx.read_immediate(&ecx.project_index(str2, k)?)?;
+                            let ne = ecx.binary_op(mir::BinOp::Ne, &ch1, &ch2)?;
 
                             if ne.to_scalar().to_bool()? {
                                 result &= !(1 << i);
@@ -199,16 +198,16 @@ fn compare_strings<'tcx>(
 /// corresponding to the x86 128-bit integer SIMD type.
 fn deconstruct_args<'tcx>(
     unprefixed_name: &str,
-    this: &mut MiriInterpCx<'tcx>,
+    ecx: &mut MiriInterpCx<'tcx>,
     link_name: Symbol,
-    abi: Abi,
+    abi: ExternAbi,
     args: &[OpTy<'tcx>],
 ) -> InterpResult<'tcx, (OpTy<'tcx>, OpTy<'tcx>, Option<(u64, u64)>, u8)> {
-    let array_layout_fn = |this: &mut MiriInterpCx<'tcx>, imm: u8| {
+    let array_layout_fn = |ecx: &mut MiriInterpCx<'tcx>, imm: u8| {
         if imm & USE_WORDS != 0 {
-            this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u16, 8))
+            ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u16, 8))
         } else {
-            this.layout_of(Ty::new_array(this.tcx.tcx, this.tcx.types.u8, 16))
+            ecx.layout_of(Ty::new_array(ecx.tcx.tcx, ecx.tcx.types.u8, 16))
         }
     };
 
@@ -224,25 +223,26 @@ fn deconstruct_args<'tcx>(
 
     if is_explicit {
         let [str1, len1, str2, len2, imm] =
-            this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
-        let imm = this.read_scalar(imm)?.to_u8()?;
+            ecx.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
+        let imm = ecx.read_scalar(imm)?.to_u8()?;
 
         let default_len = default_len::<u32>(imm);
-        let len1 = u64::from(this.read_scalar(len1)?.to_u32()?.min(default_len));
-        let len2 = u64::from(this.read_scalar(len2)?.to_u32()?.min(default_len));
+        let len1 = u64::from(ecx.read_scalar(len1)?.to_u32()?.min(default_len));
+        let len2 = u64::from(ecx.read_scalar(len2)?.to_u32()?.min(default_len));
 
-        let array_layout = array_layout_fn(this, imm)?;
-        let str1 = str1.transmute(array_layout, this)?;
-        let str2 = str2.transmute(array_layout, this)?;
+        let array_layout = array_layout_fn(ecx, imm)?;
+        let str1 = str1.transmute(array_layout, ecx)?;
+        let str2 = str2.transmute(array_layout, ecx)?;
 
         interp_ok((str1, str2, Some((len1, len2)), imm))
     } else {
-        let [str1, str2, imm] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
-        let imm = this.read_scalar(imm)?.to_u8()?;
+        let [str1, str2, imm] =
+            ecx.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
+        let imm = ecx.read_scalar(imm)?.to_u8()?;
 
-        let array_layout = array_layout_fn(this, imm)?;
-        let str1 = str1.transmute(array_layout, this)?;
-        let str2 = str2.transmute(array_layout, this)?;
+        let array_layout = array_layout_fn(ecx, imm)?;
+        let str1 = str1.transmute(array_layout, ecx)?;
+        let str2 = str2.transmute(array_layout, ecx)?;
 
         interp_ok((str1, str2, None, imm))
     }
@@ -251,16 +251,16 @@ fn deconstruct_args<'tcx>(
 /// Calculate the c-style string length for a given string `str`.
 /// The string is either a length 16 array of bytes a length 8 array of two-byte words.
 fn implicit_len<'tcx>(
-    this: &mut MiriInterpCx<'tcx>,
+    ecx: &mut MiriInterpCx<'tcx>,
     str: &OpTy<'tcx>,
     imm: u8,
 ) -> InterpResult<'tcx, Option<u64>> {
     let mut result = None;
-    let zero = ImmTy::from_int(0, str.layout.field(this, 0));
+    let zero = ImmTy::from_int(0, str.layout.field(ecx, 0));
 
     for i in 0..default_len::<u64>(imm) {
-        let ch = this.read_immediate(&this.project_index(str, i)?)?;
-        let is_zero = this.binary_op(mir::BinOp::Eq, &ch, &zero)?;
+        let ch = ecx.read_immediate(&ecx.project_index(str, i)?)?;
+        let is_zero = ecx.binary_op(mir::BinOp::Eq, &ch, &zero)?;
         if is_zero.to_scalar().to_bool()? {
             result = Some(i);
             break;
@@ -279,7 +279,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_x86_sse42_intrinsic(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -388,7 +388,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=924,925
             "pcmpistriz128" | "pcmpistris128" => {
                 let [str1, str2, imm] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let imm = this.read_scalar(imm)?.to_u8()?;
 
                 let str = if unprefixed_name == "pcmpistris128" { str1 } else { str2 };
@@ -409,7 +409,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#ig_expand=1046,1047
             "pcmpestriz128" | "pcmpestris128" => {
                 let [_, len1, _, len2, imm] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let len = if unprefixed_name == "pcmpestris128" { len1 } else { len2 };
                 let len = this.read_scalar(len)?.to_i32()?;
                 let imm = this.read_scalar(imm)?.to_u8()?;
@@ -437,14 +437,14 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
                 }
 
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
                 let left = this.read_scalar(left)?;
                 let right = this.read_scalar(right)?;
 
                 let crc = if bit_size == 64 {
                     // The 64-bit version will only consider the lower 32 bits,
                     // while the upper 32 bits get discarded.
-                    #[allow(clippy::cast_possible_truncation)]
+                    #[expect(clippy::cast_possible_truncation)]
                     u128::from((left.to_u64()? as u32).reverse_bits())
                 } else {
                     u128::from(left.to_u32()?.reverse_bits())
diff --git a/src/tools/miri/src/shims/x86/ssse3.rs b/src/tools/miri/src/shims/x86/ssse3.rs
index 76a2451dea4..cdab78bc742 100644
--- a/src/tools/miri/src/shims/x86/ssse3.rs
+++ b/src/tools/miri/src/shims/x86/ssse3.rs
@@ -1,6 +1,6 @@
+use rustc_abi::ExternAbi;
 use rustc_middle::mir;
 use rustc_span::Symbol;
-use rustc_target::spec::abi::Abi;
 
 use super::{horizontal_bin_op, int_abs, pmulhrsw, psign};
 use crate::*;
@@ -10,7 +10,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
     fn emulate_x86_ssse3_intrinsic(
         &mut self,
         link_name: Symbol,
-        abi: Abi,
+        abi: ExternAbi,
         args: &[OpTy<'tcx>],
         dest: &MPlaceTy<'tcx>,
     ) -> InterpResult<'tcx, EmulateItemResult> {
@@ -23,7 +23,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Used to implement the _mm_abs_epi{8,16,32} functions.
             // Calculates the absolute value of packed 8/16/32-bit integers.
             "pabs.b.128" | "pabs.w.128" | "pabs.d.128" => {
-                let [op] = this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                let [op] = this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 int_abs(this, op, dest)?;
             }
@@ -32,7 +32,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_shuffle_epi8
             "pshuf.b.128" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (left, left_len) = this.project_to_simd(left)?;
                 let (right, right_len) = this.project_to_simd(right)?;
@@ -62,7 +62,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             "phadd.w.128" | "phadd.sw.128" | "phadd.d.128" | "phsub.w.128" | "phsub.sw.128"
             | "phsub.d.128" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (which, saturating) = match unprefixed_name {
                     "phadd.w.128" | "phadd.d.128" => (mir::BinOp::Add, false),
@@ -82,7 +82,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_maddubs_epi16
             "pmadd.ub.sw.128" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 let (left, left_len) = this.project_to_simd(left)?;
                 let (right, right_len) = this.project_to_simd(right)?;
@@ -118,7 +118,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // https://www.intel.com/content/www/us/en/docs/intrinsics-guide/index.html#text=_mm_mulhrs_epi16
             "pmul.hr.sw.128" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 pmulhrsw(this, left, right, dest)?;
             }
@@ -129,7 +129,7 @@ pub(super) trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
             // Basically, we multiply `left` with `right.signum()`.
             "psign.b.128" | "psign.w.128" | "psign.d.128" => {
                 let [left, right] =
-                    this.check_shim(abi, Abi::C { unwind: false }, link_name, args)?;
+                    this.check_shim(abi, ExternAbi::C { unwind: false }, link_name, args)?;
 
                 psign(this, left, right, dest)?;
             }
diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs
new file mode 100644
index 00000000000..65d29b2c6ba
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.rs
@@ -0,0 +1,65 @@
+//@only-target: linux
+//~^ERROR: deadlocked
+//~^^ERROR: deadlocked
+//@compile-flags: -Zmiri-preemption-rate=0
+//@error-in-other-file: deadlock
+
+use std::thread;
+
+// Test the behaviour of a thread being blocked on an eventfd read, get unblocked, and then
+// get blocked again.
+
+// The expected execution is
+// 1. Thread 1 blocks.
+// 2. Thread 2 blocks.
+// 3. Thread 3 unblocks both thread 1 and thread 2.
+// 4. Thread 1 reads.
+// 5. Thread 2's `read` deadlocked.
+
+fn main() {
+    // eventfd write will block when EFD_NONBLOCK flag is clear
+    // and the addition caused counter to exceed u64::MAX - 1.
+    let flags = libc::EFD_CLOEXEC;
+    let fd = unsafe { libc::eventfd(0, flags) };
+
+    let thread1 = thread::spawn(move || {
+        thread::park();
+        let mut buf: [u8; 8] = [0; 8];
+        // This read will block initially.
+        let res: i64 = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), 8).try_into().unwrap() };
+        assert_eq!(res, 8);
+        let counter = u64::from_ne_bytes(buf);
+        assert_eq!(counter, 1_u64);
+    });
+
+    let thread2 = thread::spawn(move || {
+        thread::park();
+        let mut buf: [u8; 8] = [0; 8];
+        // This read will block initially, then get unblocked by thread3, then get blocked again
+        // because the `read` in thread1 executes first and set the counter to 0 again.
+        let res: i64 = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), 8).try_into().unwrap() };
+        //~^ERROR: deadlocked
+        assert_eq!(res, 8);
+        let counter = u64::from_ne_bytes(buf);
+        assert_eq!(counter, 1_u64);
+    });
+
+    let thread3 = thread::spawn(move || {
+        thread::park();
+        let sized_8_data = 1_u64.to_ne_bytes();
+        // Write 1 to the counter, so both thread1 and thread2 will unblock.
+        let res: i64 = unsafe {
+            libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+        };
+        // Make sure that write is successful.
+        assert_eq!(res, 8);
+    });
+
+    thread1.thread().unpark();
+    thread2.thread().unpark();
+    thread3.thread().unpark();
+
+    thread1.join().unwrap();
+    thread2.join().unwrap();
+    thread3.join().unwrap();
+}
diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr
new file mode 100644
index 00000000000..bb235345c5e
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_read_twice.stderr
@@ -0,0 +1,41 @@
+error: deadlock: the evaluated program deadlocked
+  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   |
+LL |         let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
+   |                                                                  ^ the evaluated program deadlocked
+   |
+   = note: BACKTRACE:
+   = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+   = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+note: inside `main`
+  --> tests/fail-dep/libc/eventfd_block_read_twice.rs:LL:CC
+   |
+LL |     thread2.join().unwrap();
+   |     ^^^^^^^^^^^^^^
+
+error: deadlock: the evaluated program deadlocked
+   |
+   = note: the evaluated program deadlocked
+   = note: (no span available)
+   = note: BACKTRACE on thread `unnamed-ID`:
+
+error: deadlock: the evaluated program deadlocked
+  --> tests/fail-dep/libc/eventfd_block_read_twice.rs:LL:CC
+   |
+LL |         let res: i64 = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), 8).try_into().unwrap() };
+   |                                                                          ^ the evaluated program deadlocked
+   |
+   = note: BACKTRACE on thread `unnamed-ID`:
+   = note: inside closure at tests/fail-dep/libc/eventfd_block_read_twice.rs:LL:CC
+
+error: deadlock: the evaluated program deadlocked
+   |
+   = note: the evaluated program deadlocked
+   = note: (no span available)
+   = note: BACKTRACE on thread `unnamed-ID`:
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs
new file mode 100644
index 00000000000..f9d34d2fb58
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.rs
@@ -0,0 +1,71 @@
+//@only-target: linux
+//~^ERROR: deadlocked
+//~^^ERROR: deadlocked
+//@compile-flags: -Zmiri-preemption-rate=0
+//@error-in-other-file: deadlock
+
+use std::thread;
+
+// Test the behaviour of a thread being blocked on an eventfd `write`, get unblocked, and then
+// get blocked again.
+
+// The expected execution is
+// 1. Thread 1 blocks.
+// 2. Thread 2 blocks.
+// 3. Thread 3 unblocks both thread 1 and thread 2.
+// 4. Thread 1 writes u64::MAX.
+// 5. Thread 2's `write` deadlocked.
+fn main() {
+    // eventfd write will block when EFD_NONBLOCK flag is clear
+    // and the addition caused counter to exceed u64::MAX - 1.
+    let flags = libc::EFD_CLOEXEC;
+    let fd = unsafe { libc::eventfd(0, flags) };
+    // Write u64 - 1, so the all subsequent write will block.
+    let sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes();
+    let res: i64 = unsafe {
+        libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+    };
+    assert_eq!(res, 8);
+
+    let thread1 = thread::spawn(move || {
+        thread::park();
+        let sized_8_data = (u64::MAX - 1).to_ne_bytes();
+        let res: i64 = unsafe {
+            libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+        };
+        // Make sure that write is successful.
+        assert_eq!(res, 8);
+    });
+
+    let thread2 = thread::spawn(move || {
+        thread::park();
+        let sized_8_data = (u64::MAX - 1).to_ne_bytes();
+        // Write u64::MAX - 1, so the all subsequent write will block.
+        let res: i64 = unsafe {
+            // This `write` will initially blocked, then get unblocked by thread3, then get blocked again
+            // because the `write` in thread1 executes first.
+            libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+            //~^ERROR: deadlocked
+        };
+        // Make sure that write is successful.
+        assert_eq!(res, 8);
+    });
+
+    let thread3 = thread::spawn(move || {
+        thread::park();
+        let mut buf: [u8; 8] = [0; 8];
+        // This will unblock both `write` in thread1 and thread2.
+        let res: i64 = unsafe { libc::read(fd, buf.as_mut_ptr().cast(), 8).try_into().unwrap() };
+        assert_eq!(res, 8);
+        let counter = u64::from_ne_bytes(buf);
+        assert_eq!(counter, (u64::MAX - 1));
+    });
+
+    thread1.thread().unpark();
+    thread2.thread().unpark();
+    thread3.thread().unpark();
+
+    thread1.join().unwrap();
+    thread2.join().unwrap();
+    thread3.join().unwrap();
+}
diff --git a/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr
new file mode 100644
index 00000000000..d9163a5748c
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/eventfd_block_write_twice.stderr
@@ -0,0 +1,41 @@
+error: deadlock: the evaluated program deadlocked
+  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   |
+LL |         let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
+   |                                                                  ^ the evaluated program deadlocked
+   |
+   = note: BACKTRACE:
+   = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+   = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+note: inside `main`
+  --> tests/fail-dep/libc/eventfd_block_write_twice.rs:LL:CC
+   |
+LL |     thread2.join().unwrap();
+   |     ^^^^^^^^^^^^^^
+
+error: deadlock: the evaluated program deadlocked
+   |
+   = note: the evaluated program deadlocked
+   = note: (no span available)
+   = note: BACKTRACE on thread `unnamed-ID`:
+
+error: deadlock: the evaluated program deadlocked
+  --> tests/fail-dep/libc/eventfd_block_write_twice.rs:LL:CC
+   |
+LL |             libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+   |                                                                            ^ the evaluated program deadlocked
+   |
+   = note: BACKTRACE on thread `unnamed-ID`:
+   = note: inside closure at tests/fail-dep/libc/eventfd_block_write_twice.rs:LL:CC
+
+error: deadlock: the evaluated program deadlocked
+   |
+   = note: the evaluated program deadlocked
+   = note: (no span available)
+   = note: BACKTRACE on thread `unnamed-ID`:
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs
new file mode 100644
index 00000000000..7f5ec477e19
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.rs
@@ -0,0 +1,96 @@
+//@compile-flags: -Zmiri-preemption-rate=0
+//~^ERROR: deadlocked
+//~^^ERROR: deadlocked
+//@only-target: linux
+//@error-in-other-file: deadlock
+
+use std::convert::TryInto;
+use std::thread;
+use std::thread::spawn;
+
+// Using `as` cast since `EPOLLET` wraps around
+const EPOLL_IN_OUT_ET: u32 = (libc::EPOLLIN | libc::EPOLLOUT | libc::EPOLLET) as _;
+
+#[track_caller]
+fn check_epoll_wait<const N: usize>(
+    epfd: i32,
+    expected_notifications: &[(u32, u64)],
+    timeout: i32,
+) {
+    let epoll_event = libc::epoll_event { events: 0, u64: 0 };
+    let mut array: [libc::epoll_event; N] = [epoll_event; N];
+    let maxsize = N;
+    let array_ptr = array.as_mut_ptr();
+    let res = unsafe { libc::epoll_wait(epfd, array_ptr, maxsize.try_into().unwrap(), timeout) };
+    if res < 0 {
+        panic!("epoll_wait failed: {}", std::io::Error::last_os_error());
+    }
+    assert_eq!(
+        res,
+        expected_notifications.len().try_into().unwrap(),
+        "got wrong number of notifications"
+    );
+    let slice = unsafe { std::slice::from_raw_parts(array_ptr, res.try_into().unwrap()) };
+    for (return_event, expected_event) in slice.iter().zip(expected_notifications.iter()) {
+        let event = return_event.events;
+        let data = return_event.u64;
+        assert_eq!(event, expected_event.0, "got wrong events");
+        assert_eq!(data, expected_event.1, "got wrong data");
+    }
+}
+
+// Test if only one thread is unblocked if multiple threads blocked on same epfd.
+// Expected execution:
+// 1. Thread 2 blocks.
+// 2. Thread 3 blocks.
+// 3. Thread 1 unblocks thread 3.
+// 4. Thread 2 deadlocks.
+fn main() {
+    // Create an epoll instance.
+    let epfd = unsafe { libc::epoll_create1(0) };
+    assert_ne!(epfd, -1);
+
+    // Create a socketpair instance.
+    let mut fds = [-1, -1];
+    let res = unsafe { libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr()) };
+    assert_eq!(res, 0);
+
+    // Register one side of the socketpair with epoll.
+    let mut ev = libc::epoll_event { events: EPOLL_IN_OUT_ET, u64: fds[0] as u64 };
+    let res = unsafe { libc::epoll_ctl(epfd, libc::EPOLL_CTL_ADD, fds[0], &mut ev) };
+    assert_eq!(res, 0);
+
+    // epoll_wait to clear notification.
+    let expected_event = u32::try_from(libc::EPOLLOUT).unwrap();
+    let expected_value = fds[0] as u64;
+    check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], 0);
+
+    let thread1 = spawn(move || {
+        thread::park();
+        let data = "abcde".as_bytes().as_ptr();
+        let res = unsafe { libc::write(fds[1], data as *const libc::c_void, 5) };
+        assert_eq!(res, 5);
+    });
+
+    let expected_event = u32::try_from(libc::EPOLLIN | libc::EPOLLOUT).unwrap();
+    let expected_value = fds[0] as u64;
+    let thread2 = spawn(move || {
+        thread::park();
+        check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], -1);
+        //~^ERROR: deadlocked
+    });
+    let thread3 = spawn(move || {
+        thread::park();
+        check_epoll_wait::<1>(epfd, &[(expected_event, expected_value)], -1);
+    });
+
+    thread2.thread().unpark();
+    thread::yield_now();
+    thread3.thread().unpark();
+    thread::yield_now();
+    thread1.thread().unpark();
+
+    thread1.join().unwrap();
+    thread2.join().unwrap();
+    thread3.join().unwrap();
+}
diff --git a/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr
new file mode 100644
index 00000000000..010dabc1364
--- /dev/null
+++ b/src/tools/miri/tests/fail-dep/libc/libc_epoll_block_two_thread.stderr
@@ -0,0 +1,41 @@
+error: deadlock: the evaluated program deadlocked
+  --> RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   |
+LL |         let ret = unsafe { libc::pthread_join(id, ptr::null_mut()) };
+   |                                                                  ^ the evaluated program deadlocked
+   |
+   = note: BACKTRACE:
+   = note: inside `std::sys::pal::PLATFORM::thread::Thread::join` at RUSTLIB/std/src/sys/pal/PLATFORM/thread.rs:LL:CC
+   = note: inside `std::thread::JoinInner::<'_, ()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+   = note: inside `std::thread::JoinHandle::<()>::join` at RUSTLIB/std/src/thread/mod.rs:LL:CC
+note: inside `main`
+  --> tests/fail-dep/libc/libc_epoll_block_two_thread.rs:LL:CC
+   |
+LL |     thread2.join().unwrap();
+   |     ^^^^^^^^^^^^^^
+
+error: deadlock: the evaluated program deadlocked
+   |
+   = note: the evaluated program deadlocked
+   = note: (no span available)
+   = note: BACKTRACE on thread `unnamed-ID`:
+
+error: deadlock: the evaluated program deadlocked
+  --> tests/fail-dep/libc/libc_epoll_block_two_thread.rs:LL:CC
+   |
+LL |         check_epoll_wait::<TAG>(epfd, &[(expected_event, expected_value)], -1);
+   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program deadlocked
+   |
+   = note: BACKTRACE on thread `unnamed-ID`:
+   = note: inside closure at tests/fail-dep/libc/libc_epoll_block_two_thread.rs:LL:CC
+
+error: deadlock: the evaluated program deadlocked
+   |
+   = note: the evaluated program deadlocked
+   = note: (no span available)
+   = note: BACKTRACE on thread `unnamed-ID`:
+
+note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
+
+error: aborting due to 4 previous errors
+
diff --git a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.rs b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.rs
deleted file mode 100644
index 0212a63bd0f..00000000000
--- a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.rs
+++ /dev/null
@@ -1,11 +0,0 @@
-//@only-target: linux
-fn main() {
-    // eventfd read will block when EFD_NONBLOCK flag is clear and counter = 0.
-    // This will pass when blocking is implemented.
-    let flags = libc::EFD_CLOEXEC;
-    let fd = unsafe { libc::eventfd(0, flags) };
-    let mut buf: [u8; 8] = [0; 8];
-    let _res: i32 = unsafe {
-        libc::read(fd, buf.as_mut_ptr().cast(), buf.len() as libc::size_t).try_into().unwrap() //~ERROR: blocking is unsupported
-    };
-}
diff --git a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.stderr b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.stderr
deleted file mode 100644
index aff30c81ebd..00000000000
--- a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_read_block.stderr
+++ /dev/null
@@ -1,14 +0,0 @@
-error: unsupported operation: eventfd: blocking is unsupported
-  --> tests/fail-dep/libc/libc_eventfd_read_block.rs:LL:CC
-   |
-LL |         libc::read(fd, buf.as_mut_ptr().cast(), buf.len() as libc::size_t).try_into().unwrap()
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ eventfd: blocking is unsupported
-   |
-   = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
-   = note: BACKTRACE:
-   = note: inside `main` at tests/fail-dep/libc/libc_eventfd_read_block.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_eventfd_write_block.rs b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.rs
deleted file mode 100644
index ed6ad466901..00000000000
--- a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.rs
+++ /dev/null
@@ -1,21 +0,0 @@
-//@only-target: linux
-fn main() {
-    // eventfd write will block when EFD_NONBLOCK flag is clear
-    // and the addition caused counter to exceed u64::MAX - 1.
-    // This will pass when blocking is implemented.
-    let flags = libc::EFD_CLOEXEC;
-    let fd = unsafe { libc::eventfd(0, flags) };
-    // Write u64 - 1.
-    let mut sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes();
-    let res: i64 = unsafe {
-        libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
-    };
-    assert_eq!(res, 8);
-
-    // Write 1.
-    sized_8_data = 1_u64.to_ne_bytes();
-    // Write 1 to the counter.
-    let _res: i64 = unsafe {
-        libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap() //~ERROR: blocking is unsupported
-    };
-}
diff --git a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.stderr b/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.stderr
deleted file mode 100644
index 2b606605794..00000000000
--- a/src/tools/miri/tests/fail-dep/libc/libc_eventfd_write_block.stderr
+++ /dev/null
@@ -1,14 +0,0 @@
-error: unsupported operation: eventfd: blocking is unsupported
-  --> tests/fail-dep/libc/libc_eventfd_write_block.rs:LL:CC
-   |
-LL |         libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
-   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ eventfd: blocking is unsupported
-   |
-   = help: this is likely not a bug in the program; it indicates that the program performed an operation that Miri does not support
-   = note: BACKTRACE:
-   = note: inside `main` at tests/fail-dep/libc/libc_eventfd_write_block.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/intrinsic_fallback_is_spec.rs b/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.rs
index fa7c0bf5c0c..e95ef4d9062 100644
--- a/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.rs
+++ b/src/tools/miri/tests/fail/intrinsic_fallback_is_spec.rs
@@ -1,4 +1,4 @@
-#![feature(rustc_attrs)]
+#![feature(intrinsics, rustc_attrs)]
 
 #[rustc_intrinsic]
 #[rustc_nounwind]
diff --git a/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.rs b/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.rs
index a47bb671e32..a2e8a533c43 100644
--- a/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.rs
+++ b/src/tools/miri/tests/fail/tree_borrows/write-during-2phase.rs
@@ -8,8 +8,8 @@
 
 struct Foo(u64);
 impl Foo {
-    #[rustfmt::skip] // rustfmt is wrong about which line contains an error
-    fn add(&mut self, n: u64) -> u64 { //~ ERROR: /reborrow through .* is forbidden/
+    fn add(&mut self, n: u64) -> u64 {
+        //~^ ERROR: /reborrow through .* is forbidden/
         self.0 + n
     }
 }
diff --git a/src/tools/miri/tests/pass-dep/concurrency/linux-futex.rs b/src/tools/miri/tests/pass-dep/concurrency/linux-futex.rs
index 20e642a0a29..3adeb89ecec 100644
--- a/src/tools/miri/tests/pass-dep/concurrency/linux-futex.rs
+++ b/src/tools/miri/tests/pass-dep/concurrency/linux-futex.rs
@@ -1,4 +1,4 @@
-//@only-target: linux
+//@only-target: linux android
 //@compile-flags: -Zmiri-disable-isolation
 
 // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
@@ -7,8 +7,8 @@
 use std::mem::MaybeUninit;
 use std::ptr::{self, addr_of};
 use std::sync::atomic::{AtomicI32, Ordering};
-use std::thread;
 use std::time::{Duration, Instant};
+use std::{io, thread};
 
 fn wake_nobody() {
     let futex = 0;
@@ -40,9 +40,12 @@ fn wake_dangling() {
     let ptr: *const i32 = &*futex;
     drop(futex);
 
-    // Wake 1 waiter. Expect zero waiters woken up, as nobody is waiting.
+    // Expect error since this is now "unmapped" memory.
+    // parking_lot relies on this:
+    // <https://github.com/Amanieu/parking_lot/blob/ca920b31312839013b4455aba1d53a4aede21b2f/core/src/thread_parker/linux.rs#L138-L145>
     unsafe {
-        assert_eq!(libc::syscall(libc::SYS_futex, ptr, libc::FUTEX_WAKE, 1), 0);
+        assert_eq!(libc::syscall(libc::SYS_futex, ptr, libc::FUTEX_WAKE, 1), -1);
+        assert_eq!(io::Error::last_os_error().raw_os_error().unwrap(), libc::EFAULT);
     }
 }
 
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs
index c92d9c3fe70..dd9c0eb0b54 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-eventfd.rs
@@ -1,5 +1,5 @@
 //@only-target: linux
-// test_race depends on a deterministic schedule.
+// test_race, test_blocking_read and test_blocking_write depend on a deterministic schedule.
 //@compile-flags: -Zmiri-preemption-rate=0
 
 // FIXME(static_mut_refs): Do not allow `static_mut_refs` lint
@@ -11,6 +11,9 @@ fn main() {
     test_read_write();
     test_race();
     test_syscall();
+    test_blocking_read();
+    test_blocking_write();
+    test_two_threads_blocked_on_eventfd();
 }
 
 fn read_bytes<const N: usize>(fd: i32, buf: &mut [u8; N]) -> i32 {
@@ -118,3 +121,117 @@ fn test_syscall() {
     let fd = unsafe { libc::syscall(libc::SYS_eventfd2, initval, flags) };
     assert_ne!(fd, -1);
 }
+
+// This test will block on eventfd read then get unblocked by `write`.
+fn test_blocking_read() {
+    // eventfd read will block when EFD_NONBLOCK flag is clear and counter = 0.
+    let flags = libc::EFD_CLOEXEC;
+    let fd = unsafe { libc::eventfd(0, flags) };
+    let thread1 = thread::spawn(move || {
+        let mut buf: [u8; 8] = [0; 8];
+        // This will block.
+        let res = read_bytes(fd, &mut buf);
+        // read returns number of bytes has been read, which is always 8.
+        assert_eq!(res, 8);
+        let counter = u64::from_ne_bytes(buf);
+        assert_eq!(counter, 1);
+    });
+    let sized_8_data: [u8; 8] = 1_u64.to_ne_bytes();
+    // Pass control to thread1 so it can block on eventfd `read`.
+    thread::yield_now();
+    // Write 1 to the counter to unblock thread1.
+    let res = write_bytes(fd, sized_8_data);
+    assert_eq!(res, 8);
+    thread1.join().unwrap();
+}
+
+/// This test will block on eventfd `write` then get unblocked by `read`.
+fn test_blocking_write() {
+    // eventfd write will block when EFD_NONBLOCK flag is clear
+    // and the addition caused counter to exceed u64::MAX - 1.
+    let flags = libc::EFD_CLOEXEC;
+    let fd = unsafe { libc::eventfd(0, flags) };
+    // Write u64 - 1, so the all subsequent write will block.
+    let sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes();
+    let res: i64 = unsafe {
+        libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+    };
+    assert_eq!(res, 8);
+
+    let thread1 = thread::spawn(move || {
+        let sized_8_data = 1_u64.to_ne_bytes();
+        // Write 1 to the counter, this will block.
+        let res: i64 = unsafe {
+            libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+        };
+        // Make sure that write is successful.
+        assert_eq!(res, 8);
+    });
+    let mut buf: [u8; 8] = [0; 8];
+    // Pass control to thread1 so it can block on eventfd `write`.
+    thread::yield_now();
+    // This will unblock previously blocked eventfd read.
+    let res = read_bytes(fd, &mut buf);
+    // read returns number of bytes has been read, which is always 8.
+    assert_eq!(res, 8);
+    let counter = u64::from_ne_bytes(buf);
+    assert_eq!(counter, (u64::MAX - 1));
+    thread1.join().unwrap();
+}
+
+// Test two threads blocked on eventfd.
+// Expected behaviour:
+// 1. thread1 and thread2 both blocked on `write`.
+// 2. thread3 unblocks both thread1 and thread2
+// 3. The write in thread1 and thread2 return successfully.
+fn test_two_threads_blocked_on_eventfd() {
+    // eventfd write will block when EFD_NONBLOCK flag is clear
+    // and the addition caused counter to exceed u64::MAX - 1.
+    let flags = libc::EFD_CLOEXEC;
+    let fd = unsafe { libc::eventfd(0, flags) };
+    // Write u64 - 1, so the all subsequent write will block.
+    let sized_8_data: [u8; 8] = (u64::MAX - 1).to_ne_bytes();
+    let res: i64 = unsafe {
+        libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+    };
+    assert_eq!(res, 8);
+
+    let thread1 = thread::spawn(move || {
+        thread::park();
+        let sized_8_data = 1_u64.to_ne_bytes();
+        let res: i64 = unsafe {
+            libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+        };
+        // Make sure that write is successful.
+        assert_eq!(res, 8);
+    });
+
+    let thread2 = thread::spawn(move || {
+        thread::park();
+        let sized_8_data = 1_u64.to_ne_bytes();
+        let res: i64 = unsafe {
+            libc::write(fd, sized_8_data.as_ptr() as *const libc::c_void, 8).try_into().unwrap()
+        };
+        // Make sure that write is successful.
+        assert_eq!(res, 8);
+    });
+
+    let thread3 = thread::spawn(move || {
+        thread::park();
+        let mut buf: [u8; 8] = [0; 8];
+        // This will unblock previously blocked eventfd read.
+        let res = read_bytes(fd, &mut buf);
+        // read returns number of bytes has been read, which is always 8.
+        assert_eq!(res, 8);
+        let counter = u64::from_ne_bytes(buf);
+        assert_eq!(counter, (u64::MAX - 1));
+    });
+
+    thread1.thread().unpark();
+    thread2.thread().unpark();
+    thread3.thread().unpark();
+
+    thread1.join().unwrap();
+    thread2.join().unwrap();
+    thread3.join().unwrap();
+}
diff --git a/src/tools/miri/tests/pass-dep/libc/libc-random.rs b/src/tools/miri/tests/pass-dep/libc/libc-random.rs
index 8f4398cbd8f..7c4010f6c0a 100644
--- a/src/tools/miri/tests/pass-dep/libc/libc-random.rs
+++ b/src/tools/miri/tests/pass-dep/libc/libc-random.rs
@@ -28,26 +28,27 @@ fn test_getrandom() {
 
     let mut buf = [0u8; 5];
     unsafe {
-        #[cfg(target_os = "linux")]
-        assert_eq!(
-            libc::syscall(
-                libc::SYS_getrandom,
-                ptr::null_mut::<libc::c_void>(),
-                0 as libc::size_t,
-                0 as libc::c_uint,
-            ),
-            0,
-        );
-        #[cfg(target_os = "linux")]
-        assert_eq!(
-            libc::syscall(
-                libc::SYS_getrandom,
-                buf.as_mut_ptr() as *mut libc::c_void,
-                5 as libc::size_t,
-                0 as libc::c_uint,
-            ),
-            5,
-        );
+        #[cfg(any(target_os = "linux", target_os = "android"))]
+        {
+            assert_eq!(
+                libc::syscall(
+                    libc::SYS_getrandom,
+                    ptr::null_mut::<libc::c_void>(),
+                    0 as libc::size_t,
+                    0 as libc::c_uint,
+                ),
+                0,
+            );
+            assert_eq!(
+                libc::syscall(
+                    libc::SYS_getrandom,
+                    buf.as_mut_ptr() as *mut libc::c_void,
+                    5 as libc::size_t,
+                    0 as libc::c_uint,
+                ),
+                5,
+            );
+        }
 
         assert_eq!(
             libc::getrandom(ptr::null_mut::<libc::c_void>(), 0 as libc::size_t, 0 as libc::c_uint),
diff --git a/src/tools/miri/tests/pass-dep/libc/pthread-sync.rs b/src/tools/miri/tests/pass-dep/libc/pthread-sync.rs
index 75848bd44db..fa11b5b1299 100644
--- a/src/tools/miri/tests/pass-dep/libc/pthread-sync.rs
+++ b/src/tools/miri/tests/pass-dep/libc/pthread-sync.rs
@@ -22,6 +22,22 @@ fn main() {
     check_condattr();
 }
 
+// We want to only use pthread APIs here for easier testing.
+// So we can't use `thread::scope`. That means panics can lead
+// to a failure to join threads which can lead to further issues,
+// so let's turn such unwinding into aborts.
+struct AbortOnDrop;
+impl AbortOnDrop {
+    fn defuse(self) {
+        mem::forget(self);
+    }
+}
+impl Drop for AbortOnDrop {
+    fn drop(&mut self) {
+        std::process::abort();
+    }
+}
+
 fn test_mutex_libc_init_recursive() {
     unsafe {
         let mut attr: libc::pthread_mutexattr_t = mem::zeroed();
@@ -122,6 +138,7 @@ impl<T> Clone for SendPtr<T> {
 }
 
 fn check_mutex() {
+    let bomb = AbortOnDrop;
     // Specifically *not* using `Arc` to make sure there is no synchronization apart from the mutex.
     unsafe {
         let data = SyncUnsafeCell::new((libc::PTHREAD_MUTEX_INITIALIZER, 0));
@@ -148,9 +165,11 @@ fn check_mutex() {
         assert_eq!(libc::pthread_mutex_trylock(mutexptr), 0);
         assert_eq!((*ptr.ptr).1, 3);
     }
+    bomb.defuse();
 }
 
 fn check_rwlock_write() {
+    let bomb = AbortOnDrop;
     unsafe {
         let data = SyncUnsafeCell::new((libc::PTHREAD_RWLOCK_INITIALIZER, 0));
         let ptr = SendPtr { ptr: data.get() };
@@ -187,9 +206,11 @@ fn check_rwlock_write() {
         assert_eq!(libc::pthread_rwlock_tryrdlock(rwlockptr), 0);
         assert_eq!((*ptr.ptr).1, 3);
     }
+    bomb.defuse();
 }
 
 fn check_rwlock_read_no_deadlock() {
+    let bomb = AbortOnDrop;
     unsafe {
         let l1 = SyncUnsafeCell::new(libc::PTHREAD_RWLOCK_INITIALIZER);
         let l1 = SendPtr { ptr: l1.get() };
@@ -213,9 +234,11 @@ fn check_rwlock_read_no_deadlock() {
         assert_eq!(libc::pthread_rwlock_rdlock(l2.ptr), 0);
         handle.join().unwrap();
     }
+    bomb.defuse();
 }
 
 fn check_cond() {
+    let bomb = AbortOnDrop;
     unsafe {
         let mut cond: MaybeUninit<libc::pthread_cond_t> = MaybeUninit::uninit();
         assert_eq!(libc::pthread_cond_init(cond.as_mut_ptr(), ptr::null()), 0);
@@ -260,6 +283,7 @@ fn check_cond() {
 
         t.join().unwrap();
     }
+    bomb.defuse();
 }
 
 fn check_condattr() {
diff --git a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs
index 0e5b501bbcc..cf634bc6890 100644
--- a/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs
+++ b/src/tools/miri/tests/pass-dep/libc/pthread-threadname.rs
@@ -199,4 +199,29 @@ fn main() {
         .unwrap()
         .join()
         .unwrap();
+
+    // Now set the name for a non-existing thread and verify error codes.
+    // (FreeBSD doesn't return an error code.)
+    #[cfg(not(target_os = "freebsd"))]
+    {
+        let invalid_thread = 0xdeadbeef;
+        let error = {
+            cfg_if::cfg_if! {
+                if #[cfg(target_os = "linux")] {
+                    libc::ENOENT
+                } else {
+                    libc::ESRCH
+                }
+            }
+        };
+        #[cfg(not(target_os = "macos"))]
+        {
+            // macOS has no `setname` function accepting a thread id as the first argument.
+            let res = unsafe { libc::pthread_setname_np(invalid_thread, [0].as_ptr()) };
+            assert_eq!(res, error);
+        }
+        let mut buf = [0; 64];
+        let res = unsafe { libc::pthread_getname_np(invalid_thread, buf.as_mut_ptr(), buf.len()) };
+        assert_eq!(res, error);
+    }
 }
diff --git a/src/tools/miri/tests/pass/float.rs b/src/tools/miri/tests/pass/float.rs
index 66843ca584b..4de315e3589 100644
--- a/src/tools/miri/tests/pass/float.rs
+++ b/src/tools/miri/tests/pass/float.rs
@@ -959,10 +959,20 @@ pub fn libm() {
         unsafe { ldexp(a, b) }
     }
 
-    assert_approx_eq!(64f32.sqrt(), 8f32);
-    assert_approx_eq!(64f64.sqrt(), 8f64);
+    assert_eq!(64_f32.sqrt(), 8_f32);
+    assert_eq!(64_f64.sqrt(), 8_f64);
+    assert_eq!(f32::INFINITY.sqrt(), f32::INFINITY);
+    assert_eq!(f64::INFINITY.sqrt(), f64::INFINITY);
+    assert_eq!(0.0_f32.sqrt().total_cmp(&0.0), std::cmp::Ordering::Equal);
+    assert_eq!(0.0_f64.sqrt().total_cmp(&0.0), std::cmp::Ordering::Equal);
+    assert_eq!((-0.0_f32).sqrt().total_cmp(&-0.0), std::cmp::Ordering::Equal);
+    assert_eq!((-0.0_f64).sqrt().total_cmp(&-0.0), std::cmp::Ordering::Equal);
     assert!((-5.0_f32).sqrt().is_nan());
     assert!((-5.0_f64).sqrt().is_nan());
+    assert!(f32::NEG_INFINITY.sqrt().is_nan());
+    assert!(f64::NEG_INFINITY.sqrt().is_nan());
+    assert!(f32::NAN.sqrt().is_nan());
+    assert!(f64::NAN.sqrt().is_nan());
 
     assert_approx_eq!(25f32.powi(-2), 0.0016f32);
     assert_approx_eq!(23.2f64.powi(2), 538.24f64);
diff --git a/src/tools/miri/tests/pass/function_calls/abi_compat.rs b/src/tools/miri/tests/pass/function_calls/abi_compat.rs
index b5feac8c677..cd48bd2accb 100644
--- a/src/tools/miri/tests/pass/function_calls/abi_compat.rs
+++ b/src/tools/miri/tests/pass/function_calls/abi_compat.rs
@@ -1,3 +1,5 @@
+#![feature(never_type)]
+
 use std::rc::Rc;
 use std::{mem, num, ptr};
 
@@ -12,6 +14,18 @@ fn id<T>(x: T) -> T {
     x
 }
 
+#[derive(Copy, Clone)]
+enum Either<T, U> {
+    Left(T),
+    Right(U),
+}
+#[derive(Copy, Clone)]
+enum Either2<T, U> {
+    Left(T),
+    #[allow(unused)]
+    Right(U, ()),
+}
+
 fn test_abi_compat<T: Clone, U: Clone>(t: T, u: U) {
     fn id<T>(x: T) -> T {
         x
@@ -81,6 +95,8 @@ fn main() {
     test_abi_compat(main as fn(), id::<i32> as fn(i32) -> i32);
     // - 1-ZST
     test_abi_compat((), [0u8; 0]);
+
+    // Guaranteed null-pointer-layout optimizations:
     // - Guaranteed Option<X> null-pointer-optimizations (RFC 3391).
     test_abi_compat(&0u32 as *const u32, Some(&0u32));
     test_abi_compat(main as fn(), Some(main as fn()));
@@ -89,6 +105,7 @@ fn main() {
     test_abi_compat(0u32, Some(Wrapper(num::NonZeroU32::new(1u32).unwrap())));
     // - Guaranteed Result<X, ZST1> does the same as Option<X> (RFC 3391)
     test_abi_compat(&0u32 as *const u32, Result::<_, ()>::Ok(&0u32));
+    test_abi_compat(&0u32 as *const u32, Result::<_, !>::Ok(&0u32));
     test_abi_compat(main as fn(), Result::<_, ()>::Ok(main as fn()));
     test_abi_compat(0u32, Result::<_, ()>::Ok(num::NonZeroU32::new(1).unwrap()));
     test_abi_compat(&0u32 as *const u32, Result::<_, ()>::Ok(Wrapper(&0u32)));
@@ -99,6 +116,13 @@ fn main() {
     test_abi_compat(0u32, Result::<(), _>::Err(num::NonZeroU32::new(1).unwrap()));
     test_abi_compat(&0u32 as *const u32, Result::<(), _>::Err(Wrapper(&0u32)));
     test_abi_compat(0u32, Result::<(), _>::Err(Wrapper(num::NonZeroU32::new(1).unwrap())));
+    // - Guaranteed null-pointer-optimizations for custom option-like types
+    test_abi_compat(&0u32 as *const u32, Either::<_, ()>::Left(&0u32));
+    test_abi_compat(&0u32 as *const u32, Either::<_, !>::Left(&0u32));
+    test_abi_compat(&0u32 as *const u32, Either::<(), _>::Right(&0u32));
+    test_abi_compat(&0u32 as *const u32, Either::<!, _>::Right(&0u32));
+    test_abi_compat(&0u32 as *const u32, Either2::<_, ()>::Left(&0u32));
+    test_abi_compat(&0u32 as *const u32, Either2::<_, [u8; 0]>::Left(&0u32));
 
     // These must work for *any* type, since we guarantee that `repr(transparent)` is ABI-compatible
     // with the wrapped field.
diff --git a/src/tools/miri/tests/pass/shims/time-with-isolation.stdout b/src/tools/miri/tests/pass/shims/time-with-isolation.stdout
index 2d7fb5f4a61..6058735f005 100644
--- a/src/tools/miri/tests/pass/shims/time-with-isolation.stdout
+++ b/src/tools/miri/tests/pass/shims/time-with-isolation.stdout
@@ -1,2 +1,2 @@
-The loop took around 1250ms
+The loop took around 1350ms
 (It's fine for this number to change when you `--bless` this test.)
diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs
index 4e892e6e3cb..ae5731bc8a6 100644
--- a/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs
+++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-sha.rs
@@ -181,7 +181,7 @@ unsafe fn schedule(v0: __m128i, v1: __m128i, v2: __m128i, v3: __m128i) -> __m128
 }
 
 // we use unaligned loads with `__m128i` pointers
-#[allow(clippy::cast_ptr_alignment)]
+#[expect(clippy::cast_ptr_alignment)]
 #[target_feature(enable = "sha,sse2,ssse3,sse4.1")]
 unsafe fn digest_blocks(state: &mut [u32; 8], blocks: &[[u8; 64]]) {
     #[allow(non_snake_case)]
diff --git a/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs
new file mode 100644
index 00000000000..68964728e4e
--- /dev/null
+++ b/src/tools/miri/tests/pass/shims/x86/intrinsics-x86-vpclmulqdq.rs
@@ -0,0 +1,193 @@
+// We're testing x86 target specific features
+//@revisions: avx512 avx
+//@only-target: x86_64 i686
+//@[avx512]compile-flags: -C target-feature=+vpclmulqdq,+avx512f
+//@[avx]compile-flags: -C target-feature=+vpclmulqdq,+avx2
+
+// The constants in the tests below are just bit patterns. They should not
+// be interpreted as integers; signedness does not make sense for them, but
+// __mXXXi happens to be defined in terms of signed integers.
+#![allow(overflowing_literals)]
+#![feature(avx512_target_feature)]
+#![feature(stdarch_x86_avx512)]
+
+#[cfg(target_arch = "x86")]
+use std::arch::x86::*;
+#[cfg(target_arch = "x86_64")]
+use std::arch::x86_64::*;
+use std::mem::transmute;
+
+fn main() {
+    // Mostly copied from library/stdarch/crates/core_arch/src/x86/vpclmulqdq.rs
+
+    assert!(is_x86_feature_detected!("pclmulqdq"));
+    assert!(is_x86_feature_detected!("vpclmulqdq"));
+
+    unsafe {
+        test_mm256_clmulepi64_epi128();
+
+        if is_x86_feature_detected!("avx512f") {
+            test_mm512_clmulepi64_epi128();
+        }
+    }
+}
+
+macro_rules! verify_kat_pclmul {
+    ($broadcast:ident, $clmul:ident, $assert:ident) => {
+        // Constants taken from https://software.intel.com/sites/default/files/managed/72/cc/clmul-wp-rev-2.02-2014-04-20.pdf
+        let a = _mm_set_epi64x(0x7b5b546573745665, 0x63746f725d53475d);
+        let a = $broadcast(a);
+        let b = _mm_set_epi64x(0x4869285368617929, 0x5b477565726f6e5d);
+        let b = $broadcast(b);
+        let r00 = _mm_set_epi64x(0x1d4d84c85c3440c0, 0x929633d5d36f0451);
+        let r00 = $broadcast(r00);
+        let r01 = _mm_set_epi64x(0x1bd17c8d556ab5a1, 0x7fa540ac2a281315);
+        let r01 = $broadcast(r01);
+        let r10 = _mm_set_epi64x(0x1a2bf6db3a30862f, 0xbabf262df4b7d5c9);
+        let r10 = $broadcast(r10);
+        let r11 = _mm_set_epi64x(0x1d1e1f2c592e7c45, 0xd66ee03e410fd4ed);
+        let r11 = $broadcast(r11);
+
+        $assert($clmul::<0x00>(a, b), r00);
+        $assert($clmul::<0x10>(a, b), r01);
+        $assert($clmul::<0x01>(a, b), r10);
+        $assert($clmul::<0x11>(a, b), r11);
+
+        let a0 = _mm_set_epi64x(0x0000000000000000, 0x8000000000000000);
+        let a0 = $broadcast(a0);
+        let r = _mm_set_epi64x(0x4000000000000000, 0x0000000000000000);
+        let r = $broadcast(r);
+        $assert($clmul::<0x00>(a0, a0), r);
+    }
+}
+
+// this function tests one of the possible 4 instances
+// with different inputs across lanes for the 512-bit version
+#[target_feature(enable = "vpclmulqdq,avx512f")]
+unsafe fn verify_512_helper(
+    linear: unsafe fn(__m128i, __m128i) -> __m128i,
+    vectorized: unsafe fn(__m512i, __m512i) -> __m512i,
+) {
+    let a = _mm512_set_epi64(
+        0xDCB4DB3657BF0B7D,
+        0x18DB0601068EDD9F,
+        0xB76B908233200DC5,
+        0xE478235FA8E22D5E,
+        0xAB05CFFA2621154C,
+        0x1171B47A186174C9,
+        0x8C6B6C0E7595CEC9,
+        0xBE3E7D4934E961BD,
+    );
+    let b = _mm512_set_epi64(
+        0x672F6F105A94CEA7,
+        0x8298B8FFCA5F829C,
+        0xA3927047B3FB61D8,
+        0x978093862CDE7187,
+        0xB1927AB22F31D0EC,
+        0xA9A5DA619BE4D7AF,
+        0xCA2590F56884FDC6,
+        0x19BE9F660038BDB5,
+    );
+
+    let a_decomp = transmute::<_, [__m128i; 4]>(a);
+    let b_decomp = transmute::<_, [__m128i; 4]>(b);
+
+    let r = vectorized(a, b);
+
+    let e_decomp = [
+        linear(a_decomp[0], b_decomp[0]),
+        linear(a_decomp[1], b_decomp[1]),
+        linear(a_decomp[2], b_decomp[2]),
+        linear(a_decomp[3], b_decomp[3]),
+    ];
+    let e = transmute::<_, __m512i>(e_decomp);
+
+    assert_eq_m512i(r, e)
+}
+
+// this function tests one of the possible 4 instances
+// with different inputs across lanes for the 256-bit version
+#[target_feature(enable = "vpclmulqdq")]
+unsafe fn verify_256_helper(
+    linear: unsafe fn(__m128i, __m128i) -> __m128i,
+    vectorized: unsafe fn(__m256i, __m256i) -> __m256i,
+) {
+    let a = _mm256_set_epi64x(
+        0xDCB4DB3657BF0B7D,
+        0x18DB0601068EDD9F,
+        0xB76B908233200DC5,
+        0xE478235FA8E22D5E,
+    );
+    let b = _mm256_set_epi64x(
+        0x672F6F105A94CEA7,
+        0x8298B8FFCA5F829C,
+        0xA3927047B3FB61D8,
+        0x978093862CDE7187,
+    );
+
+    let a_decomp = transmute::<_, [__m128i; 2]>(a);
+    let b_decomp = transmute::<_, [__m128i; 2]>(b);
+
+    let r = vectorized(a, b);
+
+    let e_decomp = [linear(a_decomp[0], b_decomp[0]), linear(a_decomp[1], b_decomp[1])];
+    let e = transmute::<_, __m256i>(e_decomp);
+
+    assert_eq_m256i(r, e)
+}
+
+#[target_feature(enable = "vpclmulqdq,avx512f")]
+unsafe fn test_mm512_clmulepi64_epi128() {
+    verify_kat_pclmul!(_mm512_broadcast_i32x4, _mm512_clmulepi64_epi128, assert_eq_m512i);
+
+    verify_512_helper(
+        |a, b| _mm_clmulepi64_si128::<0x00>(a, b),
+        |a, b| _mm512_clmulepi64_epi128::<0x00>(a, b),
+    );
+    verify_512_helper(
+        |a, b| _mm_clmulepi64_si128::<0x01>(a, b),
+        |a, b| _mm512_clmulepi64_epi128::<0x01>(a, b),
+    );
+    verify_512_helper(
+        |a, b| _mm_clmulepi64_si128::<0x10>(a, b),
+        |a, b| _mm512_clmulepi64_epi128::<0x10>(a, b),
+    );
+    verify_512_helper(
+        |a, b| _mm_clmulepi64_si128::<0x11>(a, b),
+        |a, b| _mm512_clmulepi64_epi128::<0x11>(a, b),
+    );
+}
+
+#[target_feature(enable = "vpclmulqdq")]
+unsafe fn test_mm256_clmulepi64_epi128() {
+    verify_kat_pclmul!(_mm256_broadcastsi128_si256, _mm256_clmulepi64_epi128, assert_eq_m256i);
+
+    verify_256_helper(
+        |a, b| _mm_clmulepi64_si128::<0x00>(a, b),
+        |a, b| _mm256_clmulepi64_epi128::<0x00>(a, b),
+    );
+    verify_256_helper(
+        |a, b| _mm_clmulepi64_si128::<0x01>(a, b),
+        |a, b| _mm256_clmulepi64_epi128::<0x01>(a, b),
+    );
+    verify_256_helper(
+        |a, b| _mm_clmulepi64_si128::<0x10>(a, b),
+        |a, b| _mm256_clmulepi64_epi128::<0x10>(a, b),
+    );
+    verify_256_helper(
+        |a, b| _mm_clmulepi64_si128::<0x11>(a, b),
+        |a, b| _mm256_clmulepi64_epi128::<0x11>(a, b),
+    );
+}
+
+#[track_caller]
+#[target_feature(enable = "avx512f")]
+unsafe fn assert_eq_m512i(a: __m512i, b: __m512i) {
+    assert_eq!(transmute::<_, [u64; 8]>(a), transmute::<_, [u64; 8]>(b))
+}
+
+#[track_caller]
+#[target_feature(enable = "avx")]
+unsafe fn assert_eq_m256i(a: __m256i, b: __m256i) {
+    assert_eq!(transmute::<_, [u64; 4]>(a), transmute::<_, [u64; 4]>(b))
+}
diff --git a/src/tools/miri/tests/pass/stacked-borrows/2phase.rs b/src/tools/miri/tests/pass/stacked-borrows/2phase.rs
index eb543d691e1..fb4ba605837 100644
--- a/src/tools/miri/tests/pass/stacked-borrows/2phase.rs
+++ b/src/tools/miri/tests/pass/stacked-borrows/2phase.rs
@@ -1,3 +1,6 @@
+// FIXME: this miscompiles with optimizations, see <https://github.com/rust-lang/rust/issues/132898>.
+//@compile-flags: -Zmir-opt-level=0
+
 trait S: Sized {
     fn tpb(&mut self, _s: Self) {}
 }
@@ -75,6 +78,25 @@ fn with_interior_mutability() {
     });
 }
 
+// This one really shouldn't be accepted, but since we treat 2phase as raw, we do accept it.
+// Tree Borrows rejects it.
+fn aliasing_violation() {
+    struct Foo(u64);
+    impl Foo {
+        fn add(&mut self, n: u64) -> u64 {
+            self.0 + n
+        }
+    }
+
+    let mut f = Foo(0);
+    let alias = &mut f.0 as *mut u64;
+    let res = f.add(unsafe {
+        *alias = 42;
+        0
+    });
+    assert_eq!(res, 42);
+}
+
 fn main() {
     two_phase1();
     two_phase2();
@@ -84,4 +106,5 @@ fn main() {
     with_interior_mutability();
     two_phase_overlapping1();
     two_phase_overlapping2();
+    aliasing_violation();
 }
diff --git a/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs b/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs
index c75824d7f9b..4261f411eea 100644
--- a/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs
+++ b/src/tools/miri/tests/pass/stacked-borrows/stacked-borrows.rs
@@ -8,6 +8,7 @@ fn main() {
     mut_raw_then_mut_shr();
     mut_shr_then_mut_raw();
     mut_raw_mut();
+    mut_raw_mut2();
     partially_invalidate_mut();
     drop_after_sharing();
     // direct_mut_to_const_raw();
@@ -96,6 +97,18 @@ fn mut_raw_mut() {
     assert_eq!(x, 4);
 }
 
+// A variant of `mut_raw_mut` that does *not* get accepted by Tree Borrows.
+// It's kind of an accident that we accept it in Stacked Borrows...
+fn mut_raw_mut2() {
+    unsafe {
+        let mut root = 0;
+        let to = &mut root as *mut i32;
+        *to = 0;
+        let _val = root;
+        *to = 0;
+    }
+}
+
 fn partially_invalidate_mut() {
     let data = &mut (0u8, 0u8);
     let reborrow = &mut *data as *mut (u8, u8);
diff --git a/src/tools/miri/tests/pass/tls/windows-tls.rs b/src/tools/miri/tests/pass/tls/windows-tls.rs
new file mode 100644
index 00000000000..58131be1903
--- /dev/null
+++ b/src/tools/miri/tests/pass/tls/windows-tls.rs
@@ -0,0 +1,18 @@
+//@only-target: windows # this directly tests windows-only functions
+
+use std::ffi::c_void;
+use std::ptr;
+
+extern "system" {
+    fn TlsAlloc() -> u32;
+    fn TlsSetValue(key: u32, val: *mut c_void) -> bool;
+    fn TlsGetValue(key: u32) -> *mut c_void;
+    fn TlsFree(key: u32) -> bool;
+}
+
+fn main() {
+    let key = unsafe { TlsAlloc() };
+    assert!(unsafe { TlsSetValue(key, ptr::without_provenance_mut(1)) });
+    assert_eq!(unsafe { TlsGetValue(key).addr() }, 1);
+    assert!(unsafe { TlsFree(key) });
+}
diff --git a/src/tools/opt-dist/Cargo.toml b/src/tools/opt-dist/Cargo.toml
index d34f8ad0520..d0413911014 100644
--- a/src/tools/opt-dist/Cargo.toml
+++ b/src/tools/opt-dist/Cargo.toml
@@ -4,7 +4,7 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-build_helper = { path = "../build_helper" }
+build_helper = { path = "../../build_helper" }
 env_logger = "0.11"
 log = "0.4"
 anyhow = { version = "1", features = ["backtrace"] }
diff --git a/src/tools/opt-dist/src/environment.rs b/src/tools/opt-dist/src/environment.rs
index bc01b7fb8a3..90d0ca717b2 100644
--- a/src/tools/opt-dist/src/environment.rs
+++ b/src/tools/opt-dist/src/environment.rs
@@ -3,7 +3,7 @@ use derive_builder::Builder;
 
 #[derive(Builder)]
 pub struct Environment {
-    host_triple: String,
+    host_tuple: String,
     python_binary: String,
     /// The rustc checkout, where the compiler source is located.
     checkout_dir: Utf8PathBuf,
@@ -28,8 +28,8 @@ pub struct Environment {
 }
 
 impl Environment {
-    pub fn host_triple(&self) -> &str {
-        &self.host_triple
+    pub fn host_tuple(&self) -> &str {
+        &self.host_tuple
     }
 
     pub fn python_binary(&self) -> &str {
@@ -45,7 +45,7 @@ impl Environment {
     }
 
     pub fn build_artifacts(&self) -> Utf8PathBuf {
-        self.build_root().join("build").join(&self.host_triple)
+        self.build_root().join("build").join(&self.host_tuple)
     }
 
     pub fn artifact_dir(&self) -> Utf8PathBuf {
diff --git a/src/tools/opt-dist/src/exec.rs b/src/tools/opt-dist/src/exec.rs
index e50f2600e85..deff69a7f9c 100644
--- a/src/tools/opt-dist/src/exec.rs
+++ b/src/tools/opt-dist/src/exec.rs
@@ -105,9 +105,9 @@ impl Bootstrap {
             env.checkout_path().join("x.py").as_str(),
             "build",
             "--target",
-            &env.host_triple(),
+            &env.host_tuple(),
             "--host",
-            &env.host_triple(),
+            &env.host_tuple(),
             "--stage",
             "2",
             "library/std",
diff --git a/src/tools/opt-dist/src/main.rs b/src/tools/opt-dist/src/main.rs
index 6211b5670aa..c871200f3cf 100644
--- a/src/tools/opt-dist/src/main.rs
+++ b/src/tools/opt-dist/src/main.rs
@@ -127,7 +127,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
             shared,
         } => {
             let env = EnvironmentBuilder::default()
-                .host_triple(target_triple)
+                .host_tuple(target_triple)
                 .python_binary(python)
                 .checkout_dir(checkout_dir.clone())
                 .host_llvm_dir(llvm_dir)
@@ -148,7 +148,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
 
             let checkout_dir = Utf8PathBuf::from("/checkout");
             let env = EnvironmentBuilder::default()
-                .host_triple(target_triple)
+                .host_tuple(target_triple)
                 .python_binary("python3".to_string())
                 .checkout_dir(checkout_dir.clone())
                 .host_llvm_dir(Utf8PathBuf::from("/rustroot"))
@@ -170,7 +170,7 @@ fn create_environment(args: Args) -> anyhow::Result<(Environment, Vec<String>)>
 
             let checkout_dir: Utf8PathBuf = std::env::current_dir()?.try_into()?;
             let env = EnvironmentBuilder::default()
-                .host_triple(target_triple)
+                .host_tuple(target_triple)
                 .python_binary("python".to_string())
                 .checkout_dir(checkout_dir.clone())
                 .host_llvm_dir(checkout_dir.join("citools").join("clang-rust"))
diff --git a/src/tools/opt-dist/src/tests.rs b/src/tools/opt-dist/src/tests.rs
index e401554640c..887055798e0 100644
--- a/src/tools/opt-dist/src/tests.rs
+++ b/src/tools/opt-dist/src/tests.rs
@@ -22,7 +22,7 @@ pub fn run_tests(env: &Environment) -> anyhow::Result<()> {
         assert!(extracted_path.is_dir());
         Ok(extracted_path)
     };
-    let host_triple = env.host_triple();
+    let host_triple = env.host_tuple();
     let version = find_dist_version(&dist_dir)?;
 
     // Extract rustc, libstd, cargo and src archives to create the optimized sysroot
@@ -87,7 +87,7 @@ llvm-config = "{llvm_config}"
         x_py.as_str(),
         "test",
         "--build",
-        env.host_triple(),
+        env.host_tuple(),
         "--stage",
         "0",
         "tests/assembly",
diff --git a/src/tools/run-make-support/Cargo.toml b/src/tools/run-make-support/Cargo.toml
index 3affa199fa5..3c172b2d956 100644
--- a/src/tools/run-make-support/Cargo.toml
+++ b/src/tools/run-make-support/Cargo.toml
@@ -10,7 +10,7 @@ similar = "2.5.0"
 wasmparser = { version = "0.216", default-features = false, features = ["std"] }
 regex = "1.8" # 1.8 to avoid memchr 2.6.0, as 2.5.0 is pinned in the workspace
 gimli = "0.31.0"
-build_helper = { path = "../build_helper" }
+build_helper = { path = "../../build_helper" }
 serde_json = "1.0"
 libc = "0.2"
 
diff --git a/src/tools/run-make-support/src/external_deps/c_build.rs b/src/tools/run-make-support/src/external_deps/c_build.rs
index f8d1666adda..9dd30713f95 100644
--- a/src/tools/run-make-support/src/external_deps/c_build.rs
+++ b/src/tools/run-make-support/src/external_deps/c_build.rs
@@ -1,7 +1,7 @@
 use std::path::PathBuf;
 
 use crate::artifact_names::{dynamic_lib_name, static_lib_name};
-use crate::external_deps::cc::{cc, cxx};
+use crate::external_deps::c_cxx_compiler::{cc, cxx};
 use crate::external_deps::llvm::llvm_ar;
 use crate::path_helpers::path;
 use crate::targets::{is_darwin, is_msvc, is_windows};
diff --git a/src/tools/run-make-support/src/external_deps/cc.rs b/src/tools/run-make-support/src/external_deps/c_cxx_compiler/cc.rs
index 011ad89e170..becb91ae989 100644
--- a/src/tools/run-make-support/src/external_deps/cc.rs
+++ b/src/tools/run-make-support/src/external_deps/c_cxx_compiler/cc.rs
@@ -1,7 +1,7 @@
 use std::path::Path;
 
 use crate::command::Command;
-use crate::{env_var, is_msvc, is_windows, uname};
+use crate::{env_var, is_msvc};
 
 /// Construct a new platform-specific C compiler invocation.
 ///
@@ -127,99 +127,3 @@ impl Cc {
         self
     }
 }
-
-/// `EXTRACFLAGS`
-pub fn extra_c_flags() -> Vec<&'static str> {
-    // Adapted from tools.mk (trimmed):
-    //
-    // ```makefile
-    // ifdef IS_WINDOWS
-    //     ifdef IS_MSVC
-    //         EXTRACFLAGS := ws2_32.lib userenv.lib advapi32.lib bcrypt.lib ntdll.lib synchronization.lib
-    //     else
-    //         EXTRACFLAGS := -lws2_32 -luserenv -lbcrypt -lntdll -lsynchronization
-    //     endif
-    // else
-    //     ifeq ($(UNAME),Darwin)
-    //         EXTRACFLAGS := -lresolv
-    //     else
-    //         ifeq ($(UNAME),FreeBSD)
-    //             EXTRACFLAGS := -lm -lpthread -lgcc_s
-    //         else
-    //             ifeq ($(UNAME),SunOS)
-    //                 EXTRACFLAGS := -lm -lpthread -lposix4 -lsocket -lresolv
-    //             else
-    //                 ifeq ($(UNAME),OpenBSD)
-    //                     EXTRACFLAGS := -lm -lpthread -lc++abi
-    //                 else
-    //                     EXTRACFLAGS := -lm -lrt -ldl -lpthread
-    //                 endif
-    //             endif
-    //         endif
-    //     endif
-    // endif
-    // ```
-
-    if is_windows() {
-        if is_msvc() {
-            vec![
-                "ws2_32.lib",
-                "userenv.lib",
-                "advapi32.lib",
-                "bcrypt.lib",
-                "ntdll.lib",
-                "synchronization.lib",
-            ]
-        } else {
-            vec!["-lws2_32", "-luserenv", "-lbcrypt", "-lntdll", "-lsynchronization"]
-        }
-    } else {
-        match uname() {
-            n if n.contains("Darwin") => vec!["-lresolv"],
-            n if n.contains("FreeBSD") => vec!["-lm", "-lpthread", "-lgcc_s"],
-            n if n.contains("SunOS") => {
-                vec!["-lm", "-lpthread", "-lposix4", "-lsocket", "-lresolv"]
-            }
-            n if n.contains("OpenBSD") => vec!["-lm", "-lpthread", "-lc++abi"],
-            _ => vec!["-lm", "-lrt", "-ldl", "-lpthread"],
-        }
-    }
-}
-
-/// `EXTRACXXFLAGS`
-pub fn extra_cxx_flags() -> Vec<&'static str> {
-    // Adapted from tools.mk (trimmed):
-    //
-    // ```makefile
-    // ifdef IS_WINDOWS
-    //     ifdef IS_MSVC
-    //     else
-    //         EXTRACXXFLAGS := -lstdc++
-    //     endif
-    // else
-    //     ifeq ($(UNAME),Darwin)
-    //         EXTRACXXFLAGS := -lc++
-    //     else
-    //         ifeq ($(UNAME),FreeBSD)
-    //         else
-    //             ifeq ($(UNAME),SunOS)
-    //             else
-    //                 ifeq ($(UNAME),OpenBSD)
-    //                 else
-    //                     EXTRACXXFLAGS := -lstdc++
-    //                 endif
-    //             endif
-    //         endif
-    //     endif
-    // endif
-    // ```
-    if is_windows() {
-        if is_msvc() { vec![] } else { vec!["-lstdc++"] }
-    } else {
-        match &uname()[..] {
-            "Darwin" => vec!["-lc++"],
-            "FreeBSD" | "SunOS" | "OpenBSD" => vec![],
-            _ => vec!["-lstdc++"],
-        }
-    }
-}
diff --git a/src/tools/run-make-support/src/external_deps/c_cxx_compiler/extras.rs b/src/tools/run-make-support/src/external_deps/c_cxx_compiler/extras.rs
new file mode 100644
index 00000000000..49210d75e06
--- /dev/null
+++ b/src/tools/run-make-support/src/external_deps/c_cxx_compiler/extras.rs
@@ -0,0 +1,97 @@
+use crate::{is_msvc, is_windows, uname};
+
+/// `EXTRACFLAGS`
+pub fn extra_c_flags() -> Vec<&'static str> {
+    // Adapted from tools.mk (trimmed):
+    //
+    // ```makefile
+    // ifdef IS_WINDOWS
+    //     ifdef IS_MSVC
+    //         EXTRACFLAGS := ws2_32.lib userenv.lib advapi32.lib bcrypt.lib ntdll.lib synchronization.lib
+    //     else
+    //         EXTRACFLAGS := -lws2_32 -luserenv -lbcrypt -lntdll -lsynchronization
+    //     endif
+    // else
+    //     ifeq ($(UNAME),Darwin)
+    //         EXTRACFLAGS := -lresolv
+    //     else
+    //         ifeq ($(UNAME),FreeBSD)
+    //             EXTRACFLAGS := -lm -lpthread -lgcc_s
+    //         else
+    //             ifeq ($(UNAME),SunOS)
+    //                 EXTRACFLAGS := -lm -lpthread -lposix4 -lsocket -lresolv
+    //             else
+    //                 ifeq ($(UNAME),OpenBSD)
+    //                     EXTRACFLAGS := -lm -lpthread -lc++abi
+    //                 else
+    //                     EXTRACFLAGS := -lm -lrt -ldl -lpthread
+    //                 endif
+    //             endif
+    //         endif
+    //     endif
+    // endif
+    // ```
+
+    if is_windows() {
+        if is_msvc() {
+            vec![
+                "ws2_32.lib",
+                "userenv.lib",
+                "advapi32.lib",
+                "bcrypt.lib",
+                "ntdll.lib",
+                "synchronization.lib",
+            ]
+        } else {
+            vec!["-lws2_32", "-luserenv", "-lbcrypt", "-lntdll", "-lsynchronization"]
+        }
+    } else {
+        match uname() {
+            n if n.contains("Darwin") => vec!["-lresolv"],
+            n if n.contains("FreeBSD") => vec!["-lm", "-lpthread", "-lgcc_s"],
+            n if n.contains("SunOS") => {
+                vec!["-lm", "-lpthread", "-lposix4", "-lsocket", "-lresolv"]
+            }
+            n if n.contains("OpenBSD") => vec!["-lm", "-lpthread", "-lc++abi"],
+            _ => vec!["-lm", "-lrt", "-ldl", "-lpthread"],
+        }
+    }
+}
+
+/// `EXTRACXXFLAGS`
+pub fn extra_cxx_flags() -> Vec<&'static str> {
+    // Adapted from tools.mk (trimmed):
+    //
+    // ```makefile
+    // ifdef IS_WINDOWS
+    //     ifdef IS_MSVC
+    //     else
+    //         EXTRACXXFLAGS := -lstdc++
+    //     endif
+    // else
+    //     ifeq ($(UNAME),Darwin)
+    //         EXTRACXXFLAGS := -lc++
+    //     else
+    //         ifeq ($(UNAME),FreeBSD)
+    //         else
+    //             ifeq ($(UNAME),SunOS)
+    //             else
+    //                 ifeq ($(UNAME),OpenBSD)
+    //                 else
+    //                     EXTRACXXFLAGS := -lstdc++
+    //                 endif
+    //             endif
+    //         endif
+    //     endif
+    // endif
+    // ```
+    if is_windows() {
+        if is_msvc() { vec![] } else { vec!["-lstdc++"] }
+    } else {
+        match &uname()[..] {
+            "Darwin" => vec!["-lc++"],
+            "FreeBSD" | "SunOS" | "OpenBSD" => vec![],
+            _ => vec!["-lstdc++"],
+        }
+    }
+}
diff --git a/src/tools/run-make-support/src/external_deps/c_cxx_compiler/gcc.rs b/src/tools/run-make-support/src/external_deps/c_cxx_compiler/gcc.rs
new file mode 100644
index 00000000000..e02c4f97255
--- /dev/null
+++ b/src/tools/run-make-support/src/external_deps/c_cxx_compiler/gcc.rs
@@ -0,0 +1,66 @@
+use std::path::Path;
+
+use crate::command::Command;
+
+/// Construct a gcc invocation.
+///
+/// WARNING: This assumes *a* `gcc` exists in the environment and is suitable for use.
+#[track_caller]
+pub fn gcc() -> Gcc {
+    Gcc::new()
+}
+
+/// A specific `gcc`.
+#[derive(Debug)]
+#[must_use]
+pub struct Gcc {
+    cmd: Command,
+}
+
+crate::macros::impl_common_helpers!(Gcc);
+
+impl Gcc {
+    /// Construct a `gcc` invocation. This assumes that *a* suitable `gcc` is available in the
+    /// environment.
+    ///
+    /// Note that this does **not** prepopulate the `gcc` invocation with `CC_DEFAULT_FLAGS`.
+    #[track_caller]
+    pub fn new() -> Self {
+        let cmd = Command::new("gcc");
+        Self { cmd }
+    }
+
+    /// Specify path of the input file.
+    pub fn input<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
+        self.cmd.arg(path.as_ref());
+        self
+    }
+
+    /// Adds directories to the list that the linker searches for libraries.
+    /// Equivalent to `-L`.
+    pub fn library_search_path<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
+        self.cmd.arg("-L");
+        self.cmd.arg(path.as_ref());
+        self
+    }
+
+    /// Specify `-o`.
+    pub fn out_exe(&mut self, name: &str) -> &mut Self {
+        self.cmd.arg("-o");
+        self.cmd.arg(name);
+        self
+    }
+
+    /// Specify path of the output binary.
+    pub fn output<P: AsRef<Path>>(&mut self, path: P) -> &mut Self {
+        self.cmd.arg("-o");
+        self.cmd.arg(path.as_ref());
+        self
+    }
+
+    /// Optimize the output at `-O3`.
+    pub fn optimize(&mut self) -> &mut Self {
+        self.cmd.arg("-O3");
+        self
+    }
+}
diff --git a/src/tools/run-make-support/src/external_deps/c_cxx_compiler/mod.rs b/src/tools/run-make-support/src/external_deps/c_cxx_compiler/mod.rs
new file mode 100644
index 00000000000..9aaefd8aa47
--- /dev/null
+++ b/src/tools/run-make-support/src/external_deps/c_cxx_compiler/mod.rs
@@ -0,0 +1,7 @@
+mod cc;
+mod extras;
+mod gcc;
+
+pub use cc::*;
+pub use extras::*;
+pub use gcc::*;
diff --git a/src/tools/run-make-support/src/external_deps/mod.rs b/src/tools/run-make-support/src/external_deps/mod.rs
index 80c34a9070f..129b06761ef 100644
--- a/src/tools/run-make-support/src/external_deps/mod.rs
+++ b/src/tools/run-make-support/src/external_deps/mod.rs
@@ -2,8 +2,8 @@
 //! such as `cc` or `python`.
 
 pub mod c_build;
+pub mod c_cxx_compiler;
 pub mod cargo;
-pub mod cc;
 pub mod clang;
 pub mod htmldocck;
 pub mod llvm;
diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs
index 35d983dc607..494daeca963 100644
--- a/src/tools/run-make-support/src/external_deps/rustc.rs
+++ b/src/tools/run-make-support/src/external_deps/rustc.rs
@@ -5,7 +5,7 @@ use crate::command::Command;
 use crate::env::env_var;
 use crate::path_helpers::cwd;
 use crate::util::set_host_rpath;
-use crate::{is_darwin, is_msvc, is_windows, uname};
+use crate::{is_aix, is_darwin, is_msvc, is_windows, uname};
 
 /// Construct a new `rustc` invocation. This will automatically set the library
 /// search path as `-L cwd()`. Use [`bare_rustc`] to avoid this.
@@ -346,7 +346,7 @@ impl Rustc {
         //     endif
         // endif
         // ```
-        let flag = if is_windows() {
+        if is_windows() {
             // So this is a bit hacky: we can't use the DLL version of libstdc++ because
             // it pulls in the DLL version of libgcc, which means that we end up with 2
             // instances of the DW2 unwinding implementation. This is a problem on
@@ -362,18 +362,19 @@ impl Rustc {
             // So we end up with the following hack: we link use static:-bundle to only
             // link the parts of libstdc++ that we actually use, which doesn't include
             // the dependency on the pthreads DLL.
-            if is_msvc() { None } else { Some("-lstatic:-bundle=stdc++") }
+            if !is_msvc() {
+                self.cmd.arg("-lstatic:-bundle=stdc++");
+            };
         } else if is_darwin() {
-            Some("-lc++")
+            self.cmd.arg("-lc++");
+        } else if is_aix() {
+            self.cmd.arg("-lc++");
+            self.cmd.arg("-lc++abi");
         } else {
-            match &uname()[..] {
-                "FreeBSD" | "SunOS" | "OpenBSD" => None,
-                _ => Some("-lstdc++"),
-            }
+            if !matches!(&uname()[..], "FreeBSD" | "SunOS" | "OpenBSD") {
+                self.cmd.arg("-lstdc++");
+            };
         };
-        if let Some(flag) = flag {
-            self.cmd.arg(flag);
-        }
         self
     }
 }
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index 368b98c9f0d..819bbc161e6 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -41,14 +41,15 @@ pub use libc;
 pub use object;
 pub use regex;
 pub use serde_json;
+pub use similar;
 pub use wasmparser;
 // tidy-alphabetical-end
 
 // Re-exports of external dependencies.
-pub use external_deps::{c_build, cc, clang, htmldocck, llvm, python, rustc, rustdoc};
+pub use external_deps::{c_build, c_cxx_compiler, clang, htmldocck, llvm, python, rustc, rustdoc};
 
 // These rely on external dependencies.
-pub use cc::{cc, cxx, extra_c_flags, extra_cxx_flags, Cc};
+pub use c_cxx_compiler::{Cc, Gcc, cc, cxx, extra_c_flags, extra_cxx_flags, gcc};
 pub use c_build::{
     build_native_dynamic_lib, build_native_static_lib, build_native_static_lib_cxx,
     build_native_static_lib_optimized,
@@ -77,7 +78,7 @@ pub use env::{env_var, env_var_os, set_current_dir};
 pub use run::{cmd, run, run_fail, run_with_args};
 
 /// Helpers for checking target information.
-pub use targets::{is_darwin, is_msvc, is_windows, llvm_components_contain, target, uname, apple_os};
+pub use targets::{is_aix, is_darwin, is_msvc, is_windows, llvm_components_contain, target, uname, apple_os};
 
 /// Helpers for building names of output artifacts that are potentially target-specific.
 pub use artifact_names::{
diff --git a/src/tools/run-make-support/src/targets.rs b/src/tools/run-make-support/src/targets.rs
index 896abb73fc1..ae004fd0cbd 100644
--- a/src/tools/run-make-support/src/targets.rs
+++ b/src/tools/run-make-support/src/targets.rs
@@ -28,6 +28,12 @@ pub fn is_darwin() -> bool {
     target().contains("darwin")
 }
 
+/// Check if target uses AIX.
+#[must_use]
+pub fn is_aix() -> bool {
+    target().contains("aix")
+}
+
 /// Get the target OS on Apple operating systems.
 #[must_use]
 pub fn apple_os() -> &'static str {
diff --git a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
index 3c0eb1b42a6..c97596d5097 100644
--- a/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
+++ b/src/tools/rust-analyzer/crates/parser/src/lexed_str.rs
@@ -183,7 +183,7 @@ impl<'a> Converter<'a> {
                 rustc_lexer::TokenKind::Ident => {
                     SyntaxKind::from_keyword(token_text, self.edition).unwrap_or(IDENT)
                 }
-                rustc_lexer::TokenKind::InvalidPrefix | rustc_lexer::TokenKind::InvalidIdent => {
+                rustc_lexer::TokenKind::InvalidIdent => {
                     err = "Ident contains invalid characters";
                     IDENT
                 }
diff --git a/src/tools/rustbook/.gitignore b/src/tools/rustbook/.gitignore
new file mode 100644
index 00000000000..ea8c4bf7f35
--- /dev/null
+++ b/src/tools/rustbook/.gitignore
@@ -0,0 +1 @@
+/target
diff --git a/src/tools/rustbook/Cargo.lock b/src/tools/rustbook/Cargo.lock
index 27ccb205b50..400eb7c5e0d 100644
--- a/src/tools/rustbook/Cargo.lock
+++ b/src/tools/rustbook/Cargo.lock
@@ -47,9 +47,9 @@ dependencies = [
 
 [[package]]
 name = "anstream"
-version = "0.6.15"
+version = "0.6.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
+checksum = "23a1e53f0f5d86382dafe1cf314783b2044280f406e7e1506368220ad11b1338"
 dependencies = [
  "anstyle",
  "anstyle-parse",
@@ -62,43 +62,43 @@ dependencies = [
 
 [[package]]
 name = "anstyle"
-version = "1.0.8"
+version = "1.0.10"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1bec1de6f59aedf83baf9ff929c98f2ad654b97c9510f4e70cf6f661d49fd5b1"
+checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
 
 [[package]]
 name = "anstyle-parse"
-version = "0.2.5"
+version = "0.2.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "eb47de1e80c2b463c735db5b217a0ddc39d612e7ac9e2e96a5aed1f57616c1cb"
+checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
 dependencies = [
  "utf8parse",
 ]
 
 [[package]]
 name = "anstyle-query"
-version = "1.1.1"
+version = "1.1.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
+checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
 dependencies = [
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "anstyle-wincon"
-version = "3.0.4"
+version = "3.0.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
+checksum = "2109dbce0e72be3ec00bed26e6a7479ca384ad226efdd66db8fa2e3a38c83125"
 dependencies = [
  "anstyle",
- "windows-sys 0.52.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
 name = "anyhow"
-version = "1.0.89"
+version = "1.0.92"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "86fdf8605db99b54d3cd748a44c6d04df638eb5dafb219b135d0149bd0db01f6"
+checksum = "74f37166d7d48a0284b99dd824694c26119c700b53bf0d1540cdb147dbdaaf13"
 
 [[package]]
 name = "autocfg"
@@ -161,9 +161,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
 
 [[package]]
 name = "cc"
-version = "1.1.22"
+version = "1.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9540e661f81799159abee814118cc139a2004b3a3aa3ea37724a1b66530b90e0"
+checksum = "1aeb932158bd710538c73702db6945cb68a8fb08c519e6e12706b94263b36db8"
 dependencies = [
  "shlex",
 ]
@@ -185,14 +185,14 @@ dependencies = [
  "js-sys",
  "num-traits",
  "wasm-bindgen",
- "windows-targets 0.52.6",
+ "windows-targets",
 ]
 
 [[package]]
 name = "clap"
-version = "4.5.18"
+version = "4.5.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b0956a43b323ac1afaffc053ed5c4b7c1f1800bacd1683c353aabbb752515dd3"
+checksum = "b97f376d85a664d5837dbae44bf546e6477a679ff6610010f17276f686d867e8"
 dependencies = [
  "clap_builder",
  "clap_derive",
@@ -200,9 +200,9 @@ dependencies = [
 
 [[package]]
 name = "clap_builder"
-version = "4.5.18"
+version = "4.5.20"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d72166dd41634086d5803a47eb71ae740e61d84709c36f3c34110173db3961b"
+checksum = "19bc80abd44e4bed93ca373a0704ccbd1b710dc5749406201bb018272808dc54"
 dependencies = [
  "anstream",
  "anstyle",
@@ -213,9 +213,9 @@ dependencies = [
 
 [[package]]
 name = "clap_complete"
-version = "4.5.29"
+version = "4.5.36"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8937760c3f4c60871870b8c3ee5f9b30771f792a7045c48bcbba999d7d6b3b8e"
+checksum = "86bc73de94bc81e52f3bebec71bc4463e9748f7a59166663e32044669577b0e2"
 dependencies = [
  "clap",
 ]
@@ -240,9 +240,9 @@ checksum = "1462739cb27611015575c0c11df5df7601141071f07518d56fcc1be504cbec97"
 
 [[package]]
 name = "colorchoice"
-version = "1.0.2"
+version = "1.0.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3fd119d74b830634cea2a0f58bbd0d54540518a14397557951e79340abc28c0"
+checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
 
 [[package]]
 name = "core-foundation-sys"
@@ -441,11 +441,12 @@ dependencies = [
 
 [[package]]
 name = "handlebars"
-version = "5.1.2"
+version = "6.2.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d08485b96a0e6393e9e4d1b8d48cf74ad6c063cd905eb33f42c1ce3f0377539b"
+checksum = "fd4ccde012831f9a071a637b0d4e31df31c0f6c525784b35ae76a9ac6bc1e315"
 dependencies = [
  "log",
+ "num-order",
  "pest",
  "pest_derive",
  "serde",
@@ -455,9 +456,9 @@ dependencies = [
 
 [[package]]
 name = "hashbrown"
-version = "0.14.5"
+version = "0.15.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
+checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
 
 [[package]]
 name = "heck"
@@ -535,9 +536,9 @@ dependencies = [
 
 [[package]]
 name = "indexmap"
-version = "2.5.0"
+version = "2.6.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68b900aa2f7301e21c36462b170ee99994de34dff39a4a6a528e80e7376d07e5"
+checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
 dependencies = [
  "equivalent",
  "hashbrown",
@@ -557,9 +558,9 @@ checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
 
 [[package]]
 name = "js-sys"
-version = "0.3.70"
+version = "0.3.72"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1868808506b929d7b0cfa8f75951347aa71bb21144b7791bae35d9bccfcfe37a"
+checksum = "6a88f1bda2bd75b0452a14784937d796722fdebfe50df998aeb3f0b7603019a9"
 dependencies = [
  "wasm-bindgen",
 ]
@@ -572,9 +573,9 @@ checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe"
 
 [[package]]
 name = "libc"
-version = "0.2.159"
+version = "0.2.161"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "561d97a539a36e26a9a5fad1ea11a3039a67714694aaa379433e580854bc3dc5"
+checksum = "8e9489c2807c139ffd9c1794f4af0ebe86a828db53ecdc7fea2111d0fed085d1"
 
 [[package]]
 name = "libdbus-sys"
@@ -645,9 +646,9 @@ dependencies = [
 
 [[package]]
 name = "mdbook"
-version = "0.4.40"
+version = "0.4.42"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b45a38e19bd200220ef07c892b0157ad3d2365e5b5a267ca01ad12182491eea5"
+checksum = "7624879735513024d323e7267a0b3a7176aceb0db537939beb4ee31d9e8945e3"
 dependencies = [
  "ammonia",
  "anyhow",
@@ -763,6 +764,21 @@ dependencies = [
 ]
 
 [[package]]
+name = "num-modular"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "17bb261bf36fa7d83f4c294f834e91256769097b3cb505d44831e0a179ac647f"
+
+[[package]]
+name = "num-order"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "537b596b97c40fcf8056d153049eb22f481c17ebce72a513ec9286e4986d1bb6"
+dependencies = [
+ "num-modular",
+]
+
+[[package]]
 name = "num-traits"
 version = "0.2.19"
 source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -773,9 +789,9 @@ dependencies = [
 
 [[package]]
 name = "once_cell"
-version = "1.19.0"
+version = "1.20.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92"
+checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
 
 [[package]]
 name = "onig"
@@ -831,14 +847,14 @@ dependencies = [
  "libc",
  "redox_syscall",
  "smallvec",
- "windows-targets 0.52.6",
+ "windows-targets",
 ]
 
 [[package]]
 name = "pathdiff"
-version = "0.2.1"
+version = "0.2.2"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd"
+checksum = "d61c5ce1153ab5b689d0c074c4e7fc613e942dfb7dd9eea5ab202d2ad91fe361"
 
 [[package]]
 name = "percent-encoding"
@@ -848,9 +864,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e"
 
 [[package]]
 name = "pest"
-version = "2.7.13"
+version = "2.7.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fdbef9d1d47087a895abd220ed25eb4ad973a5e26f6a4367b038c25e28dfc2d9"
+checksum = "879952a81a83930934cbf1786752d6dedc3b1f29e8f8fb2ad1d0a36f377cf442"
 dependencies = [
  "memchr",
  "thiserror",
@@ -859,9 +875,9 @@ dependencies = [
 
 [[package]]
 name = "pest_derive"
-version = "2.7.13"
+version = "2.7.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4d3a6e3394ec80feb3b6393c725571754c6188490265c61aaf260810d6b95aa0"
+checksum = "d214365f632b123a47fd913301e14c946c61d1c183ee245fa76eb752e59a02dd"
 dependencies = [
  "pest",
  "pest_generator",
@@ -869,9 +885,9 @@ dependencies = [
 
 [[package]]
 name = "pest_generator"
-version = "2.7.13"
+version = "2.7.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "94429506bde1ca69d1b5601962c73f4172ab4726571a59ea95931218cb0e930e"
+checksum = "eb55586734301717aea2ac313f50b2eb8f60d2fc3dc01d190eefa2e625f60c4e"
 dependencies = [
  "pest",
  "pest_meta",
@@ -882,9 +898,9 @@ dependencies = [
 
 [[package]]
 name = "pest_meta"
-version = "2.7.13"
+version = "2.7.14"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ac8a071862e93690b6e34e9a5fb8e33ff3734473ac0245b27232222c4906a33f"
+checksum = "b75da2a70cf4d9cb76833c990ac9cd3923c9a8905a8929789ce347c84564d03d"
 dependencies = [
  "once_cell",
  "pest",
@@ -980,9 +996,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c"
 
 [[package]]
 name = "proc-macro2"
-version = "1.0.86"
+version = "1.0.89"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+checksum = "f139b0662de085916d1fb67d2b4169d1addddda1919e696f3252b740b629986e"
 dependencies = [
  "unicode-ident",
 ]
@@ -1083,18 +1099,18 @@ dependencies = [
 
 [[package]]
 name = "redox_syscall"
-version = "0.5.6"
+version = "0.5.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "355ae415ccd3a04315d3f8246e86d67689ea74d88d915576e1589a351062a13b"
+checksum = "9b6dfecf2c74bce2466cabf93f6664d6998a69eb21e39f4207930065b27b771f"
 dependencies = [
  "bitflags 2.6.0",
 ]
 
 [[package]]
 name = "regex"
-version = "1.10.6"
+version = "1.11.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4219d74c6b67a3654a9fbebc4b419e22126d13d2f3c4a07ee0cb61ff79a79619"
+checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1104,9 +1120,9 @@ dependencies = [
 
 [[package]]
 name = "regex-automata"
-version = "0.4.7"
+version = "0.4.8"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df"
+checksum = "368758f23274712b504848e9d5a6f010445cc8b87a7cdb4d7cbee666c1288da3"
 dependencies = [
  "aho-corasick",
  "memchr",
@@ -1115,9 +1131,9 @@ dependencies = [
 
 [[package]]
 name = "regex-syntax"
-version = "0.8.4"
+version = "0.8.5"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b"
+checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
 
 [[package]]
 name = "rustbook"
@@ -1134,9 +1150,9 @@ dependencies = [
 
 [[package]]
 name = "rustix"
-version = "0.38.37"
+version = "0.38.38"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8acb788b847c24f28525660c4d7758620a7210875711f79e7f663cc152726811"
+checksum = "aa260229e6538e52293eeb577aabd09945a09d6d9cc0fc550ed7529056c2e32a"
 dependencies = [
  "bitflags 2.6.0",
  "errno",
@@ -1174,18 +1190,18 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
 
 [[package]]
 name = "serde"
-version = "1.0.210"
+version = "1.0.214"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
+checksum = "f55c3193aca71c12ad7890f1785d2b73e1b9f63a0bbc353c08ef26fe03fc56b5"
 dependencies = [
  "serde_derive",
 ]
 
 [[package]]
 name = "serde_derive"
-version = "1.0.210"
+version = "1.0.214"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
+checksum = "de523f781f095e28fa605cdce0f8307e451cc0fd14e2eb4cd2e98a355b147766"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1194,9 +1210,9 @@ dependencies = [
 
 [[package]]
 name = "serde_json"
-version = "1.0.128"
+version = "1.0.132"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6ff5456707a1de34e7e37f2a6fd3d3f808c318259cbd01ab6377795054b483d8"
+checksum = "d726bfaff4b320266d395898905d0eba0345aae23b54aee3a737e260fd46db03"
 dependencies = [
  "itoa",
  "memchr",
@@ -1276,9 +1292,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
 
 [[package]]
 name = "syn"
-version = "2.0.79"
+version = "2.0.87"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "89132cd0bf050864e1d38dc3bbc07a0eb8e7530af26344d3d2bbbef83499f590"
+checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1331,12 +1347,12 @@ dependencies = [
 
 [[package]]
 name = "terminal_size"
-version = "0.3.0"
+version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "21bebf2b7c9e0a515f6e0f8c51dc0f8e4696391e6f1ff30379559f8365fb0df7"
+checksum = "4f599bd7ca042cfdf8f4512b277c02ba102247820f9d9d4a9f521f496751a6ef"
 dependencies = [
  "rustix",
- "windows-sys 0.48.0",
+ "windows-sys 0.59.0",
 ]
 
 [[package]]
@@ -1347,18 +1363,18 @@ checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9"
 
 [[package]]
 name = "thiserror"
-version = "1.0.64"
+version = "1.0.66"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d50af8abc119fb8bb6dbabcfa89656f46f84aa0ac7688088608076ad2b459a84"
+checksum = "5d171f59dbaa811dbbb1aee1e73db92ec2b122911a48e1390dfe327a821ddede"
 dependencies = [
  "thiserror-impl",
 ]
 
 [[package]]
 name = "thiserror-impl"
-version = "1.0.64"
+version = "1.0.66"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08904e7672f5eb876eaaf87e0ce17857500934f4981c4a0ab2b4aa98baac7fc3"
+checksum = "b08be0f17bd307950653ce45db00cd31200d82b624b36e181337d9c7d92765b5"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1437,24 +1453,21 @@ checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825"
 
 [[package]]
 name = "ucd-trie"
-version = "0.1.6"
+version = "0.1.7"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
+checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971"
 
 [[package]]
 name = "unicase"
-version = "2.7.0"
+version = "2.8.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89"
-dependencies = [
- "version_check",
-]
+checksum = "7e51b68083f157f853b6379db119d1c1be0e6e4dec98101079dec41f6f5cf6df"
 
 [[package]]
 name = "unicode-bidi"
-version = "0.3.15"
+version = "0.3.17"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75"
+checksum = "5ab17db44d7388991a428b2ee655ce0c212e862eff1768a455c58f9aad6e7893"
 
 [[package]]
 name = "unicode-ident"
@@ -1524,9 +1537,9 @@ checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
 
 [[package]]
 name = "wasm-bindgen"
-version = "0.2.93"
+version = "0.2.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a82edfc16a6c469f5f44dc7b571814045d60404b55a0ee849f9bcfa2e63dd9b5"
+checksum = "128d1e363af62632b8eb57219c8fd7877144af57558fb2ef0368d0087bddeb2e"
 dependencies = [
  "cfg-if",
  "once_cell",
@@ -1535,9 +1548,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-backend"
-version = "0.2.93"
+version = "0.2.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9de396da306523044d3302746f1208fa71d7532227f15e347e2d93e4145dd77b"
+checksum = "cb6dd4d3ca0ddffd1dd1c9c04f94b868c37ff5fac97c30b97cff2d74fce3a358"
 dependencies = [
  "bumpalo",
  "log",
@@ -1550,9 +1563,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro"
-version = "0.2.93"
+version = "0.2.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "585c4c91a46b072c92e908d99cb1dcdf95c5218eeb6f3bf1efa991ee7a68cccf"
+checksum = "e79384be7f8f5a9dd5d7167216f022090cf1f9ec128e6e6a482a2cb5c5422c56"
 dependencies = [
  "quote",
  "wasm-bindgen-macro-support",
@@ -1560,9 +1573,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-macro-support"
-version = "0.2.93"
+version = "0.2.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "afc340c74d9005395cf9dd098506f7f44e38f2b4a21c6aaacf9a105ea5e1e836"
+checksum = "26c6ab57572f7a24a4985830b120de1594465e5d500f24afe89e16b4e833ef68"
 dependencies = [
  "proc-macro2",
  "quote",
@@ -1573,9 +1586,9 @@ dependencies = [
 
 [[package]]
 name = "wasm-bindgen-shared"
-version = "0.2.93"
+version = "0.2.95"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c62a0a307cb4a311d3a07867860911ca130c3494e8c2719593806c08bc5d0484"
+checksum = "65fc09f10666a9f147042251e0dda9c18f166ff7de300607007e96bdebc1068d"
 
 [[package]]
 name = "winapi"
@@ -1614,16 +1627,7 @@ version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9"
 dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-sys"
-version = "0.48.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
-dependencies = [
- "windows-targets 0.48.5",
+ "windows-targets",
 ]
 
 [[package]]
@@ -1632,7 +1636,7 @@ version = "0.52.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
 dependencies = [
- "windows-targets 0.52.6",
+ "windows-targets",
 ]
 
 [[package]]
@@ -1641,22 +1645,7 @@ version = "0.59.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b"
 dependencies = [
- "windows-targets 0.52.6",
-]
-
-[[package]]
-name = "windows-targets"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
-dependencies = [
- "windows_aarch64_gnullvm 0.48.5",
- "windows_aarch64_msvc 0.48.5",
- "windows_i686_gnu 0.48.5",
- "windows_i686_msvc 0.48.5",
- "windows_x86_64_gnu 0.48.5",
- "windows_x86_64_gnullvm 0.48.5",
- "windows_x86_64_msvc 0.48.5",
+ "windows-targets",
 ]
 
 [[package]]
@@ -1665,48 +1654,30 @@ version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
 dependencies = [
- "windows_aarch64_gnullvm 0.52.6",
- "windows_aarch64_msvc 0.52.6",
- "windows_i686_gnu 0.52.6",
+ "windows_aarch64_gnullvm",
+ "windows_aarch64_msvc",
+ "windows_i686_gnu",
  "windows_i686_gnullvm",
- "windows_i686_msvc 0.52.6",
- "windows_x86_64_gnu 0.52.6",
- "windows_x86_64_gnullvm 0.52.6",
- "windows_x86_64_msvc 0.52.6",
+ "windows_i686_msvc",
+ "windows_x86_64_gnu",
+ "windows_x86_64_gnullvm",
+ "windows_x86_64_msvc",
 ]
 
 [[package]]
 name = "windows_aarch64_gnullvm"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
-
-[[package]]
-name = "windows_aarch64_gnullvm"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
 
 [[package]]
 name = "windows_aarch64_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
-
-[[package]]
-name = "windows_aarch64_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
 
 [[package]]
 name = "windows_i686_gnu"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
-
-[[package]]
-name = "windows_i686_gnu"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b"
@@ -1719,48 +1690,24 @@ checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
 
 [[package]]
 name = "windows_i686_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
-
-[[package]]
-name = "windows_i686_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
 
 [[package]]
 name = "windows_x86_64_gnu"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
-
-[[package]]
-name = "windows_x86_64_gnu"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
 
 [[package]]
 name = "windows_x86_64_gnullvm"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
-
-[[package]]
-name = "windows_x86_64_gnullvm"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
 
 [[package]]
 name = "windows_x86_64_msvc"
-version = "0.48.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
-
-[[package]]
-name = "windows_x86_64_msvc"
 version = "0.52.6"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
diff --git a/src/tools/rustbook/Cargo.toml b/src/tools/rustbook/Cargo.toml
index 2c29a2848b7..854c4547337 100644
--- a/src/tools/rustbook/Cargo.toml
+++ b/src/tools/rustbook/Cargo.toml
@@ -12,7 +12,7 @@ env_logger = "0.11"
 mdbook-trpl-listing = { path = "../../doc/book/packages/mdbook-trpl-listing" }
 mdbook-trpl-note = { path = "../../doc/book/packages/mdbook-trpl-note" }
 mdbook-i18n-helpers = "0.3.3"
-mdbook-spec = { path = "../../doc/reference/mdbook-spec"}
+mdbook-spec = { path = "../../doc/reference/mdbook-spec" }
 
 [dependencies.mdbook]
 version = "0.4.37"
diff --git a/src/tools/rustbook/src/main.rs b/src/tools/rustbook/src/main.rs
index f905b9277ff..a1ef18610b0 100644
--- a/src/tools/rustbook/src/main.rs
+++ b/src/tools/rustbook/src/main.rs
@@ -31,6 +31,20 @@ fn main() {
                               (Defaults to the current directory when omitted)")
     .value_parser(clap::value_parser!(PathBuf));
 
+    // Note: we don't parse this into a `PathBuf` because it is comma separated
+    // strings *and* we will ultimately pass it into `MDBook::test()`, which
+    // accepts `Vec<&str>`. Although it is a bit annoying that `-l/--lang` and
+    // `-L/--library-path` are so close, this is the same set of arguments we
+    // would pass when invoking mdbook on the CLI, so making them match when
+    // invoking rustbook makes for good consistency.
+    let library_path_arg = arg!(
+        -L --"library-path" <PATHS>
+        "A comma-separated list of directories to add to the crate search\n\
+        path when building tests"
+    )
+    .required(false)
+    .value_parser(parse_library_paths);
+
     let matches = Command::new("rustbook")
         .about("Build a book with mdBook")
         .author("Steve Klabnik <steve@steveklabnik.com>")
@@ -48,11 +62,12 @@ fn main() {
         .subcommand(
             Command::new("test")
                 .about("Tests that a book's Rust code samples compile")
-                .arg(dir_arg),
+                .arg(dir_arg)
+                .arg(library_path_arg),
         )
         .get_matches();
 
-    // Check which subcomamnd the user ran...
+    // Check which subcommand the user ran...
     match matches.subcommand() {
         Some(("build", sub_matches)) => {
             if let Err(e) = build(sub_matches) {
@@ -113,8 +128,12 @@ pub fn build(args: &ArgMatches) -> Result3<()> {
 
 fn test(args: &ArgMatches) -> Result3<()> {
     let book_dir = get_book_dir(args);
+    let library_paths = args
+        .try_get_one::<Vec<String>>("library-path")?
+        .map(|v| v.iter().map(|s| s.as_str()).collect::<Vec<&str>>())
+        .unwrap_or_default();
     let mut book = load_book(&book_dir)?;
-    book.test(vec![])
+    book.test(library_paths)
 }
 
 fn get_book_dir(args: &ArgMatches) -> PathBuf {
@@ -132,6 +151,10 @@ fn load_book(book_dir: &Path) -> Result3<MDBook> {
     Ok(book)
 }
 
+fn parse_library_paths(input: &str) -> Result<Vec<String>, String> {
+    Ok(input.split(",").map(String::from).collect())
+}
+
 fn handle_error(error: mdbook::errors::Error) -> ! {
     eprintln!("Error: {}", error);
 
@@ -139,5 +162,5 @@ fn handle_error(error: mdbook::errors::Error) -> ! {
         eprintln!("\tCaused By: {}", cause);
     }
 
-    ::std::process::exit(101);
+    std::process::exit(101);
 }
diff --git a/src/tools/rustdoc-gui-test/Cargo.toml b/src/tools/rustdoc-gui-test/Cargo.toml
index 4cb200ebc7c..f7384a98f85 100644
--- a/src/tools/rustdoc-gui-test/Cargo.toml
+++ b/src/tools/rustdoc-gui-test/Cargo.toml
@@ -4,7 +4,7 @@ version = "0.1.0"
 edition = "2021"
 
 [dependencies]
-build_helper = { path = "../build_helper" }
+build_helper = { path = "../../build_helper" }
 compiletest = { path = "../compiletest" }
 getopts = "0.2"
 walkdir = "2"
diff --git a/src/tools/rustdoc-js/tester.js b/src/tools/rustdoc-js/tester.js
index e162ba033cc..7aa5e102e6d 100644
--- a/src/tools/rustdoc-js/tester.js
+++ b/src/tools/rustdoc-js/tester.js
@@ -2,6 +2,14 @@
 const fs = require("fs");
 const path = require("path");
 
+
+function arrayToCode(array) {
+    return array.map((value, index) => {
+        value = value.split("&nbsp;").join(" ");
+        return (index % 2 === 1) ? ("`" + value + "`") : value;
+    }).join("");
+}
+
 function loadContent(content) {
     const Module = module.constructor;
     const m = new Module();
@@ -84,7 +92,6 @@ function checkNeededFields(fullPath, expected, error_text, queryName, position)
     if (fullPath.length === 0) {
         fieldsToCheck = [
             "foundElems",
-            "original",
             "returned",
             "userQuery",
             "error",
@@ -181,15 +188,7 @@ function valueCheck(fullPath, expected, result, error_text, queryName) {
                 if (!result_v.forEach) {
                     throw result_v;
                 }
-                result_v.forEach((value, index) => {
-                    value = value.split("&nbsp;").join(" ");
-                    if (index % 2 === 1) {
-                        result_v[index] = "`" + value + "`";
-                    } else {
-                        result_v[index] = value;
-                    }
-                });
-                result_v = result_v.join("");
+                result_v = arrayToCode(result_v);
             }
             const obj_path = fullPath + (fullPath.length > 0 ? "." : "") + key;
             valueCheck(obj_path, expected[key], result_v, error_text, queryName);
@@ -437,9 +436,41 @@ function loadSearchJS(doc_folder, resource_suffix) {
     searchModule.initSearch(searchIndex.searchIndex);
     const docSearch = searchModule.docSearch;
     return {
-        doSearch: function(queryStr, filterCrate, currentCrate) {
-            return docSearch.execQuery(searchModule.parseQuery(queryStr),
+        doSearch: async function(queryStr, filterCrate, currentCrate) {
+            const result = await docSearch.execQuery(searchModule.parseQuery(queryStr),
                 filterCrate, currentCrate);
+            for (const tab in result) {
+                if (!Object.prototype.hasOwnProperty.call(result, tab)) {
+                    continue;
+                }
+                if (!(result[tab] instanceof Array)) {
+                    continue;
+                }
+                for (const entry of result[tab]) {
+                    for (const key in entry) {
+                        if (!Object.prototype.hasOwnProperty.call(entry, key)) {
+                            continue;
+                        }
+                        if (key === "displayTypeSignature") {
+                            const {type, mappedNames, whereClause} =
+                                await entry.displayTypeSignature;
+                            entry.displayType = arrayToCode(type);
+                            entry.displayMappedNames = [...mappedNames.entries()]
+                                .map(([name, qname]) => {
+                                    return `${name} = ${qname}`;
+                                }).join(", ");
+                            entry.displayWhereClause = [...whereClause.entries()]
+                                .flatMap(([name, value]) => {
+                                    if (value.length === 0) {
+                                        return [];
+                                    }
+                                    return [`${name}: ${arrayToCode(value)}`];
+                                }).join(", ");
+                        }
+                    }
+                }
+            }
+            return result;
         },
         getCorrections: function(queryStr, filterCrate, currentCrate) {
             const parsedQuery = searchModule.parseQuery(queryStr);
diff --git a/src/tools/rustfmt/src/items.rs b/src/tools/rustfmt/src/items.rs
index fc043a697e0..18932587f1f 100644
--- a/src/tools/rustfmt/src/items.rs
+++ b/src/tools/rustfmt/src/items.rs
@@ -1916,15 +1916,15 @@ pub(crate) fn rewrite_struct_field_prefix(
     field: &ast::FieldDef,
 ) -> RewriteResult {
     let vis = format_visibility(context, &field.vis);
+    let safety = format_safety(field.safety);
     let type_annotation_spacing = type_annotation_spacing(context.config);
     Ok(match field.ident {
         Some(name) => format!(
-            "{}{}{}:",
-            vis,
+            "{vis}{safety}{}{}:",
             rewrite_ident(context, name),
             type_annotation_spacing.0
         ),
-        None => vis.to_string(),
+        None => format!("{vis}{safety}"),
     })
 }
 
@@ -2009,6 +2009,7 @@ pub(crate) struct StaticParts<'a> {
     safety: ast::Safety,
     vis: &'a ast::Visibility,
     ident: symbol::Ident,
+    generics: Option<&'a ast::Generics>,
     ty: &'a ast::Ty,
     mutability: ast::Mutability,
     expr_opt: Option<&'a ptr::P<ast::Expr>>,
@@ -2018,8 +2019,10 @@ pub(crate) struct StaticParts<'a> {
 
 impl<'a> StaticParts<'a> {
     pub(crate) fn from_item(item: &'a ast::Item) -> Self {
-        let (defaultness, prefix, safety, ty, mutability, expr) = match &item.kind {
-            ast::ItemKind::Static(s) => (None, "static", s.safety, &s.ty, s.mutability, &s.expr),
+        let (defaultness, prefix, safety, ty, mutability, expr, generics) = match &item.kind {
+            ast::ItemKind::Static(s) => {
+                (None, "static", s.safety, &s.ty, s.mutability, &s.expr, None)
+            }
             ast::ItemKind::Const(c) => (
                 Some(c.defaultness),
                 "const",
@@ -2027,6 +2030,7 @@ impl<'a> StaticParts<'a> {
                 &c.ty,
                 ast::Mutability::Not,
                 &c.expr,
+                Some(&c.generics),
             ),
             _ => unreachable!(),
         };
@@ -2035,6 +2039,7 @@ impl<'a> StaticParts<'a> {
             safety,
             vis: &item.vis,
             ident: item.ident,
+            generics,
             ty,
             mutability,
             expr_opt: expr.as_ref(),
@@ -2044,8 +2049,8 @@ impl<'a> StaticParts<'a> {
     }
 
     pub(crate) fn from_trait_item(ti: &'a ast::AssocItem) -> Self {
-        let (defaultness, ty, expr_opt) = match &ti.kind {
-            ast::AssocItemKind::Const(c) => (c.defaultness, &c.ty, &c.expr),
+        let (defaultness, ty, expr_opt, generics) = match &ti.kind {
+            ast::AssocItemKind::Const(c) => (c.defaultness, &c.ty, &c.expr, Some(&c.generics)),
             _ => unreachable!(),
         };
         StaticParts {
@@ -2053,6 +2058,7 @@ impl<'a> StaticParts<'a> {
             safety: ast::Safety::Default,
             vis: &ti.vis,
             ident: ti.ident,
+            generics,
             ty,
             mutability: ast::Mutability::Not,
             expr_opt: expr_opt.as_ref(),
@@ -2062,8 +2068,8 @@ impl<'a> StaticParts<'a> {
     }
 
     pub(crate) fn from_impl_item(ii: &'a ast::AssocItem) -> Self {
-        let (defaultness, ty, expr) = match &ii.kind {
-            ast::AssocItemKind::Const(c) => (c.defaultness, &c.ty, &c.expr),
+        let (defaultness, ty, expr, generics) = match &ii.kind {
+            ast::AssocItemKind::Const(c) => (c.defaultness, &c.ty, &c.expr, Some(&c.generics)),
             _ => unreachable!(),
         };
         StaticParts {
@@ -2071,6 +2077,7 @@ impl<'a> StaticParts<'a> {
             safety: ast::Safety::Default,
             vis: &ii.vis,
             ident: ii.ident,
+            generics,
             ty,
             mutability: ast::Mutability::Not,
             expr_opt: expr.as_ref(),
@@ -2085,6 +2092,14 @@ fn rewrite_static(
     static_parts: &StaticParts<'_>,
     offset: Indent,
 ) -> Option<String> {
+    // For now, if this static (or const) has generics, then bail.
+    if static_parts
+        .generics
+        .is_some_and(|g| !g.params.is_empty() || !g.where_clause.is_empty())
+    {
+        return None;
+    }
+
     let colon = colon_spaces(context.config);
     let mut prefix = format!(
         "{}{}{}{} {}{}{}",
@@ -3434,21 +3449,14 @@ impl Rewrite for ast::ForeignItem {
                     ref generics,
                     ref body,
                 } = **fn_kind;
-                if let Some(ref body) = body {
+                if body.is_some() {
                     let mut visitor = FmtVisitor::from_context(context);
                     visitor.block_indent = shape.indent;
                     visitor.last_pos = self.span.lo();
                     let inner_attrs = inner_attributes(&self.attrs);
                     let fn_ctxt = visit::FnCtxt::Foreign;
                     visitor.visit_fn(
-                        visit::FnKind::Fn(
-                            fn_ctxt,
-                            self.ident,
-                            sig,
-                            &self.vis,
-                            generics,
-                            Some(body),
-                        ),
+                        visit::FnKind::Fn(fn_ctxt, &self.ident, sig, &self.vis, generics, body),
                         &sig.decl,
                         self.span,
                         defaultness,
diff --git a/src/tools/rustfmt/src/macros.rs b/src/tools/rustfmt/src/macros.rs
index 5a35e115d8f..4083d9398f6 100644
--- a/src/tools/rustfmt/src/macros.rs
+++ b/src/tools/rustfmt/src/macros.rs
@@ -620,7 +620,7 @@ fn delim_token_to_str(
                 ("{ ", " }")
             }
         }
-        Delimiter::Invisible => unreachable!(),
+        Delimiter::Invisible(_) => unreachable!(),
     };
     if use_multiple_lines {
         let indent_str = shape.indent.to_string_with_newline(context.config);
diff --git a/src/tools/rustfmt/src/spanned.rs b/src/tools/rustfmt/src/spanned.rs
index 4d684f3c635..db7c3486e71 100644
--- a/src/tools/rustfmt/src/spanned.rs
+++ b/src/tools/rustfmt/src/spanned.rs
@@ -150,11 +150,7 @@ impl Spanned for ast::FieldDef {
 
 impl Spanned for ast::WherePredicate {
     fn span(&self) -> Span {
-        match *self {
-            ast::WherePredicate::BoundPredicate(ref p) => p.span,
-            ast::WherePredicate::RegionPredicate(ref p) => p.span,
-            ast::WherePredicate::EqPredicate(ref p) => p.span,
-        }
+        self.span
     }
 }
 
diff --git a/src/tools/rustfmt/src/types.rs b/src/tools/rustfmt/src/types.rs
index e237662f5aa..dd4a788c002 100644
--- a/src/tools/rustfmt/src/types.rs
+++ b/src/tools/rustfmt/src/types.rs
@@ -463,8 +463,8 @@ impl Rewrite for ast::WherePredicate {
 
     fn rewrite_result(&self, context: &RewriteContext<'_>, shape: Shape) -> RewriteResult {
         // FIXME: dead spans?
-        let result = match *self {
-            ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate {
+        let result = match self.kind {
+            ast::WherePredicateKind::BoundPredicate(ast::WhereBoundPredicate {
                 ref bound_generic_params,
                 ref bounded_ty,
                 ref bounds,
@@ -482,12 +482,11 @@ impl Rewrite for ast::WherePredicate {
 
                 rewrite_assign_rhs(context, lhs, bounds, &RhsAssignKind::Bounds, shape)?
             }
-            ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate {
+            ast::WherePredicateKind::RegionPredicate(ast::WhereRegionPredicate {
                 ref lifetime,
                 ref bounds,
-                span,
-            }) => rewrite_bounded_lifetime(lifetime, bounds, span, context, shape)?,
-            ast::WherePredicate::EqPredicate(ast::WhereEqPredicate {
+            }) => rewrite_bounded_lifetime(lifetime, bounds, self.span, context, shape)?,
+            ast::WherePredicateKind::EqPredicate(ast::WhereEqPredicate {
                 ref lhs_ty,
                 ref rhs_ty,
                 ..
diff --git a/src/tools/rustfmt/src/visitor.rs b/src/tools/rustfmt/src/visitor.rs
index 8102fe7ad8f..9b116b620b7 100644
--- a/src/tools/rustfmt/src/visitor.rs
+++ b/src/tools/rustfmt/src/visitor.rs
@@ -390,7 +390,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
                 block = b;
                 self.rewrite_fn_before_block(
                     indent,
-                    ident,
+                    *ident,
                     &FnSig::from_fn_kind(&fk, fd, defaultness),
                     mk_sp(s.lo(), b.span.lo()),
                 )
@@ -540,21 +540,14 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
                         ref generics,
                         ref body,
                     } = **fn_kind;
-                    if let Some(ref body) = body {
+                    if body.is_some() {
                         let inner_attrs = inner_attributes(&item.attrs);
                         let fn_ctxt = match sig.header.ext {
                             ast::Extern::None => visit::FnCtxt::Free,
                             _ => visit::FnCtxt::Foreign,
                         };
                         self.visit_fn(
-                            visit::FnKind::Fn(
-                                fn_ctxt,
-                                item.ident,
-                                sig,
-                                &item.vis,
-                                generics,
-                                Some(body),
-                            ),
+                            visit::FnKind::Fn(fn_ctxt, &item.ident, sig, &item.vis, generics, body),
                             &sig.decl,
                             item.span,
                             defaultness,
@@ -648,11 +641,11 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
                     ref generics,
                     ref body,
                 } = **fn_kind;
-                if let Some(ref body) = body {
+                if body.is_some() {
                     let inner_attrs = inner_attributes(&ai.attrs);
                     let fn_ctxt = visit::FnCtxt::Assoc(assoc_ctxt);
                     self.visit_fn(
-                        visit::FnKind::Fn(fn_ctxt, ai.ident, sig, &ai.vis, generics, Some(body)),
+                        visit::FnKind::Fn(fn_ctxt, &ai.ident, sig, &ai.vis, generics, body),
                         &sig.decl,
                         ai.span,
                         defaultness,
diff --git a/src/tools/rustfmt/tests/source/unsafe-field.rs b/src/tools/rustfmt/tests/source/unsafe-field.rs
new file mode 100644
index 00000000000..35cf40a22d3
--- /dev/null
+++ b/src/tools/rustfmt/tests/source/unsafe-field.rs
@@ -0,0 +1,16 @@
+struct Foo {
+    unsafe
+    field: (),
+}
+
+enum Bar {
+    Variant {
+        unsafe
+        field: (),
+    },
+}
+
+union Baz {
+    unsafe
+    field: (),
+}
diff --git a/src/tools/rustfmt/tests/target/const-generics.rs b/src/tools/rustfmt/tests/target/const-generics.rs
new file mode 100644
index 00000000000..94f76643664
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/const-generics.rs
@@ -0,0 +1,25 @@
+// Make sure we don't mess up the formatting of generic consts
+
+#![feature(generic_const_items)]
+
+const GENERIC<N, const M: usize>: i32 = 0;
+
+const WHERECLAUSE: i32 = 0
+where
+    i32:;
+
+trait Foo {
+    const GENERIC<N, const M: usize>: i32;
+
+    const WHERECLAUSE: i32
+    where
+        i32:;
+}
+
+impl Foo for () {
+    const GENERIC<N, const M: usize>: i32 = 0;
+
+    const WHERECLAUSE: i32 = 0
+    where
+        i32:;
+}
diff --git a/src/tools/rustfmt/tests/target/unsafe-field.rs b/src/tools/rustfmt/tests/target/unsafe-field.rs
new file mode 100644
index 00000000000..fc7fd3750c6
--- /dev/null
+++ b/src/tools/rustfmt/tests/target/unsafe-field.rs
@@ -0,0 +1,11 @@
+struct Foo {
+    unsafe field: (),
+}
+
+enum Bar {
+    Variant { unsafe field: () },
+}
+
+union Baz {
+    unsafe field: (),
+}
diff --git a/src/tools/suggest-tests/Cargo.toml b/src/tools/suggest-tests/Cargo.toml
index 7c048d53a50..d6f86078d7e 100644
--- a/src/tools/suggest-tests/Cargo.toml
+++ b/src/tools/suggest-tests/Cargo.toml
@@ -5,4 +5,4 @@ edition = "2021"
 
 [dependencies]
 glob = "0.3.0"
-build_helper = { version = "0.1.0", path = "../build_helper" }
+build_helper = { version = "0.1.0", path = "../../build_helper" }
diff --git a/src/tools/tidy/Cargo.toml b/src/tools/tidy/Cargo.toml
index 42e608ff5ce..bc75787fb1a 100644
--- a/src/tools/tidy/Cargo.toml
+++ b/src/tools/tidy/Cargo.toml
@@ -5,7 +5,7 @@ edition = "2021"
 autobins = false
 
 [dependencies]
-build_helper = { path = "../build_helper" }
+build_helper = { path = "../../build_helper" }
 cargo_metadata = "0.18"
 regex = "1"
 miropt-test-tools = { path = "../miropt-test-tools" }
diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs
index f3bd72fe647..e065f01ebba 100644
--- a/src/tools/tidy/src/deps.rs
+++ b/src/tools/tidy/src/deps.rs
@@ -24,6 +24,7 @@ const LICENSES: &[&str] = &[
     "BSD-2-Clause OR Apache-2.0 OR MIT",                   // zerocopy
     "ISC",
     "MIT / Apache-2.0",
+    "MIT AND (MIT OR Apache-2.0)",
     "MIT AND Apache-2.0 WITH LLVM-exception AND (MIT OR Apache-2.0)", // compiler-builtins
     "MIT OR Apache-2.0 OR LGPL-2.1-or-later",              // r-efi, r-efi-alloc
     "MIT OR Apache-2.0 OR Zlib",                           // tinyvec_macros
@@ -65,9 +66,6 @@ pub(crate) const WORKSPACES: &[(&str, ExceptionList, Option<(&[&str], &[&str])>,
     ),
     // tidy-alphabetical-start
     ("compiler/rustc_codegen_gcc", EXCEPTIONS_GCC, None, &[]),
-    //("library/backtrace", &[], None), // FIXME uncomment once rust-lang/backtrace#562 has been synced back to the rust repo
-    //("library/portable-simd", &[], None), // FIXME uncomment once rust-lang/portable-simd#363 has been synced back to the rust repo
-    //("library/stdarch", EXCEPTIONS_STDARCH, None), // FIXME uncomment once rust-lang/stdarch#1462 has been synced back to the rust repo
     ("src/bootstrap", EXCEPTIONS_BOOTSTRAP, None, &[]),
     ("src/ci/docker/host-x86_64/test-various/uefi_qemu_test", EXCEPTIONS_UEFI_QEMU_TEST, None, &[]),
     ("src/etc/test-float-parse", EXCEPTIONS, None, &[]),
@@ -95,6 +93,7 @@ const EXCEPTIONS: ExceptionList = &[
     ("constant_time_eq", "CC0-1.0 OR MIT-0 OR Apache-2.0"),  // rustc
     ("dissimilar", "Apache-2.0"),                            // rustdoc, rustc_lexer (few tests) via expect-test, (dev deps)
     ("fluent-langneg", "Apache-2.0"),                        // rustc (fluent translations)
+    ("foldhash", "Zlib"),                                    // rustc
     ("instant", "BSD-3-Clause"),                             // rustc_driver/tracing-subscriber/parking_lot
     ("mdbook", "MPL-2.0"),                                   // mdbook
     ("option-ext", "MPL-2.0"),                               // cargo-miri (via `directories`)
@@ -116,17 +115,6 @@ const EXCEPTIONS_STDLIB: ExceptionList = &[
     // tidy-alphabetical-end
 ];
 
-// FIXME uncomment once rust-lang/stdarch#1462 lands
-/*
-const EXCEPTIONS_STDARCH: ExceptionList = &[
-    // tidy-alphabetical-start
-    ("ryu", "Apache-2.0 OR BSL-1.0"), // BSL is not acceptble, but we use it under Apache-2.0
-    ("wasmparser", "Apache-2.0 WITH LLVM-exception"),
-    ("wasmprinter", "Apache-2.0 WITH LLVM-exception"),
-    // tidy-alphabetical-end
-];
-*/
-
 const EXCEPTIONS_CARGO: ExceptionList = &[
     // tidy-alphabetical-start
     ("arrayref", "BSD-2-Clause"),
@@ -203,7 +191,7 @@ const EXCEPTIONS_CRANELIFT: ExceptionList = &[
     ("cranelift-module", "Apache-2.0 WITH LLVM-exception"),
     ("cranelift-native", "Apache-2.0 WITH LLVM-exception"),
     ("cranelift-object", "Apache-2.0 WITH LLVM-exception"),
-    ("mach", "BSD-2-Clause"),
+    ("mach2", "BSD-2-Clause OR MIT OR Apache-2.0"),
     ("regalloc2", "Apache-2.0 WITH LLVM-exception"),
     ("target-lexicon", "Apache-2.0 WITH LLVM-exception"),
     ("wasmtime-jit-icache-coherence", "Apache-2.0 WITH LLVM-exception"),
@@ -297,6 +285,7 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
     "fluent-langneg",
     "fluent-syntax",
     "fnv",
+    "foldhash",
     "generic-array",
     "getopts",
     "getrandom",
@@ -537,7 +526,7 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
     "libc",
     "libloading",
     "log",
-    "mach",
+    "mach2",
     "memchr",
     "object",
     "once_cell",
@@ -554,9 +543,6 @@ const PERMITTED_CRANELIFT_DEPENDENCIES: &[&str] = &[
     "unicode-ident",
     "version_check",
     "wasmtime-jit-icache-coherence",
-    "winapi",
-    "winapi-i686-pc-windows-gnu",
-    "winapi-x86_64-pc-windows-gnu",
     "windows-sys",
     "windows-targets",
     "windows_aarch64_gnullvm",
diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt
index 932a58788e0..ac82a17e145 100644
--- a/src/tools/tidy/src/issues.txt
+++ b/src/tools/tidy/src/issues.txt
@@ -2756,7 +2756,6 @@ ui/lint/issue-57410-1.rs
 ui/lint/issue-57410.rs
 ui/lint/issue-63364.rs
 ui/lint/issue-70819-dont-override-forbid-in-same-scope.rs
-ui/lint/issue-79546-fuel-ice.rs
 ui/lint/issue-79744.rs
 ui/lint/issue-81218.rs
 ui/lint/issue-83477.rs
diff --git a/src/tools/tidy/src/known_bug.rs b/src/tools/tidy/src/known_bug.rs
index 182a81c6ec9..a8771654144 100644
--- a/src/tools/tidy/src/known_bug.rs
+++ b/src/tools/tidy/src/known_bug.rs
@@ -6,8 +6,22 @@ use crate::walk::*;
 
 pub fn check(filepath: &Path, bad: &mut bool) {
     walk(filepath, |path, _is_dir| filter_not_rust(path), &mut |entry, contents| {
-        let file = entry.path();
-        if !contents.lines().any(|line| line.starts_with("//@ known-bug: ")) {
+        let file: &Path = entry.path();
+
+        // files in "auxiliary" do not need to crash by themselves
+        let test_path_segments =
+            file.iter().map(|s| s.to_string_lossy().into()).collect::<Vec<String>>();
+        let test_path_segments_str =
+            test_path_segments.iter().map(|s| s.as_str()).collect::<Vec<&str>>();
+
+        if !matches!(test_path_segments_str[..], [
+            ..,
+            "tests",
+            "crashes",
+            "auxiliary",
+            _aux_file_rs
+        ]) && !contents.lines().any(|line| line.starts_with("//@ known-bug: "))
+        {
             tidy_error!(
                 bad,
                 "{} crash/ice test does not have a \"//@ known-bug: \" directive",
diff --git a/src/tools/tidy/src/style.rs b/src/tools/tidy/src/style.rs
index 6394a1463b0..35cda17e168 100644
--- a/src/tools/tidy/src/style.rs
+++ b/src/tools/tidy/src/style.rs
@@ -337,12 +337,16 @@ pub fn check(path: &Path, bad: &mut bool) {
         .case_insensitive(true)
         .build()
         .unwrap();
-    let style_file = Path::new(file!());
+
+    // In some cases, a style check would be triggered by its own implementation
+    // or comments. A simple workaround is to just allowlist this file.
+    let this_file = Path::new(file!());
+
     walk(path, skip, &mut |entry, contents| {
         let file = entry.path();
         let filename = file.file_name().unwrap().to_string_lossy();
 
-        let is_style_file = filename.ends_with(".css");
+        let is_css_file = filename.ends_with(".css");
         let under_rustfmt = filename.ends_with(".rs") &&
             // This list should ideally be sourced from rustfmt.toml but we don't want to add a toml
             // parser to tidy.
@@ -405,13 +409,13 @@ pub fn check(path: &Path, bad: &mut bool) {
         let mut comment_block: Option<(usize, usize)> = None;
         let is_test = file.components().any(|c| c.as_os_str() == "tests")
             || file.file_stem().unwrap() == "tests";
-        let is_style = file.ends_with(style_file) || style_file.ends_with(file);
-        let is_style_test =
-            is_test && file.parent().unwrap().ends_with(style_file.with_extension(""));
+        let is_this_file = file.ends_with(this_file) || this_file.ends_with(file);
+        let is_test_for_this_file =
+            is_test && file.parent().unwrap().ends_with(this_file.with_extension(""));
         // scanning the whole file for multiple needles at once is more efficient than
         // executing lines times needles separate searches.
         let any_problematic_line =
-            !is_style && !is_style_test && problematic_regex.is_match(contents);
+            !is_this_file && !is_test_for_this_file && problematic_regex.is_match(contents);
         for (i, line) in contents.split('\n').enumerate() {
             if line.is_empty() {
                 if i == 0 {
@@ -458,19 +462,19 @@ pub fn check(path: &Path, bad: &mut bool) {
                     "line longer than {max_columns} chars"
                 );
             }
-            if !is_style_file && line.contains('\t') {
+            if !is_css_file && line.contains('\t') {
                 suppressible_tidy_err!(err, skip_tab, "tab character");
             }
             if line.ends_with(' ') || line.ends_with('\t') {
                 suppressible_tidy_err!(err, skip_end_whitespace, "trailing whitespace");
             }
-            if is_style_file && line.starts_with(' ') {
+            if is_css_file && line.starts_with(' ') {
                 err("CSS files use tabs for indent");
             }
             if line.contains('\r') {
                 suppressible_tidy_err!(err, skip_cr, "CR character");
             }
-            if !is_style {
+            if !is_this_file {
                 // Allow using TODO in diagnostic suggestions by marking the
                 // relevant line with `// ignore-tidy-todo`.
                 if trimmed.contains("TODO") && !trimmed.contains("ignore-tidy-todo") {
diff --git a/src/tools/tidy/src/target_specific_tests.rs b/src/tools/tidy/src/target_specific_tests.rs
index 751d678e581..a66ccd37070 100644
--- a/src/tools/tidy/src/target_specific_tests.rs
+++ b/src/tools/tidy/src/target_specific_tests.rs
@@ -10,26 +10,6 @@ use crate::walk::filter_not_rust;
 const LLVM_COMPONENTS_HEADER: &str = "needs-llvm-components:";
 const COMPILE_FLAGS_HEADER: &str = "compile-flags:";
 
-const KNOWN_LLVM_COMPONENTS: &[&str] = &[
-    "aarch64",
-    "arm",
-    "avr",
-    "bpf",
-    "csky",
-    "hexagon",
-    "loongarch",
-    "m68k",
-    "mips",
-    "msp430",
-    "nvptx",
-    "powerpc",
-    "riscv",
-    "sparc",
-    "systemz",
-    "webassembly",
-    "x86",
-];
-
 #[derive(Default, Debug)]
 struct RevisionInfo<'a> {
     target_arch: Option<&'a str>,
@@ -94,20 +74,6 @@ pub fn check(tests_path: &Path, bad: &mut bool) {
                     // gathered.
                 }
             }
-            if let Some(llvm_components) = llvm_components {
-                for component in llvm_components {
-                    // Ensure the given component even exists.
-                    // This is somewhat redundant with COMPILETEST_REQUIRE_ALL_LLVM_COMPONENTS,
-                    // but helps detect such problems earlier (PR CI rather than bors CI).
-                    if !KNOWN_LLVM_COMPONENTS.contains(component) {
-                        eprintln!(
-                            "{}: revision {} specifies unknown LLVM component `{}`",
-                            file, rev, component
-                        );
-                        *bad = true;
-                    }
-                }
-            }
         }
     });
 }
diff --git a/src/tools/tidy/src/walk.rs b/src/tools/tidy/src/walk.rs
index 8cbc263689b..edf7658d25f 100644
--- a/src/tools/tidy/src/walk.rs
+++ b/src/tools/tidy/src/walk.rs
@@ -7,7 +7,6 @@ use ignore::DirEntry;
 
 /// The default directory filter.
 pub fn filter_dirs(path: &Path) -> bool {
-    // FIXME: sync submodule exclusion list with rustfmt.toml
     // bootstrap/etc
     let skip = [
         "tidy-test-file",
diff --git a/src/tools/unicode-table-generator/src/cascading_map.rs b/src/tools/unicode-table-generator/src/cascading_map.rs
index 036f0bd7eac..1eb35e819c0 100644
--- a/src/tools/unicode-table-generator/src/cascading_map.rs
+++ b/src/tools/unicode-table-generator/src/cascading_map.rs
@@ -65,7 +65,7 @@ impl RawEmitter {
         self.bytes_used += 256;
 
         writeln!(&mut self.file, "#[inline]").unwrap();
-        writeln!(&mut self.file, "pub fn lookup(c: char) -> bool {{").unwrap();
+        writeln!(&mut self.file, "pub const fn lookup(c: char) -> bool {{").unwrap();
         writeln!(&mut self.file, "    match c as u32 >> 8 {{").unwrap();
         for arm in arms {
             writeln!(&mut self.file, "        {},", arm).unwrap();
diff --git a/src/tools/unicode-table-generator/src/main.rs b/src/tools/unicode-table-generator/src/main.rs
index e1832091d70..415db2c4dbc 100644
--- a/src/tools/unicode-table-generator/src/main.rs
+++ b/src/tools/unicode-table-generator/src/main.rs
@@ -268,7 +268,7 @@ fn main() {
     let mut table_file = String::new();
 
     table_file.push_str(
-        "///! This file is generated by src/tools/unicode-table-generator; do not edit manually!\n",
+        "///! This file is generated by `./x run src/tools/unicode-table-generator`; do not edit manually!\n",
     );
 
     // Include the range search function
diff --git a/src/tools/unicode-table-generator/src/range_search.rs b/src/tools/unicode-table-generator/src/range_search.rs
index 221e5d75ef5..14da876eda7 100644
--- a/src/tools/unicode-table-generator/src/range_search.rs
+++ b/src/tools/unicode-table-generator/src/range_search.rs
@@ -1,5 +1,5 @@
-#[rustc_const_unstable(feature = "const_unicode_case_lookup", issue = "101400")]
 #[inline(always)]
+#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_unicode_case_lookup", since = "1.84.0"))]
 const fn bitset_search<
     const N: usize,
     const CHUNK_SIZE: usize,
diff --git a/src/tools/unicode-table-generator/src/raw_emitter.rs b/src/tools/unicode-table-generator/src/raw_emitter.rs
index 8097d6a8caf..dd064c59283 100644
--- a/src/tools/unicode-table-generator/src/raw_emitter.rs
+++ b/src/tools/unicode-table-generator/src/raw_emitter.rs
@@ -99,9 +99,8 @@ impl RawEmitter {
 
         writeln!(
             &mut self.file,
-            r#"#[rustc_const_unstable(feature = "const_unicode_case_lookup", issue = "101400")]"#
-        )
-        .unwrap();
+            r#"#[cfg_attr(bootstrap, rustc_const_stable(feature = "const_unicode_case_lookup", since = "1.84.0"))]"#
+        ).unwrap();
         writeln!(&mut self.file, "pub const fn lookup(c: char) -> bool {{").unwrap();
         if first_code_point > 0x7f {
             writeln!(&mut self.file, "    (c as u32) >= {first_code_point:#04x} &&").unwrap();